DllImport: Funktion als char*

  • VB.NET
  • .NET (FX) 4.5–4.8

Es gibt 3 Antworten in diesem Thema. Der letzte Beitrag () ist von Hutti.

    DllImport: Funktion als char*

    Hallo werte VBler!

    Ich habe ein Problem an dem ich mir die Zähne ausbeisse und ich habe dabei den Verdacht, dass ich etwas komplett falsch aufgefasst habe.


    (1) Ausgangslage:

    Ich habe eine .dll sowie auch die Dokumentation als .pdf und weiter einen "Simulator" .exe. Der Simulator gibt Antworten zurück und ersetzt somit die Laseranlage, die ich eigentlich letzten Endes ansprechen will.
    In der Dokumentation steht sowas hier:
    2.1 Connect To Server
    Prototype: int ConnectToServer(void);
    Returns: 0 for successful completion, -1 for error.
    Description: Establish a network connection of the client PC and the Hyper Rapid server PC.


    In meinem VB.Net Projekt (Als eigene Class namens "Wrapper"):

    VB.NET-Quellcode

    1. Const Pfad As String = "RapidClient.dll"
    2. <DllImport(Pfad)>
    3. Public Shared Function ConnectToServer() As Integer
    4. End Function


    Rufe ich nun die Funktion auf:

    VB.NET-Quellcode

    1. Wrapper.ConnectToServer()


    Meldet der Simulator: "Client connected"
    Ich gehe somit davon aus dass die .dll funktioniert und der weiter unter geschilderte Fehler bei mir liegt.



    (2) Ziel

    Das Ziel ist simpel: Alle Funktionen einbauen die angeboten werden und sich aus der Dokumentation herauslesen lassen.
    Auch Funktionen wie z.b diese:
    2.3 Laser Status Get
    Prototype: char *StatusGet(void);
    Returns: A pointer to a null terminated ASCII string indicating the status of the laser system.
    Description: Used to identify the laser status.




    (3) Problem

    Wenn ich nun eben jenes Beispiel (siehe 2) verwirklichen will:

    VB.NET-Quellcode

    1. Const Pfad As String = "RapidClient.dll"
    2. <DllImport(Pfad)>
    3. Public Shared Function StatusGet() As Byte
    4. End Function


    Will es nicht wirklich funktionieren. Ich habe bei der Suche (Google) mehrere mögliche Antworten bekommen. Zum einen dass es als String deklariert werden kann und das auch funktionieren soll. Oder auch als StringBuilder und auch als Byte und Byte().

    Folgende Antworten erhalte ich:

    String: Nichts. Das Programm schliesst sich einfach auch mit Try Catch kommt nichts.
    Byte: ein Zahlenwert (bisher immer 224)
    Byte(): "return value" kann nicht gemarshallt werden: Ungültige verwaltete/nicht verwaltete Typenkombination..
    StringBuilder: Nichts. Das Programm schliesst sich einfach auch mit Try Catch kommt nichts.

    Hier eines der Links die ich bezüglich Typen aufgesucht habe:
    marvintest.com/KnowledgeBase/KBArticle.aspx?ID=210

    Ich verstehe noch immer nicht wieso es bei String einfach die Anwendung beendet ohne eine Fehlermeldung oder sonst wie mit mir zu Kommunizieren, aber da es bei Connection Funktion die erwartete Aktion ausführ und bei Byte eine Zahl zurückkommt, kann es nicht an der DLL liegen sondern an mir alleine dass ich das char* nicht richtig ins VB.Net übersetze.


    Kernfrage: Was für ein Typ stellt eine Funktion in VB.Net dar, welches in C als char* deklariert ist?


    Falls Jemand Zeit hat mir das zu erklären, wäre ich sehr dankbar dafür! Selbstverständlich auch anhand von Verweise über Links zu anderen Foren und Beiträgen, aber bitte immer mit der Bereitschaft mich aufzuklären sollte ich den Inhalt nicht gänzlich verstehen.

    Danke dass ihr euch Zeit nehmt.

    Gruss Hutti


    *Topic verschoben*

    Dieser Beitrag wurde bereits 1 mal editiert, zuletzt von „Marcus Gräfe“ ()

    Normalerweise wenn man einen C-String von einer DLL bekommt, wird der char pointer als Parameter übergeben

    C-Quellcode

    1. ​void StatusGet(char*)


    In dem Fall muss ein StringBuilder übergeben werden, da ein String nicht schreibbar ist, StringBuilder allerdings schon. Der Marshaller kümmert sich dann um den Rest.

    Wenn der Rückgabe Wert ein char* ist, dann kannst du nur den Pointer nehmen und per hand konvertieren:

    VB.NET-Quellcode

    1. Private Function PtrToStringUtf8(ByRef ptr As IntPtr) As String ' aPtr is nul-terminated
    2. If (ptr = IntPtr.Zero) Then
    3. Return ""
    4. End If
    5. Dim Len As Integer = 0
    6. While (System.Runtime.InteropServices.Marshal.ReadByte(ptr, Len) <> 0)
    7. Len += 1
    8. End While
    9. If (Len = 0) Then
    10. Return ""
    11. End If
    12. Dim array As Byte() = New Byte(Len) {}
    13. System.Runtime.InteropServices.Marshal.Copy(ptr, array, 0, Len)
    14. Return System.Text.Encoding.UTF8.GetString(array)
    15. End Function
    16. <DllImport("RapidClient.dll", EntryPoint:="StatusGet")>
    17. Public Function StatusGetExternal() As IntPtr
    18. End Function
    19. Public Function StatusGet() As String
    20. Dim ptr As IntPtr = StatusGetExternal()
    21. ' assume returned string Is utf-8 encoded
    22. Return PtrToStringUtf8(ptr)
    23. End Function​


    Du solltest allerdings mal schauen ob es eine Methode zum freigeben des Speichers (FreeString oder so ähnlich) in der DLL gibt. Ansonsten leakst du nämlich memory.
    SWYgeW91IGNhbiByZWFkIHRoaXMsIHlvdSdyZSBhIGdlZWsgOkQ=

    Weil einfach, einfach zu einfach ist! :D
    Ich muss mich entschuldigen! Ich habe diesen Beitrag bereits gelesen, aber diese Stelle:

    RodFromGermany schrieb:

    Die Daten sind dann in Variablen vom Typ IntPtr abgelegt, das rein- und rauskopieren wird in der Klasse Marshal durchgeführt.

    anscheinend komplett überlesen... "Marshal" ist der Suchbegriff den ich brauche! Danke :)



    BiedermannS schrieb:

    In dem Fall muss ein StringBuilder übergeben werden, da ein String nicht schreibbar ist, StringBuilder allerdings schon. Der Marshaller kümmert sich dann um den Rest.


    Nun verstehe ich alles viel besser! Danke für die Erklärung.

    BiedermannS schrieb:

    Wenn der Rückgabe Wert ein char* ist, dann kannst du nur den Pointer nehmen und per hand konvertieren
    Du solltest allerdings mal schauen ob es eine Methode zum freigeben des Speichers (FreeString oder so ähnlich) in der DLL gibt. Ansonsten leakst du nämlich memory.


    Ich werde mich umsehen und bald Rückmeldung machen, bis dahin ist die Frage beantwortet und ich werde diesen Beitrag als gelöst markieren!

    Danke Leute :) :thumbsup:


    EDIT 14:21

    BiedermannS schrieb:

    freigeben des Speichers


    Volker Steitz

    VB.NET-Quellcode

    1. Private Declare Function SetProcessWorkingSetSize Lib "kernel32.dll" ( _
    2. ByVal process As IntPtr, _
    3. ByVal minimumWorkingSetSize As Integer, _
    4. ByVal maximumWorkingSetSize As Integer) _
    5. As Integer
    6. Public Sub FlushMemory()
    7. GC.Collect()
    8. GC.WaitForPendingFinalizers()
    9. If (Environment.OSVersion.Platform = PlatformID.Win32NT) Then
    10. SetProcessWorkingSetSize(Process.GetCurrentProcess().Handle, -1, -1)
    11. End If
    12. End Sub

    Dieser Beitrag wurde bereits 5 mal editiert, zuletzt von „Hutti“ ()