Problem med att anropa C++ DLL från VB

C, C++, Pascal, Assembly, Raspberry, Java, Matlab, Python, BASIC, SQL, PHP, etc.
H.O
Inlägg: 5739
Blev medlem: 19 mars 2007, 10:11:27
Ort: Ronneby

Problem med att anropa C++ DLL från VB

Inlägg av H.O »

På Win7 64bit försöker jag, från Visual Basic Express 2013, anropa funktioner i en "C++ dll".

Det finns exempelprojekt, som kompileras med Qt, och i det ser C++ funktionsdeklarationen ut som följer:

Kod: Markera allt

// Open SM RS485 communication bus. Parameters:
//       -devicename: "USB2VSD" or com port as "COMx" where x=1-16
//       -return value: handle to be used with all other commands, -1 if fails

LIB smbus   smOpenBus(  const char * devicename );
(där smbus är typedef'ad som long)

I exempelprojektet har jag sedan redigerat lite och för enkelhetens skull hårdkodat anropet till funktionen så att det ser ut så här:

Kod: Markera allt

busHandle=smOpenBus("COM10")
Ovanstående fungerar, programmet ansluter till COM10, helt enligt plan. Detta visar att hårdvaran fungerar korrekt, RS485-adaptern hittas på COM10).

Nu över till Visual Basic.
Här har jag (försökt) översatta funktionsdeklarationen till:

Kod: Markera allt

    <DllImport("smv2.dll", EntryPoint:="smOpenBus", SetLastError:=True, CharSet:=CharSet.Unicode, ExactSpelling:=True, CallingConvention:=CallingConvention.Cdecl)>
    Public Function smOpenBus(ByRef DeviceName As String()) As Int32
    End Function
Och själva anropet ser ut:

Kod: Markera allt

Dim busHandle As Int32   ' smOpenBus returnerar en "C++ LONG vilket är en Int32 i VB).
busHandle=smOpenBus("COM10")
Men här fungerar det inte, funktionen returnerar -1 vilket ju betyder FAIL. "Normalt" sett så betyder ju det att det är en icke existerande COM-port, att den inte hittar RS485-adaptern (en FTDI variant) eller att porten redan är öppen. Men det fungerar fint att både öppna och stänga porten från exempelprojektet.

Jag har på liknande sätt lyckats "importera" och anropa tre andra funktioner i dll'en så jag känner att jag inte är helt ute och cyklar men här har jag kört fast. Vad exakt menas med const char * devicename i funktionsdeklarationen? Jag, som är urdålig på C, fattar det som att funktionen förväntar sig en pekare till en sträng vilket är anledningen till att jag i VB säger ByRef DeviceName as String - men det fungerar ju inte....jag har även provat ByVal in deklarationen men det hjälper inte.

Någon som har några idéer om vad jag gör för fel?
Användarvisningsbild
mri
Inlägg: 1165
Blev medlem: 15 mars 2007, 13:20:50
Ort: Jakobstad, Finland
Kontakt:

Re: Problem med att anropa C++ DLL från VB

Inlägg av mri »

"const char * devicename"
Betyder att devicename är en pekare (en variabel som innehåller en minnesadress) till första tecknet i textsträngen, alltså "C" i ditt fall. Notera att textsträngen måste termineras med ett tecken med värdet noll.
Tyvärr kan jag inte nåt om basic syntaxen.
Användarvisningsbild
sodjan
EF Sponsor
Inlägg: 43205
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping
Kontakt:

Re: Problem med att anropa C++ DLL från VB

Inlägg av sodjan »

ByRef borde vara OK, "Reference" motsvarar i princip en adress (eller "pekare").
Kanske att du som mri säger ska testa att lägga på en "null" sist i strängen.
Exakt hur man skriver vet jag inte, kanske något av:

busHandle=smOpenBus("COM10\0")
busHandle=smOpenBus("COM10" + Chr(0))
busHandle=smOpenBus("COM10" + vbNullChar)
H.O
Inlägg: 5739
Blev medlem: 19 mars 2007, 10:11:27
Ort: Ronneby

Re: Problem med att anropa C++ DLL från VB

Inlägg av H.O »

Mmm, det är ju som jag trodde då - vilket alltså "borde" motsvaras av ByRef i Visual Basic. Men Google antyder att strängtypen som används i VB redan är "pekarbaserad", och att man just därför måste skicka den ByVal (annars blir det en pekare till en pekare). Nu fungerar ju inte det heller men jag tror ändå att jag i min fortsatta felsökning skall ha det satt som ByVal.....

