Zugriff auf C++Dll funktioniert nicht

  • VB.NET

Es gibt 19 Antworten in diesem Thema. Der letzte Beitrag () ist von wolfi_bayern.

    Zugriff auf C++Dll funktioniert nicht

    Guten Abend liebe Forum-Gemeinde!

    Ich hab ein USB-Gerät welches einfach Zählwerte aufnimmt. Diese möchte ich in meinem Programm auslesen. Der Hersteller des Gerätes hat den kompletten Sourecode mitgeliefert. Daher habe ich die .h-Datei geöffnet und entsprechende Declare-Lib anweisungen in VB geschrieben.

    Ich kann zwar auch auf die DLL zugreifen ohne dass das Programm abstürzt, bekomme aber als Rückmeldung der Init-Funktion -5 statt -1. Was mich etwas verwirrt ist die deklaration PASCAL in dem .h-File.

    Auch kann ich keine Strings auslesen. Zumindes die DLL-Version sollte doch lesbar sein auch wenn kein USB-Gerät angesteckt ist.

    Ich habe auch schon die Sachen auf MSDN und hier im Forum gelesen. Aber so richtig schlau werd ich nicht draus. Ich häng mal die beiden Dateien an. Evtl. kann jemand das Problem erklären oder einen Hinweis geben.

    Achja. Ich verwende VB Express und kompiliere mit 32 bit.

    Danke erstmal und Gruß

    Wolfgang
    Dateien
    • WrapperUSBCount.vb

      (5,9 kB, 147 mal heruntergeladen, zuletzt: )
    • HS_UCApi.txt

      (4,21 kB, 224 mal heruntergeladen, zuletzt: )
    Der Beitrag wurde aus 100% wiederverwendbaren Elektronen erstellt!
    @wolfi_bayern:: ByRef ist falsch.
    Gugst Du hier.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!

    Aufrufparameter geändert

    Hallo!

    Danke für die schnelle Antwort!

    Diesen Beitrag kenn ich und hab den auch genau durchgelesen. ByRef hab ich verwendet, da über diese Variablen Werte zurückgegeben werden. Aber ich werds nochmal Umschreiben und testen.

    Gruß

    UPDATE

    Ich hab die Parameter jetzt umgeschrieben. Hab aber immer noch das Problem dass der Zurückgegebene Wert -5 und nicht -1 ist. Siehe Bilder

    Gruß
    Bilder
    • Screenshot.jpg

      16,01 kB, 627×50, 183 mal angesehen
    • USBInit.jpg

      91,02 kB, 788×546, 179 mal angesehen
    Der Beitrag wurde aus 100% wiederverwendbaren Elektronen erstellt!

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „wolfi_bayern“ () aus folgendem Grund: Test durchgeführt

    wolfi_bayern schrieb:

    ByRef hab ich verwendet,
    um innerhalb von .NET Daten zurückzubekommen.
    Wenn ich aus einer C / C++-DLL Daten zurückholen will, muss ich IntPtr und Marshal verwenden.
    Lade Dir mein Beispiel runter,
    kompiliere es,
    teste es,
    verstehe es,
    wende es an.
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Guten Morgen!

    Danke erstmal für die schnellen Hinweise. DLL-tut hab ich gemacht. Das FAR PASCAL hat mich die ganze Zeit verwirrt bis ich dahinter gekommen bin, dass das für VB keine Auswirkung hat sondern nur für den Compiler der DLL...

    bezüglich Verstehen am Beispiel String übergeben:

    Ein leerer Zeiger wird erzeugt und an die Funktion übergeben. Hier kann die DLL-Funktion einen Wert hineinspeichern.
    Ein zweiter Zeiger wird erzeugt. Dieser Zeiger weiß wo die Marshal-Funktion das Ergebnis vom Umwandeln abgelegt hat. Umgewandelt wird das was an der Speicherposition des ersten Zeigers abgelegt ist.
    Umgewandelt werden muss vermutlich weil C und .NET Strings bzw. Char unterschiedlich verarbeiten...?!
    Eine ByRef-Übergabe des Zeigers an die Funktion ist nich nötig, da durch die übergebenen Zeiger die Daten gelesen werden. Sozusagen an der Funktion vorbei...

    Ich denke ich habs richtig verstanden...
    Der Beitrag wurde aus 100% wiederverwendbaren Elektronen erstellt!

    wolfi_bayern schrieb:

    Sozusagen an der Funktion vorbei...
    Nicht ganz.
    Ein wesentlicher Unterschied der Programmiersprachen (und der entsprechenden Compiler) besteht in der Handhabung von Strings.
    Möglichkeiten der Ablage im Speicher:
    Länge (1, 2, 4 Byte) + n Zeichen
    n Zeichen, Endekennung: 0
    Ablegen der Zeichen in einem Vektor<Byte>
    usw.
    Das ganze für Ansi (8 Bit pro Charakter) und Unicode (16 Bit pro Charakter)
    sowie beliebige Konvertierungen zwischen denen hin und her.
    Dann gibt es die Windows-Welt und die Unix-Welt (bzw. Motorola), da ist die Anordnung von 16-, 32-, 64-Bit-Zeichen im Speicher anders:
    16-Bit:
    8 Bit high, 8 Bit low (HL)
    bzw.
    8 Bit low, 8 Bit high (LH)
    usw.
    Bei Unicode-Dateien gibt es dafür das ByteOrderMark (BOM), das sagt, ob der Inhalt HL oder LH abgespeichert ist.
    Unter .NET intern wurde mit all solch Zeugs aufgeräumt, es wurde ein (Quasi-)Standard vorgegeben.
    -----
    Nun kommt @wolfi_bayern: und will von der "heilen" .NET-Welt aus mit einer irgend-anders-Welt Daten austauschen.
    Da die irgend-anders-Welt das weder übernimmt noch kann, muss solch halt von der .NET-Seite aus organisiert werden.
    Damit das funktioniert, wurde ein Marshal eingestellt, der sich um dies kümmert.
    Der IntPtr ist lediglich die Verpackung für einen nicht kompatiblen Parameter, der vom Marshal kompatibilisiert wurde. :D
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Hallo!

    Danke RodFromGermany für die erklärung! Jetzt ist einiges klarer.

    Hab mein Programm auch umgeschrieben laut dem Beispiel.

    Kann jetzt mit der DLL kommunizieren und ich krieg auch Pointer zurück. Allerding kann ich keinen Text auslesen (ANSI). Könnte es sein dass es sich um UNICODE handelt und deshalb nicht funktioniert? Wenn ja wie bzw. wo im Treiber ist das definiert. Wenn nein woran kanns sonst noch liegen????

    Danke erstmal!!!

    Gruß Wolfgang
    Der Beitrag wurde aus 100% wiederverwendbaren Elektronen erstellt!
    Hallo!

    Anbei ein teil meines Quellcodes. Diese vier Aufrufe nutze ich im Moment. Die anderen hab ich noch nicht angepasst solang es nicht richtig geht.


    VB.NET-Quellcode

    1. Declare Auto Function CounterGetSerialnumber Lib "HS_UC.dll" _
    2. Alias "HS_UC_GetSerialnumber" (ByVal CounterNo As Integer, _
    3. ByVal Serialnumber As IntPtr) As Integer
    4. Declare Auto Function CounterGetVersion Lib "HS_UC.dll" _
    5. Alias "HS_UC_GetVersion" (ByVal CounterNo As Integer, _
    6. ByVal DllVersion As IntPtr, _
    7. ByVal FirmwareVersion As IntPtr) As Integer
    8. Declare Auto Function CounterVal Lib "HS_UC.dll" _
    9. Alias "HS_UC_GetCounterVal" (ByVal CounterNo As Integer, _
    10. ByVal Value As IntPtr, _
    11. ByVal Status As IntPtr) As Integer
    12. Declare Auto Function CounterInit Lib "HS_UC.dll" _
    13. Alias "HS_UC_Init" (ByVal CounterNo As Integer, _
    14. ByVal Type As Integer) As Integer


    Hier noch das auslesen:

    VB.NET-Quellcode

    1. Dim successANSI As Integer
    2. Dim DllVer As String = String.Empty
    3. Dim FWVer As String = String.Empty
    4. Dim p1 As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(IntPtr.Zero))
    5. Dim p2 As IntPtr = Marshal.AllocHGlobal(Marshal.SizeOf(IntPtr.Zero))
    6. successANSI = CounterGetVersion(CInt(numNumber.Value), p1, p2)
    7. If successANSI = 0 Then
    8. Dim p11 As IntPtr
    9. Dim p21 As IntPtr
    10. p11 = Marshal.ReadIntPtr(p1)
    11. p21 = Marshal.ReadIntPtr(p2)
    12. DllVer = Marshal.PtrToStringAnsi(p11)
    13. FWVer = Marshal.PtrToStringAnsi(p21)
    14. lblDllVersion.Text = DllVer
    15. lblFWVersion.Text = FWVer
    16. Else
    17. Messagebox.show("Unbekannte Rückmeldung: " + success.ToString)
    18. End If


    Hier bekomm ich als Rückmeldung einen Pointer geliefert. Aber das ptrToStringAnsi liefert nix

    Gruß wolfgang
    Der Beitrag wurde aus 100% wiederverwendbaren Elektronen erstellt!
    Du denkst da viel zu kompliziert, mal ein Beispiel

    C-Quellcode

    1. long FAR PASCAL EXPORT HS_UC_GetSerialnumber(long CounterNo, char *Serialnumber);

    Die Typen: Ein long istn Long und kein Integer, FAR PASCAL ist __sdtcall, ein char* ist ein Bytepointer, damit man den nicht extra per IntPtr marshallen muss, benutzt man hier nen StringBuilder.
    Also hätte man in C# dann

    C-Quellcode

    1. [DllImport("Pfad", CharSet = CharSet.Ansi, EntryPoint = "HS_UC_GetSerialnumber")]
    2. static extern long GetSerialNumber(long CounterNo, [MarshalAs(UnmanagedType.LPStr)]StringBuilder b);
    Ich hab meine bisherigen Aufrufe an das Beispiel von RodfromGermany angelehnt.

    So... jetz bin ich richtig verwirrt!!!

    Hab mal Dein Beispiel auf VB umgemodelt... Dann gibts erstmal eine Access violation. Hab dann den String nicht als Stringbuilder sonder als String angegeben... Immer noch fehler. Erst als ich dann statt long integer verwendet hab ging der Aufruf. Allerdings wieder mit leerem string.

    VB.NET-Quellcode

    1. <DllImport("HS_UC.dll", CharSet:=CharSet.Ansi, EntryPoint:="HS_UC_GetSerialnumber")> _
    2. Public Shared Function GetSerialNumber(CounterNo As Integer, <MarshalAs(UnmanagedType.LPStr)> b As String) As Integer
    3. End Function


    Ich werd noch wahnsinnig mit dem scheiß.

    Achja... habs ja zuerst mit long deklariert. Dann sagt mir VS dass das 64bit sind. Hab ja vor der Anfrage hier schon bei MSDN und Co gelesen. Da wurde überall gesagt das das Long in der DLL nur 32bit hat. Mein Programm hab ich ja auch 32 bit kompiliert. würd ichs als 64 bit kompilieren funktionieren gar keine Zugriffe.

    Hinzu kommt ja noch z. B. beim Aufruf um den Wert zu holen, dass hier zwei Parameter Wert und Status als Zeiger übergeben werden müssen.

    Irre...
    Der Beitrag wurde aus 100% wiederverwendbaren Elektronen erstellt!
    Da nimmt man normal n Stringbuilder, der's schneller. Ausserdem ist bei mir ein Long n Long, ob ich mein Programm x64 oder x32 kompiliere braucht ein long 8 Byte, ein long long 16. Ich wüsste nicht wieso sich das ändern sollte. Wie rufst du die Funktion denn auf ?

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Gonger96“ ()

    Jetz ruf ich den Code so auf und es stürzt nix ab.

    VB.NET-Quellcode

    1. Dim b As String = String.Empty
    2. Dim s = GetSerialNumber(1, b)
    3. MsgBox(s.ToString)
    4. MsgBox(b)


    und so hab ich den dllimport umgebaut:

    VB.NET-Quellcode

    1. <DllImport("HS_UC.dll", CharSet:=CharSet.Ansi, EntryPoint:="HS_UC_GetSerialnumber")> _
    2. Public Shared Function GetSerialNumber(CounterNo As Integer, <MarshalAs(UnmanagedType.LPStr)> b As String) As Integer
    3. End Function


    Hab Dein Beispiel mit dem Snippetconverter übersetzt und angepasst. Aber dann gabs beim Aufruf eine access violation.

    Liegst evtl. am VS-Express???

    Ich denke der Aufruf is für jemand der Ahnung hat nix besonderes. Ich steh aber scheinbar irgendwie auf dem Schlauch. Mach jetz schon 1,5 Wochen da dran rum aus der scheiß DLL werte auszulesen. Hab auch mit dem Programmierer schon kontakt gehabt. Der sagt er hat nix für .NET. sonst wärs ja vermutlich kein problem... So ein Dreck!!!

    Aber danke für die Hilfe!!!!!!!!
    Bilder
    • Long.jpg

      23,92 kB, 786×93, 165 mal angesehen
    Der Beitrag wurde aus 100% wiederverwendbaren Elektronen erstellt!
    Nimm nen StringBuillder. Ich bin grade etwas verwirrt, ich hab grade getestet - ein long und int verbrauchen jeweils 4 Byte egal ob x64 oder x32. Standardmäßig müsste ein long maximal +2147483647 tragen und ein int +32767. +32767 passen genau in 2 Byte, 2147483647 genau in 4 Bytes. Entwder macht Windows da was seltsam oder mein Compiler. Da wird mir als Maximum für int und long 2147483647 ausgespuckt und sizeof bringt trotzdem 4 Bytes für beide. Da kann ja irgendwas nicht hinhauen :huh:

    /Edit
    Ah ^^
    int kannste auslegen wie du willst, entweder istn int ein short int oder ein long int. Laut Standard muss es minimal short sein. Trotzdem bleiben diese Typen fix und verbrauchen nicht mehr oder weniger Speicher bei anderer Architektur, sowas gilt nur für Zeiger.

    char 1 Byte
    short int 2 Byte
    long int 4 Byte
    long long int 8 Byte
    float 4 Byte
    double 8 Byte

    Dieser Beitrag wurde bereits 2 mal editiert, zuletzt von „Gonger96“ ()

    @wolfi_bayern:: long in C/C++ ist Integer in .NET :!: :!: :!:
    Int32 <=> int, long
    im Gegensatz zu
    Int64 <=> LONGLONG bzw. __int64
    Falls Du diesen Code kopierst, achte auf die C&P-Bremse.
    Jede einzelne Zeile Deines Programms, die Du nicht explizit getestet hast, ist falsch :!:
    Ein guter .NET-Snippetkonverter (der ist verfügbar).
    Programmierfragen über PN / Konversation werden ignoriert!
    Die Datentypen sind mir klar... stand auch so bei MSDN dass das long in c als integer(int32) umzusetzen ist...
    Habs jetz nochmal so gemach:

    VB.NET-Quellcode

    1. <DllImport("HS_UC.dll", CharSet:=CharSet.Ansi, EntryPoint:="HS_UC_GetSerialnumber")> _
    2. Public Shared Function GetSerialNumber(CounterNo As Integer, <MarshalAs(UnmanagedType.LPStr)> b As StringBuilder) As Integer
    3. End Function


    und den Aufruf so:

    VB.NET-Quellcode

    1. Dim b As New StringBuilder()
    2. Dim s = GetSerialNumber(1, b)
    3. MsgBox(s.ToString)
    4. MsgBox(b.ToString)


    ist der Aufruf bzw. die verwendung von b (.tostring) so richtig???
    Der Beitrag wurde aus 100% wiederverwendbaren Elektronen erstellt!