DLLs in Perseus schreiben und in AutoIt nutzen

  • Das Tutorial wird mal kurz und schmerzlos ^^ .

    Prinzipiell schreiben wir eine DLL in Perseus und rufen sie in AutoIt auf.

    1 - Perseus DLL schreiben

    Dazu zunächst das aktuelle Build herunterladen, Notepad++ öffnen und eine neue Datei namens Test.p++ anlegen. Jetzt wollen wir dem Compiler mitteilen, dass wir eine DLL schreiben wollen. Dazu kommt in die erste Zeile folgendes (warum siehe Wiki):

    Code
    application PE GUI DLL;

    Dann überlegen wir mal, was die DLL überhaupt machen soll. Rein aus Jux zeichnet die Funktion in diesem Tutorial eine Sinuskurve in einer beliebigen Farbe. Dazu werden vom aufrufenden Skript Device Context, Breite, Höhe und Farbe übergeben. In Perseus kann jede DLL alles, was auch in GUI oder CUI Anwendungen möglich ist. Wir können hier die gesamte WinAPI und natürlich alle Perseus-Includes nutzen. Wir könnten sogar Objekte und Klassen in die DLL packen, aber das brauchen wir hier ganz sicher nicht.

    Was wir aber brauchen sind die WinAPI und diverse Funktionen zur Datentyp-Konvertierung und zum Rechnen. So können wir Floats und Ints nutzen ohne weitere Variablen zu verwenden:

    Code
    include "Windows.inc", "Conversion.inc", "Math.inc";

    Dann deklarieren wir alle DLL-eigenen Variablen, in diesem Fall sind das zwei Puffer für die Koordinaten und ein Loopcounter. Könnten wir auch local machen, brächte aber nichts:

    Code
    dword ptX, ptY, i;

    Jetzt kommen wir zur Hauptfunktion. Im Gegensatz zu PEs brauchen DLLs keinen Einsprungpunkt. Eine Basis-Adresse wird automatisch vom Compiler angelegt. Die Funktion nimmt vier Parameter auf (alles dwords) und gibt provisorisch "1" zurück (auch als dword). Der Header sieht dann so aus:

    Code
    export DrawSine(dword hdc, dword cxClient, dword cyClient, dword crColor) as dword;

    Jetzt rücken wir ein und schreiben den Code. Das ist einfache Mathematik und bedarf nicht viel Erklärung. Am Ende der Funktion steht wie immer ein end:

    Damit ist der DLL Code schon fertig. Wir schmeißen ihm einfach den DC einer AutoIt GUI vor die Füße und den ganzen Rest übernimmt die DLL.

    2 - AutoIt Test Code

    GUI erstellen, DC auslesen, DLL aufrufen und das wars schon. Bei mir dauert das komplette neuzeichnen etwa 3.6ms (~280 FPS). Durch spielen mit den Variablen und durch besseren Perseus-Code kann man da noch ne Menge Speed rausholen, aber das ist ja nur eine Demonstration. Hier ist der AutoIt Code:

    [autoit]

    #include <WinAPI.au3>

    [/autoit][autoit][/autoit][autoit]

    ; Standard BlaBla
    $hGUI = GUICreate("Perseus Dll Test")
    $hDC = _WinAPI_GetDC($hGUI)
    GUISetBkColor(0xFFFFFF)
    GUISetState()

    [/autoit][autoit][/autoit][autoit]

    ; 1000x testen und durschn. Zeit ermitteln
    $iTimer = TimerInit()
    For $i = 1 To 10^3
    ; Dll wie jede andere DLL aufrufen
    DllCall("Test.dll", "dword", "DrawSine", "dword", $hDC, "dword", 400, "dword", 400, "dword", 0x00A088FF)
    Next
    ConsoleWrite("-> Avg. time: " & Round(TimerDiff($iTimer) / 10^3, 2) & " ms." & @LF)

    [/autoit][autoit][/autoit][autoit]

    While GUIGetMsg()<>-3
    WEnd

    [/autoit][autoit][/autoit][autoit]

    Exit _WinAPI_ReleaseDC($hGUI, $hDC) - 1

    [/autoit]

    3 - Schon fertig

    Ich hoffe es war anschaulich genug. Die Source-Files hänge ich an (auch die kompilierte DLL). Sicher lässt sich das für andere Zwecke sinnvoll einsetzen (Multithreading ist extrem einfach in Perseus. Damit lassen sich komplexe Routinen schön auslagern ^^ ). Vielleicht schreibe ich mal ein Mandelbrot-Beispiel o.ä.

  • Hi,
    habs nicht ausprobiert, aber die dll angeschaut :D
    Ggf. könnest du noch den Compiler "optimieren"

    Das 2-Zeilen LEA-Konstrukt passt natürich in ein MOV, allerdings weiss ich, woher das kommt und welche Idee dahinter steckt, daher für die hervorragende Arbeit 5 Sternchen und noch eins extra für das Grinsen in meinem Gesicht infolge

    Code
    .code:004027DD                 mov     dword_401020, eax
    .code:004027E2                 push    dword_401020
    .code:004027E8                jmp     $+5
    .code:004027ED                 leave
    .code:004027EE                 retn    4


    //EDIT (ich wollte es doch mal ausprobieren....)
    Beim compilieren über NP++Exec wird folgender Fehler geworfen:

    Spoiler anzeigen


    //EDIT2
    ich meine irgendwo gelesen zu haben, dass Perseus5 nur noch unter Win8.1 läuft....habe Win7-64, liegt das daran?

  • Getestet sind Windows 8 und Windows 7 64 Bit. Der Fehler, der bei dir auftritt sieht noch unregistrierter DLL aus. Hast du die prsco.dll per regsvr32 registriert?

  • Das dll-Compiler-Script wird jetzt gestartet, greift allerdings auf eine Exe im Verzeichnis C:\PerseusDEV\ zu...diese Exe existiert dort logischerweise nicht^^
    Wo finde ich diese Datei?
    Btw. habe ich die Verzeichnisse angepasst im Execute-Fenster, daher nur aus dem schütteren Gedächtnis..

    Das Mandelbrot ist schon in der Warteschleife 8o

  • C:\PerseusDEV\

    Keine Ahnung wo du das herzauberst. Einfach weder das NPP Skript noch sonstwas anfassen :D , dann müsste es eigentlich gehen. Optimalerweise wird das ganze in C:\minxtech\Perseus entpackt, aber das ist auch nicht zwingend.

    Zitat

    Btw. habe ich die Verzeichnisse angepasst im Execute-Fenster, daher nur aus dem schütteren Gedächtnis..

    Warum nur das? Dort wird der Pfad doch automatisch bestimmt. Da gibts nichts anzupassen. pmake kümmert sich um den Rest.

    // Nachdem zwei Leute unabhängig voneinander gesagt haben es liefe auf Windows 7 habe ich dem eigentlich vertraut. Wenn dann frag mal PainTain o.ä.

    Im besten Fall folgendes machen:

    • Alles löschen
    • 5.14.7.4 herunterladen
    • entpacken
    • DLL registrieren
    • pmake Admin-Rechte gewähren
    • Test.p++ anlegen und per F5 (nicht über irgendein Menü, sondern nur über F5) kompilieren


    Egal wo Test.p++ liegt, dort wird jetzt auch Test.exe bzw. Test.dll erzeugt. Das Aufräumen und kopieren übernimmt pmake ;)

    Einmal editiert, zuletzt von minx (10. August 2014 um 14:15)

  • Die Dll wird nun erstellt, allerdings gibt es ein Problem im AutoIt-code.
    Bei ca. 19 von 20 Starts des Scriptes wirft der DllCall() eine Fehlermeldung ( >Error code: 1 ) irgendwann klappt der Call und man sieht die Kurve im Fenster. Sehr seltsam...

  • Das ist mir auch schon aufgefallen. Ich habe aber keine Ahnung woran das liegt. Selbst mit einem Debugger scheint alles zu gehen, aber AutoIt steigt in Eigenregie aus. Seltsam indeed...

  • Bei mir gibts schon einen Error wenn die dll per DllOpen() geladen werden soll.
    Ich vermute, dass die Dll nur dann geladen wird, wenn zufällig der richtige "Zustand" erreicht wurde.
    Daher auch meine Verwunderung bzgl. dem jmp $+5 , das ist ein Sprung in deine Funktionen-Adressliste?!
    Ich habe mal eine Minimal-dll gebaut und mit Perseus compiliert.

    Code
    application PE GUI DLL;
    export DrawSine( dword cxClient) as dword;
    	return(cxClient);
    end;


    Da du auch FASM benutzt, habe ich mal eine Minimal-DLL per FASM erstellt, die funzt einwandfrei, ich lasse aber auch GetModuleHandleA (probier mal GetModuleHandleW) immer weg. Bei der Gelegenheit habe ich auch gesehen, dass du die Sections kleiner machst.
    Zeig doch mal den Code, welchen du dem FASM vorwirfst.

    //EDIT Schon im DllEntryPoint() zeigt IDA einen Stackpointer-Fehler beim DllEntryPoint endp! Da stimmt was nicht :D

    ciao
    Andy


    "Schlechtes Benehmen halten die Leute doch nur deswegen für eine Art Vorrecht, weil keiner ihnen aufs Maul haut." Klaus Kinski
    "Hint: Write comments after each line. So you can (better) see what your program does and what it not does. And we can see what you're thinking what your program does and we can point to the missunderstandings." A-Jay

    Wie man Fragen richtig stellt... Tutorial: Wie man Script-Fehler findet und beseitigt...X-Y-Problem

    2 Mal editiert, zuletzt von Andy (10. August 2014 um 18:57)

  • Da du auch FASM benutzt, habe ich mal eine Minimal-DLL per FASM erstellt, die funzt einwandfrei, ich lasse aber auch GetModuleHandleA (probier mal GetModuleHandleW) immer weg. Bei der Gelegenheit habe ich auch gesehen, dass du die Sections kleiner machst.
    Zeig doch mal den Code, welchen du dem FASM vorwirfst.

    Ich benutze nicht FASM :huh: . Perseus wird über das DirectByte Framework direkt in Bytecode übersetzt, ohne Assembler.

    //EDIT Schon im DllEntryPoint() zeigt IDA einen Stackpointer-Fehler beim DllEntryPoint endp! Da stimmt was nicht

    Da schaue ich nochmal nach. Ich mach mal ein Bug Ticket im Tracker dafür.

  • //EDIT
    Habs gefunden, der Code von Perseus (funzt nicht)

    Code
    .code:00402000                 push    0               ; lpModuleName
    .code:00402002                 call    ds:GetModuleHandleA
    .code:00402008                 mov     dword_401000, eax
    .code:0040200D                 push    ebp
    .code:0040200E                 mov     ebp, esp
    .code:00402010                 mov     eax, 1
    .code:00402015                 leave
    .code:00402016                 retn    0Ch


    der hier schon....

    Code
    .text:00401000                 push    ebp
    .text:00401001                 mov     ebp, esp
    .text:00401003                 push    0               ; lpModuleName
    .text:00401005                 call   ds:GetModuleHandleA
    .text:0040100A                 mov     eax, 1
    .text:0040100F                 leave
    .text:00401010                 retn    0Ch

    doch Stack....

    ciao
    Andy


    "Schlechtes Benehmen halten die Leute doch nur deswegen für eine Art Vorrecht, weil keiner ihnen aufs Maul haut." Klaus Kinski
    "Hint: Write comments after each line. So you can (better) see what your program does and what it not does. And we can see what you're thinking what your program does and we can point to the missunderstandings." A-Jay

    Wie man Fragen richtig stellt... Tutorial: Wie man Script-Fehler findet und beseitigt...X-Y-Problem

    3 Mal editiert, zuletzt von Andy (10. August 2014 um 21:44)

  • Hi,
    tut sich etwas?

  • Hi minx,

    hat sich bzgl. Dll schon etwas getan, bzw. woher kommt, dass

    push ebp
    mov ebp, esp

    entgegen allen Konventionen irgendwo im Code geschrieben, und damit der Stack durcheinandergewürfelt wird?