Funktionsaufruf aus einer DLL

  • VB.NET

Es gibt 12 Antworten in diesem Thema. Der letzte Beitrag () ist von ErfinderDesRades.

    Funktionsaufruf aus einer DLL

    Ich möchte eine Funktion aus einer DLL aufrufen, die wie folgt definiert ist und unter VB6 auch einwandfrei fnktioniert.

    So ist sie im Handbuch beschrieben:

    VB.NET-Quellcode

    1. int LoadConnection_ex6 (int ConNr, char* pAccessPoint, int ConTableLen CON_TABLE_TYPE * pConTable);


    Aufruf unter VB6:

    VB.NET-Quellcode

    1. Declare Function LoadConnection_ex6 Lib "Prodave6.dll" _
    2. (ByVal ConNr As Integer, ByVal AccessPoint As String, _
    3. ByVal ConTableLen As Integer, pConTable As CON_TABLE_TYPE) As Long
    4. ret = LoadConnection_ex6(nr, "S7ONLINE", LenB(adr), adr)


    Unter VB6 entsprach Long dem INT32, Int dem INT16 und Char war lediglich 1 Byte lang.
    Also habe ich die Definition wir folgt angepasst:

    VB.NET-Quellcode

    1. Declare Function LoadConnection_ex6 Lib "Prodave6.dll" _
    2. (ByVal ConNr As Int16, ByVal AccessPoint As IntPtr, _
    3. ByVal ConTableLen As Int16, ByVal pConTable As IntPtr) As Int16


    Aufgerufen habe ich später

    VB.NET-Quellcode

    1. Public Structure CON_TABLE_TYPE
    2. Public adr1 As Byte ' Verbindungsadresse
    3. Public adr2 As Byte ' Verbindungsadresse
    4. Public adr3 As Byte ' Verbindungsadresse
    5. Public adr4 As Byte ' Verbindungsadresse
    6. Public adr5 As Byte ' Verbindungsadresse
    7. Public adr6 As Byte ' Verbindungsadresse
    8. Public AdrType As Byte ' Typ der Adresse MPI(1) IP(2) MAC(3)
    9. Public SlotNr As Byte ' Slot-Nummer
    10. Public RackNr As Byte ' Rack-Nummer
    11. End Structure
    12. dim adr As CON_TABLE_TYPE
    13. ....
    14. Dim adr_lg As UInt32 = System.Runtime.InteropServices.Marshal.SizeOf(adr)
    15. ' Nullterminierter String "S7ONLINE"
    16. Dim verbindung() As Byte = _
    17. {Convert.ToByte("S"c), _
    18. Convert.ToByte("7"c), _
    19. Convert.ToByte("O"c), _
    20. Convert.ToByte("N"c), _
    21. Convert.ToByte("L"c), _
    22. Convert.ToByte("I"c), _
    23. Convert.ToByte("N"c), _
    24. Convert.ToByte("E"c), _
    25. 0}
    26. ' Pointer auf die Verbindungsadresse erstellen
    27. Dim pAuf_Adr As IntPtr = Marshal.AllocCoTaskMem(Marshal.SizeOf(adr))
    28. Marshal.StructureToPtr(adr, pAuf_Adr, False)
    29. ' Pointer auf den Verbindungsnamen erstellen
    30. Dim pAuf_Verbindung As IntPtr = Marshal.AllocCoTaskMem(9)
    31. Marshal.StructureToPtr(verbindung(0), pAuf_Verbindung, False)
    32. ' Funktionsaufruf
    33. ret_ex6 = LoadConnection_ex6(nr, pAuf_Verbindung, adr_lg, pAuf_Adr)


    Die Funktion liefert mir eimmer einen Verbungsfehler zurück. Dies deutet dauf hin , dass ich die Parameter nicht richtig übergebe.
    Also die Zeiger pAuf_Verbindung sowie pAuf_Adr

    Für Hinweise bin ich sehr dankbar
    a) Gib Strukturen welche du marshallen willst immer nen StructLayoutAttribute mit Parameter Sequential. Dadurch stellst du sicher, dass die Felder der Struktur auch wirklich im Speicher in der selben Reihenfolge angeordnet sind.
    b) Probiere mal in der Signatur den IntPtr wegzulassen und direkt die Struktur anzugeben. Dafür aber mit ByRef anstatt ByVal.
    c) Du kannst auch wie bei b) alles verwenden nur kein ByRef sondern ByVal. Dafür musst du aber dem Parameter das MarshalAs Attribut geben. Dort gibst du als Argument UnmanagedType.LPStruct an.


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
    char* kannst Du nicht so einfach mit String übersetzen, in diesem Sinne sind char's ggf. Bytes.
    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!
    Ou, das habe ich ja ganz übersehen. Auch für char* ist hier sicher etwas passendes dabei: msdn.microsoft.com/en-us/libra…rvices.unmanagedtype.aspx. Einfach das MarshalAs Attribut verwenden.


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
    Uiii, wenn ich das richtig verstanden habe muss die Deklaration so aussehen:

    VB.NET-Quellcode

    1. Declare Function LoadConnection_ex6_5 Lib "Prodave6.dll" Alias "LoadConnection_ex6" _
    2. (ByVal ConNr As Int16, <MarshalAs(UnmanagedType.LPStr)> ByVal AccessPoint As Byte, _
    3. ByVal ConTableLen As Int16, <MarshalAs(UnmanagedType.LPStruct)> ByVal ConTable As CON_TABLE_TYPE) As Int16


    und der Aufruf dann

    VB.NET-Quellcode

    1. ret_ex6 = LoadConnection_ex6_5(Verbindungs_nr, verbindung(0), adr_lg, adr)


    Das führt dann aber zum Laufzeitfehler
    "parameter #2" kann nicht gemarshallt werden: Ungültige verwaltete/nicht verwaltete Typenkombination (Byte/SByte muss mit I1 oder U1 kombiniert werden).
    Du musst dann natürlich aber schon auch einen String beim 2. Parameter nehmen. Das marshalas attribut macht, dann nen char* draus.


    Opensource Audio-Bibliothek auf github: KLICK, im Showroom oder auf NuGet.
    @thefiloe:: Jou.
    @myownshadow:: Sieh Dir mal an, was alles geMarshalt werden kann. Probier das mal durch und sieh, wo es geht.
    Integer ist allerdings auch bei VB6 ein Int32.
    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!
    Jetzt hapert es nur noch an der Struktur

    VB.NET-Quellcode

    1. <StructLayout(LayoutKind.Sequential)> _
    2. Public Structure CON_TABLE_TYPE
    3. Public adr1 As Byte ' Verbindungsadresse
    4. Public adr2 As Byte ' Verbindungsadresse
    5. Public adr3 As Byte ' Verbindungsadresse
    6. Public adr4 As Byte ' Verbindungsadresse
    7. Public adr5 As Byte ' Verbindungsadresse
    8. Public adr6 As Byte ' Verbindungsadresse
    9. Public AdrType As Byte ' Typ der Adresse MPI(1) IP(2) MAC(3)
    10. Public SlotNr As Byte ' Slot-Nummer
    11. Public RackNr As Byte ' Rack-Nummer
    12. End StructureDim adr As CON_TABLE_TYPE
    13. Declare Function LoadConnection_ex6_5 Lib "Prodave6.dll" Alias "LoadConnection_ex6" _
    14. (ByVal ConNr As Int16, <MarshalAs(UnmanagedType.LPStr)> ByVal AccessPoint As String, _
    15. ByVal ConTableLen As Int16, <MarshalAs(UnmanagedType.LPStruct)> ByVal ConTable As CON_TABLE_TYPE) As Int16
    16. ret_ex6 = LoadConnection_ex6_5(Verbindungs_nr, "S7ONLINE", adr_lg, adr)


    Die Fehlermeldung lautet jetzt

    "parameter #4" kann nicht gemarshallt werden: Ungültige verwaltete/nicht verwaltete Typenkombination (dieser Werttyp muss mit "Struct" kombiniert werden)..


    Ich habe doch zunächst einen Zeiger auf Char definiert, danach auf die Struktur.
    Da solltest Du über IntPrt gehen.
    Hole Dir Speicher entsprechend der Größe, befülle ihn, ruf Deine Funktion auf und gib den Speicher wieder frei.
    Numm das Beispiel in dem Link.
    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!
    Es ist vollbracht, es funktioniert.

    Der richtige Aufruf, nachdem ich die MarshalAs Funktion wohl endlich verstanden habe ist:

    VB.NET-Quellcode

    1. Declare Function LoadConnection_ex6_1 Lib "Prodave6.dll" Alias "LoadConnection_ex6" _
    2. (ByVal ConNr As Int16, <MarshalAs(UnmanagedType.LPStr)> ByVal AccessPoint As String, _
    3. ByVal ConTableLen As Int16, ByRef pConTable As CON_TABLE_TYPE) As Int16


    Vielen Dank für die vielen tollen Tipps.

    Am Rande sei noch folgendes bemerkt:

    Diese DLL, die ich da benutzen muss ist von SIEMENS und dient der Kommunikations mit S7 SPS-Steuerungen.

    Natürlich habe ich auch bei der Hotline von Siemens nachgefragt und nach 2 Tagen dann die Auskunft erhalten:

    "Der Aufruf dieser Funktionen funktioniert NICHT mehr mit VB.net, sowie C#. Ich solle mir doch ein C++ Programm schreiben, damit dei Anbindung wieder funktioniert"

    Natürlich hat mich diese Auskunft erst einmal in tiefe Depressionen gestürzt, aber bei näherem Nachdenken konnte das ja gar nicht sein.

    Mann muss sich eben "NUR" mit den Besonderheiten der einzelnen Programmiersprachen auseinandersetzen. Und das ist manchmal eben nicht so einfach. Aber dafür gibt es ja glücklicherweise Foren und kompetentere Mitstreiter :thumbsup:

    Also, auf zum nächsten Problem ...