Null-terminering är ju intressant....
Jag behöver ju inte lägga till något i det fungerande Qt-exemplet men där paddar väl kompilatorn strängen med NULL på slutet?
Hur som helst så testade jag följande i VB (både med ByRef och ByVal)

Kod: Markera allt

Const port As String = "COM10" & ControlChars.NullChar    ' Även testat Dim port As String = ......
busHandle = 0
busHandle = smOpenBus(port)
MsgBox("BusHandle: " & busHandle)
Jag testade även "COM10" + Chr(0) samt "COM10" + vbNullChar som båda är OK syntax men inte fungerar. Får bara -1 tillbaka. Det faktum att funktionen returnerar just -1 tyder ju på att anropet i sig lyckas men att något med just parametern (för det är ju tack och lov bara EN i det här fallet) inte stämmer.

Google håller med om att det är struligt att skicka strängar mellan VB och C++ men ingen uppenbar lösning som fungerar i mitt fall har påträffats ännu...
Användarvisningsbild
sodjan
EF Sponsor
Inlägg: 43205
Blev medlem: 10 maj 2005, 16:29:20
Ort: Söderköping
Kontakt:

Re: Problem med att anropa C++ DLL från VB

Inlägg av sodjan »

I det här läget skulle jag skriva en liten egen C++ funktion
som tar samma parameter och som dumpar det som den
"ser" byte för byte.

Något annat som jag goolglade upp var "encoding". D.v.s
att VB.net eventuellt använder en multibyte UTF encoding
som default och att man ibland behöver gör en explicit
kodning till ASCII av parametern/strängen innan anropet.
Dumpen ovan skulle sannolikt visa om det var det problemet.
H.O
Inlägg: 5739
Blev medlem: 19 mars 2007, 10:11:27
Ort: Ronneby

Re: Problem med att anropa C++ DLL från VB

Inlägg av H.O »

Om jag bara viste hur man gjorde.... Jag är kass på C/C++ så det är nog kört....

Att det kan vara så krångligt....
En String i VB föregås av 4 bytes som talar om strängens längd, kanske är det så att jag skickar adressen till den första av dessa 4 bytes istället för adressen till den första byten i själva strängen. Eller så är problemet, vilket jag tror är vad du är inne på Sodjan, att Char i C++ är 8 bitar medan String i VB är ett eller flera 16-bitars ord. Och så pratas det om att DLLImport och Marshaling funktionen skall sköta detta åt en och där finns ju CharSet:=CharSet.Unicode med som en parameter. Suck.

Som du säger, någon form av testfunktion i C++ skulle behövas.

Tack föresten!
Användarvisningsbild
ajje
Inlägg: 2376
Blev medlem: 12 mars 2010, 16:35:31
Ort: Smedjebacken

Re: Problem med att anropa C++ DLL från VB

Inlägg av ajje »

En idé kan väl vara att kolla med "Process Monitor" hur anropet som går iväg ser ut och varför det fallerar.
https://technet.microsoft.com/en-us/sys ... s/bb896645
Mr Andersson
Inlägg: 1400
Blev medlem: 29 januari 2011, 21:06:30
Ort: Lapplandet

Re: Problem med att anropa C++ DLL från VB

Inlägg av Mr Andersson »

Använd CharSet.Ansi. Unicode är för wchar_t* strängar.
H.O
Inlägg: 5739
Blev medlem: 19 mars 2007, 10:11:27
Ort: Ronneby

Re: Problem med att anropa C++ DLL från VB

Inlägg av H.O »

OK, då verkar jag äntligen fått det att fungera.
Parametern (strängen) måste skicak ByVal CharSet måste vara satt till Ansi i funktionsdeklaraionen - annars funkar det inte. Alltså:

Kod: Markera allt

    <DllImport("smv2.dll", EntryPoint:="smOpenBus", SetLastError:=True, CharSet:=CharSet.Ansi, ExactSpelling:=True, CallingConvention:=CallingConvention.Cdecl)>
    Public Function smOpenBus(ByVal DeviceName As String) As Int32
    End Function
Och sedan,

Kod: Markera allt

        Dim port As String = "COM10"
        busHandle = -1
        busHandle = smOpenBus(port)
Har en känsla av att jag kommer få tillfälle att återkomma till den här tråden men för tillfället kan jag traggla vidare, tack ska ni ha!
Skriv svar