String aus einer DLLCallback Funktion als Rückgabewert senden

  • Hallo zusammen,

    Ich habe mir, zur Übung, eine kleine UDF für die Skriptsprache Wren geschrieben. Dabei bin ich auf folgendes Problem gestoßen:

    Um Module in die VM von Wren zu laden werden einige Callback Funktionen verwendet, welche zur Initialisierung der VM mit gegeben werden müssen. Das funktioniert soweit und meine AutoIt-Funktionen werden sauber gestartet.

    Bei dem Versuch ein Modul in Wren zu importieren (`import "win32" for Registry` als Beispiel) werden 2 Callback-Funktionen aufgerufen, die erste um den Pfad aufzulösen. die zweite zum eigentlichen Laden.

    Die erste Funktion (`resolveModuleFn`) hat folgende Signatur:

    Code
    typedef const char* (*WrenResolveModuleFn)(WrenVM* vm, const char* importer, const char* name);

    Also gehe ich davon aus das ich meine AutoIt-Funktion wie folgt erstelle:

    Code
    Func _ResolveModuleFnCallback($pVM, $sImporter, $sModule)
        ; do stuff
        Return "some-resolved-module"
    EndFunc
    
    Local $ResolveModuleFnHandle = DllCallbackRegister("_ResolveModuleFnCallback", "str", "ptr;str;str")

    Wie gesagt, das funktioniert auch alles. Die Callback-Funktion wird aufgerufen und tut ihr Arbeit.

    Jedoch wirft mir die Wren-VM danach einen Fehler, dass das besagt Modul nicht gefunden werden konnte. Laut dem C-Code wäre der Rückgabewert NULL.

    Mache ich hier irgendetwas falsch oder sollte ich eher in dem C-Code von Wren nach einem Fehler suchen?


    Im Anhang findet Ihr das Repo von Wren im "main"-Branch.

    Die DLL sowie der AutoIt-Code sind im Ordner "\lib" zu finden. Kompiliert habe ich das ganze mit VS2022, falls Ihr die DLL lieber selbst erstellen wollt :)

    P.S.: ich nutze AutoIt-x64, falls das Skript "test_x64.au3" nicht laufen sollte.

    Gruß

  • Mal völlig ungetestet (falls folgender Ansatz nicht geht schau ich es mir bisschen intensiver an):

    AutoIt
    Func _ResolveModuleFnCallback($pVM, $sImporter, $sModule)
        ; do stuff
    
        Local $sRet = "some-resolved-module"
        Local $tRet = DllStructCreate("char ret[" & StringLen($sRet) + 1 & "]")
        $tRet.ret = $sRet
        Return DllStructGetPtr($tRet)
    EndFunc
    
    Local $ResolveModuleFnHandle = DllCallbackRegister("_ResolveModuleFnCallback", "ptr", "ptr;str;str")
  • Moin,

    mit deisem Code bricht die DLL scheinbar weg, AutoIt beendet sich mit Errorcode -1073740940.

    Ich habe mehrere Versuche getestet:

    - DllStructGetPtr($tRet) --> Fehler

    - DllStructGetPtr($tRet, 1) --> Fehler

    Danach nochmal mit der ersten Methode, was funktioniert. Liegt das evtl. an dem Speicher den AutoIt fuer den String freimacht? Das ggf. die DLL etwas schneller war als AutoIt beim aufräumen?

    Die Methode hatte ich schon versucht, jedoch den Struct NUR mit StringLen($sRet) definiert, also zu kurz.

  • Liegt das evtl. an dem Speicher den AutoIt fuer den String freimacht?

    Sehr guter Hinweis!

    AutoIT gibt den Pointer zurück, gibt aber direkt darauf den Speicherbereich dafür frei.
    Persisten Speicher könnte man so hinbekommen.
    Da müsste man sich allerdings selbst noch um die Bereinigung kümmern - aber das heben wir uns für später auf:

    Die Methode hatte ich schon versucht, jedoch den Struct NUR mit StringLen($sRet) definiert, also zu kurz.

    Ich war mir nicht mehr sicher ob das abschließende Null mit beachtet werden muss oder nicht.
    Könnte auch ohne die +1 funktionieren.

  • So,

    das Ergebnis als Pointer zurück zu geben scheint der richtige Weg zu sein, zumindest frisst er das problemlos. Ob das jetzt 0-terminiert sein muss oder nicht, da habe ich keinen Unterschied festgestellt.

    Jedoch ergibt sich dadurch jetzt ein weiteres Problem: Nach der resolveModuleFn-Funktion nimmt eine loadModuleFn-Funktion das Ergebnis entgegen.

    Diese Funktion habe ich wie folgt als Callback registriert:

    Code
    $loadModuleFnHandle =    DllCallbackRegister("_LoadModuleFnCallback",    "ptr",  "ptr;str")

    In der Funktion selbst mache ich nur ein ConsoleWrite und gebe Null zurück.

    Bei der Ausführung kommen nur kryptische Zeichen raus.

    Wenn ich den zweiten Parameter auf "ptr" setze sieht es zumindest richtig aus, jedoch weiß ich nicht wie ich an die richtigen Daten komme.

    Versucht habe ich bereits den "Pointer" zu nutzen um mir wieder ein Struct damit zu befüllen, dabei kommt aber nur das gleiche wie bei dem ersten Versuch raus.

    Allerdings fällt mir auf das AutoIt immer mit einem Fehler abzustürzt, normalerweile würde ich von der WrenVM eine brauchbare Fehlermeldung bekommen. Daher vermute ich mal das ich etwas bei den Pointer/Speicher Dingen falsch mache.


    Das ist der Code der beiden Funktionen in Wren die für resolve/load genutzt werden:

    Hilft vermutlich auch nicht wirklich, da dort recht viele Makros genutzt werden mit denen ich nicht viel anfangen kann.

    Value von der Funktion resolveModule ist als bsp. ein `uint64_t`, vermutlich genutzt als Adresse für einen Pointer, aber da komme ich leider nicht ganz mit.

    In C/C++ wäre das bestimmt viel einfacher mit dem Speicher :)