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:


    #include <WinAPI.au3>


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


    ; 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)


    While GUIGetMsg()<>-3
    WEnd


    Exit _WinAPI_ReleaseDC($hGUI, $hDC) - 1


    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:


    //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 ;-)

  • 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

  • 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....

  • 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?