dll aus C in vb.net aufrufen

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

Es gibt 129 Antworten in diesem Thema. Der letzte Beitrag () ist von tron25.

    Nun bekomme ich beim Ausführen folgende Fehlermeldung:

    Der Assistent für verwaltetes Debugging ""PInvokeStackImbalance"" hat ein Problem in ""D:\Programmierung\PunktBilder\PunktBilder 2016\PunktBilder\PunktBilder\bin\Debug\PunktBilder.vshost.exe"" festgestellt.

    Zusätzliche Informationen: Ein Aufruf an die PInvoke-Funktion "PunktBilder!PunktBilder.PunktbilderFormular::lou_translateString" hat das Gleichgewicht des Stapels gestört. Wahrscheinlich stimmt die verwaltete PInvoke-Signatur nicht mit der nicht verwalteten Zielsignatur überein. Überprüfen Sie, ob die Aufrufkonvention und die Parameter der PInvoke-Signatur mit der nicht verwalteten Zielsignatur übereinstimmen.
    Entweder die Definition ist falsch, oder die CallingConvention. Ich tippe ja(wie bereits zuvor) auf die Calling-Convention und vermute, dass es Cdecl sein dürfte.
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---

    jvbsl schrieb:

    dass es Cdecl sein dürfte.
    Steht doch in der P/Invoke definition in Zeile 5 meines letzen Posts. (Ich geh jetzt einfach mal davon aus, dass @tron25 den Code verwendet.

    @tron25: Könntest du uns eventuell die DLL zur Verfügung stellen, so dass wir auch mal testen können.
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    Du erstellst eine Anwort, klickst auf Erweiterte Ansicht und dann unten auf den Reiter Dateianhänge. Da kannst du dann die Dateien anhängen. Aber am besten alles Zip-en, dann is alles beisammen :D
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    Bitteschön. Als Übersetzungstabelle wird die Datei "de-de-g2.ctb" angegeben. Wenn alles funktioniert, müßte aus dem Wort "wochenende" die abgekürzte Version "wo4ccde" herauskommen. 4 ist die Abkürzung für "ch" und "c" bedeutet "en".

    Dateien
    • Braille.rar

      (1,95 MB, 126 mal heruntergeladen, zuletzt: )

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

    Zum Testen braucht man lediglich die DLL und zwei Textboxen. In der ersten Textbox steht ein beliebiger Text, der dann mit Hilfe der DLL in Blindenkurzschrift übersetzt werden soll. Wenn man beispielsweise in das erste Textfeld das Wort "Wochenende" einträgt und die Funktion ausführt, sollte dann im zweiten Textfeld die Zeichenkette "wo4ccde" stehen. Beim Übersetzen wurden die Buchstaben "ch" in "4" und "en" jeweils in "c" umgewandelt. Als Übersetzungsdatei wählt man "de-de-g2.ctb" im Verzeichnis "Braille" aus. Diese Datei enthält die deutschen Kürzungsregeln.
    lou_translateString scheint einfach nur:

    C-Quellcode

    1. return lou_translate(tableList, inbuf, inlen, outbuf, outlen, typeform, spacing, 0, 0, 0, mode);

    zu sein, und lou_translate wiederum:

    C-Quellcode

    1. return trace_translate(tableList, inbuf, inlen, outbuf, outlen, typeform, spacing, 0, 0, 0, 0, 0, mode);


    die 0en sind dabei die eigentlichen optionalen Parameter.

    lou_translateString scheint außerdem doch tatsächlich nen stdcall zu sein, trace_translate hingegen cdecl(what the hell).
    Dabei scheint er nie größer outlen zu schreiben, d.h. du musst outlen wie es aussieht selbst groß genug machen, denn er wird dir nicht mitteilen, dass der output eigentlich größer ist.

    Bei dem ganzen kann ich mich natürlich irren, ich habs jetzt nicht ausprobiert, sondern nur etwas die disassembly angeschaut.
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    Hallo @tron25,

    ich hab mir mal die Lib angesehen und bin auf 2 Sachen gestoßen:
    1.) Wenn du dir die Beschreibung der Funktion noch mal durchliest, dann wirst du feststellen, dass dein Code

    VB.NET-Quellcode

    1. If lou_translateString(...) = 0 Then
    völliger Schwachsinn ist. Denn (wie in der letzten Zeile steht The function returns 1 if no errors were encountered and 0 if a complete translation could not be done => 0 bedeutet ein Fehler ist aufgetreten, eine 1 zeigt an, dass alles gut gelaufen ist.
    EDIT: IMHO hat der Programmierer der Lib hier schon einen Fehler gemacht, denn afaik wird in C und in C++ schon immer die 0 als Return value dafür verwendet um anzuzeigen 0 Fehler sind aufgetreten => alles OK und Zahlen >0 sollen anzeigen WELCHER Fehler aufgetreten ist. Das kann dann etwa per Batch und ECHO %errorcode% ermittelt werden (Historisch bedingt)

    2.) Bezüglich der deklaration der Funktion: So wie es aussieht (und damit auch an @jvbsl), die Aufrufkonvention StdCall und WinApi funktionieren. Auch lassen sich die <MarshalAs(...)> Attribute entfernen. Die braucht man nicht.

    Ich hab jetzt mal den Code mit den oben genannten Änderungen laufen lassen und lustigerweise spuckt mir der StringBuilder kein wo4ccde sondern {'}aus. Ich kann mir das nur erklären, als dass eine falsche Tabelle angegeben wird.

    Lg Radinator
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    nene, dumm ist das nicht, er hätte nur nen typedef für bool machen müssen.
    Also passt 1 für erfolgreich und 0 für nicht erfolgreich sehr wohl, dürfte sogar einfach mit einem bool return value in .Net funktionieren.
    Also wenn ich nur zwei mögliche values hab, dann passt das, für einen Return Code wird, jedoch tatsächlich 0 für erfolgreich.
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---

    jvbsl schrieb:

    lso wenn ich nur zwei mögliche values hab, dann passt das, für einen Return Code wird, jedoch tatsächlich 0 für erfolgreich.
    Mag sein, dass das hier in dem Fall passt, jedoch hab ich mir sagen lassen - von jemanden der vor 30 Jahren als Software-Ingenieure angefangen hat und damals für die Firmware von Microcontrollern zuständig war - dass das mit dem Return Wert von 0, 1, ... damit zusammen hängt, als dass man damals ja noch keine Exceptions hatte. Damals wurden die Programme noch per Batch aufgerufen und wenn das Programm abgeschmiert ist, konnte man den %errorlevel% (nicht errorcode) auswerten und bekam anschließend Meldung, was für ein Fehler aufgetreten ist.

    So stand, wie gesagt eine 0 für 0 Fehler, eine 1 für meinetwegen einen Eingebe Fehler, eine 2 für nicht vorhanden Datei und so weiter. Man nannte das halt damals StatusCode

    Wenn du dir mal die MSDN Code Guidlines ansiehst unter Exceptions, dann sagt MS auch gleich im ersten Punks:
    X DO NOT return error codes.


    Lg Radinator
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell
    Wer vor 30 Jahren angefangen hat ist oftmals nicht auf dem Stand der Dinge. Und ja Statuscodes waren und sind auch heute noch in C sinnvoll(in Cpp gibts exceptions). Jedoch hat man auch damals wenn es eben nur eine boolesche ausgabe gibt es genau so gemacht, schließlich war C damals wie heute dies hier möglich:

    C-Quellcode

    1. if(1)

    und somit viel sinnvoller zu verwenden als ein StatusCode. Den Statuscode einer Funktion hast du damals auch nicht bekommen(schließlich war es mit Debuggern ja auch noch mau), man hat nur den ExitCode des Programms bekommen und das bekommt man auch nur, wenn man den StatusCode aller funktionen bis ganz nach oben durchreicht.
    Diese Funktion die wir hier haben ist vergleichbar mit TryParse. da willste auch keinen Statuscode, du willst einfach nur wissen, ob es funktioniert hat oder nicht.->bool return

    C-Quellcode

    1. typedef int bool;
    2. //defines oder enum für true/false

    oder einfach stdbool.h includieren.
    Ich wollte auch mal ne total überflüssige Signatur:
    ---Leer---
    Hallo,

    vielen Dank für eure Bemühungen. Die Funktion mit Wahr oder Falsch habe ich nur geschrieben, um erst einmal festzustellen, ob der DLL-Aufruf syntaktisch korrekt funktioniert. Ich möchte eigentlich die DLL nutzen, um einen Text in Kurzschrift übersetzen zu lassen. Wenn ich morgen wieder auf Arbeit bin, lege ich die Readme-Datei hier herein. Dort werden die Funktionen beschrieben. Leider werde ich aber daraus nicht schlau. Vielleich könnt ihr mehr damit anfangen.

    Nochmals, vielen Dank und bis Morgen
    Ich beende diese Raterei jetzt mal. Folgendes läuft bei mir tadellos durch:

    VB.NET-Quellcode

    1. Imports System.Runtime.InteropServices
    2. Imports System.Text
    3. Module Module1
    4. 'int EXPORT_CALL lou_translateString
    5. '(const char *tableList,
    6. ' const widechar *inbuf,
    7. ' int *inlen,
    8. ' widechar * outbuf,
    9. ' int *outlen,
    10. ' char *typeform,
    11. ' char *spacing,
    12. ' int mode);
    13. <DllImport("liblouis-2.dll")>
    14. Function lou_translateString(
    15. <MarshalAs(UnmanagedType.LPStr)> tableList As String,
    16. <MarshalAs(UnmanagedType.LPWStr)> inbuf As String,
    17. ByRef inlen As Int32,
    18. <MarshalAs(UnmanagedType.LPWStr), Out()> outbuf As StringBuilder,
    19. ByRef outlen As Int32,
    20. <MarshalAs(UnmanagedType.LPStr)> typeform As String,
    21. <MarshalAs(UnmanagedType.LPStr)> spacing As String,
    22. mode As Int32) As Int32
    23. End Function
    24. Sub Main()
    25. Dim inbuf As String = "Wochenende"
    26. Dim expected As String = "Wo4ccde"
    27. Dim outbuf As New StringBuilder ' Auto-resize
    28. Dim outlen As Int32 = 100
    29. lou_translateString("de-de-g2.ctb", inbuf, inbuf.Length, outbuf, outlen, Nothing, Nothing, 0)
    30. ' Limit stringBuilder size in case of implicit memory reallocation
    31. outbuf.Length = outlen
    32. Debug.Assert(outbuf.ToString() = expected)
    33. End Sub
    34. End Module
    Gruß
    hal2000
    Wenn ich deinen Code bei mir einfüge, läuft das Programm auch ohne Fehler. Allerdings möchte ich die Funktion jederzeit aufrufen können und ihr einen Text mitgeben, der dann übersetzt wieder in einem anderen Textfeld ausgegeben werden soll. Wenn ich den Inhalt der "Sub Main" in eine Funktion kopiere, bekomme ich einen Assertionsfehler.

    Im Folgenden habe ich das komplette LibLouis-Paket hinzugefügt. Wenn man die Datei auspackt, gibt es im Hauptverzeichnis eine Readme.txt. In der werden die Funktionen und ihre Parameter ausführlicher erklärt.
    Dateien

    tron25 schrieb:

    der "Sub Main" in eine Funktion kopiere
    Am besten erstellst du dir eine eigene Klasse (mit den Namen NativeMethods), in diese fügst du die Funktion von @hal2000 ein und deklarierst sie als Public Shared Function.... Damit ermöglichst du den Zugriff, ohne eine Instanz der Klasse NativeMethods erstellen zu müssen.

    Edit: falles es wen interessiert, aber das ganze gibt es auch auf Github(also das liblouis-Projekt) :D
    In general (across programming languages), a pointer is a number that represents a physical location in memory. A nullpointer is (almost always) one that points to 0, and is widely recognized as "not pointing to anything". Since systems have different amounts of supported memory, it doesn't always take the same number of bytes to hold that number, so we call a "native size integer" one that can hold a pointer on any particular system. - Sam Harwell

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

    @tron25 Vielleicht führst Du Dir den Post #2 noch mal ausführlich zu Gemüte.
    Wenn Du das Prozedere verstanden hast, solltest Du mit Deinem Projekt keine Schwierigkeiten mehr haben.
    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!
    @Radinator meint folgendes:

    VB.NET-Quellcode

    1. Imports System.Runtime.InteropServices
    2. Imports System.Text
    3. Module Module1
    4. Sub Main()
    5. Debug.Assert(LouTranslator.Translate("Wochenende") = "Wo4ccde")
    6. End Sub
    7. End Module
    8. Class LouTranslator
    9. Public Shared Function Translate(input As String) As String
    10. If String.IsNullOrEmpty(input) Then
    11. Throw New ArgumentNullException("input")
    12. End If
    13. Dim buffer As New StringBuilder ' Auto-resize
    14. Dim refLength As Int32 = input.Length + 32 ' size heuristic
    15. NativeMethods.lou_translateString("de-de-g2.ctb", input, input.Length, buffer, refLength, Nothing, Nothing, 0)
    16. ' Limit stringBuilder size in case of implicit memory reallocation
    17. buffer.Length = refLength
    18. Return buffer.ToString()
    19. End Function
    20. End Class
    21. Class NativeMethods
    22. 'int EXPORT_CALL lou_translateString
    23. '(const char *tableList,
    24. ' const widechar *inbuf,
    25. ' int *inlen,
    26. ' widechar * outbuf,
    27. ' int *outlen,
    28. ' char *typeform,
    29. ' char *spacing,
    30. ' int mode);
    31. <DllImport("liblouis-2.dll")>
    32. Public Shared Function lou_translateString(
    33. <MarshalAs(UnmanagedType.LPStr)> tableList As String,
    34. <MarshalAs(UnmanagedType.LPWStr)> inbuf As String,
    35. ByRef inlen As Int32,
    36. <MarshalAs(UnmanagedType.LPWStr), Out()> outbuf As StringBuilder,
    37. ByRef outlen As Int32,
    38. <MarshalAs(UnmanagedType.LPStr)> typeform As String,
    39. <MarshalAs(UnmanagedType.LPStr)> spacing As String,
    40. mode As Int32) As Int32
    41. End Function
    42. End Class
    Gruß
    hal2000
    Vielen Dank. Ich werde es gleich Morgen früh testen.

    Verzeiht mir die folgende Frage, aber wo schreibe ich die Klassen hin? In ein Modul oder in die Form?
    Den Modul-Bereich schreibe ich natürlich in mein globales Modul. Ich habe schon für mein Programm eins, in dem, wie schon geschrieben, globale Variablen deklariert werden.