--->also available in English.

Assembler Win32 Tutorial Kapitel 0,5

Für diesen Kurs braucht man nur einen Assembler und Win95/98/ME/NT/2000/XP. Ahnung von ASM natürlich auch. Die Sources liegen bei (zip). Da mir keine Einleitung zu diesem Tutorial eingefallen ist, fang ich gleich mit dem Inhalt an:

1. Code unter Win32

Wer schon mal mit DPMI gecodet hat sollte keine Probleme damit haben, seinen Code auf Win32 zu portieren: Win32 Programme laufen im 32-Bit Protected Mode mit einem FLAT Speichermodell.
Das heißt:
Was Win32-Coding sooo saumäßig geil macht:

2. Was man braucht:

3.1 Los gehts:

Zuerst sagt man dem Assembler, das wir 32Bit-Code haben wollen:
Für TASM/MASM also:

                .386
                .model flat
Bei NASM wird dies bei den Segmenten angegeben (s.u.).

Die Windows-Funktionen benützen eine Menge von Konstanten, die als Text im SDK beschrieben sind. Sie sind in windows.inc oder win32n.inc definiert, also ab damit in den Code:

      %include "win32n.inc"  (NASM)
      include windows.inc    (TASM/MASM)
Die Windows-Funktionen stecken in DLLs. Sie werden über NEAR-Calls angesprochen (nicht über Interrupts) und sind demzufolge EXTERN. MASM benennt die Funktionen anders als TASM und NASM. Folgender Code löst das Problem: (TASM/MASM)
        IFDEF masm
         ExitProcess equ _ExitProcess@4
        ENDIF
Dies muß für jede Funktion, die in MASM aufgerufen wird, eingebaut werden.

Jetzt die Funktion reinsetzen:
        extrn ExitProcess : near
NASM braucht dazu noch ne Sondereinladung, bei der die DLL mitangegeben werden muß:
        IMPORT ExitProcess kernel32.dll

So, Zeit, die Daten einzubauen. Zwar könnte alles, Code und Daten, in ein Segment passen, aber wenn man die beiden trennt braucht man nur das passende Segment anzugeben um Code und Daten im Code mixen. Der Linker sortierts wieder auseinander:

                .data                (TASM/MASM)
                segment .data USE32  (NASM)
Die Daten selbst fehlen noch:
		ErrorCode dd 0
Analog das Codesegment:
		.code
bzw.
             segment .code USE32  (NASM)

Hier fängt das Programm an. Der Eintrittspunkt muß nicht immer main bzw. WinMain heißen, in ASM kann man ihn durchaus auch _DeiMutterSeiGsicht nennen. _Anfang tut es aber auch:

               _anfang:
Und für NASM:
              ..start
Wahnsinn, jetzt sind wir mitten in einem laufenden Win32-Programm! Bevor sich jetzt jemand vor Aufregung in die Hose macht, mach ich lieber Schluß für heute. Aber Moment mal... Eines kommt noch: JA, die Funktion zum Beenden des Programmes muß noch aufgerufen werden.

3.2 Funktionen unter Win32

Alle API-Funktionen mit konstanter Anzahl der Parameter (ich kenn nur eine Ausnahme) werden nach der stdcall-Konvention aufgerufen. Das heißt, die Parameter werden von rechts nach links auf den Stack gePUSHt und dann die Funktion aufgerufen. Die Funktion räumt den Stack für uns auf und liefert das Ergebnis im Register EAX zurück.

Die Parameter werden generell als DWORD übergeben.

So schauts aus:

                push ErrorCode
                call ExitProcess
Das Programm ist fertig, man braucht es nur umzusetzen.

4. Compilen und Linken

Folgende Einstellungen sind essentiell: Für die genaue Kommandozeile im zip nachschauen.

5. He, das Programm kann ja gar nix!

Stimmt. Deshalb habe ich die fertige Datei auch um einen Windows-Standarddialog erweitert. Man könnte dort den Rückgabewert abfragen und z.B. beim falschen Knopf die Funktion erneut aufrufen...

6. Weitere Themen

6.1 Ressourcen

Als Ressourcen bezeichnet man Daten, die der Programmdatei hinzugelinkt worden sind. Alle Daten sind dabei möglich. Sinnvoll ist dies vor allem um dem Programm Icons hinzuzufügen, Menüs zu bauen, verschiedene Sprachen zu verwenden (Windows lädt dann die passende!)...

Um sie zu erzeugen braucht man ein Ressource Skript (*.RC). Dieses beschreibt alle einzubindenden Ressourcen. Man kann es mit einem Texteditor oder speziellen Ressource-Editoren erzeugen. Dieses wird dann mit den Daten kompiliert zu einer *.res oder *.obj - Datei, die dann dem Linker übergeben wird. Ein Ressource-Compiler findet sich samt Icon ebenfalls im Archiv zum Tutorial.

6.2 Funktionen

Um die Win32-Funktionen zu nützen muß man dem Programm die passenden *.lib - Dateien hinzufügen. Während TASM alle Funktionen in die import32.lib packt, besitzt MASM für jede DLL eine eigene LIB. Soll heißen, das man bei MASM immer schauen muß, in welcher DLL die Funktion steckt.

Strings werden übrigens stets als Pointer übergeben, sie sind entweder nullterminiert oder ihre Länge ist ein separater Parameter. Die Register EBX,ESI,EDI und EBP bleiben unverändert, Flag DF muß vor dem Call gelöscht sein.

Alle Funktionen mit Strings gibt es in zwei Varianten: ANSI und UNICODE. Win95 beschränkt sich überwiegend auf ANSI. Die SDK-Funktion CreateFile gibt es also in Wirklickeit doppelt: In ANSI als CreateFileA und als CreateFileW in UNICODE. In Assembler muß der genaue Typ angegeben werden.

6.3. Funktionen in DLLs deren LIBs man nicht hat

Kein Problem: Hat man keine Lib, macht man sich eine.

Bei TASM verwendet man zuerst IMPDEF, um eine *.def - Datei aus der DLL zu ziehen und verwendet die *.def dann mit IMPLIB, um die *.lib zu erzeugen.

Bei MASM: DUMPBIN /exports auf die DLL, in der *.def alles außer den Zeilen mit den Funktionen rauswerfen, die Funktionen nach dem Muster
Funktionsname  @Parameterzahl_in_Bytes
verzieren, am Dateikopf noch
 LIBRARY  dllname
 EXPORTS 
dazufügen und die *.def dann auf LIB /def loslassen. Achtung: LIB.EXE fügt allen Funktionen einen Unterstrich _ hinzu!

Bei NASM muß man entweder in die DOCs des Linkers schauen oder die IMPORT-Direktive im Quellcode verwenden.

6.4 Wie funktionieren die LIBs?

Die LIBs enthalten Informationen, welche DLLs und Funktionen verwendet werden. Windows ladet diese und fügt deren Adressen in die Stellen ein, die in unserem Code durch die LIBs drinstehen. Wenn wir eine Funktion aufrufen, geht der CALL zuerst in die LIB, dort steht dann ein JMP mit der passenden Adresse. Unter MASM (und z.T. NASM) sind die Sprungziele auch als LABEL verfügbar, man kann dann z.B.
 call ExitProcess@4
durch
 call [__imp__ExitProcess@4]
ersetzen und spart sich dadurch den JMP-Befehl.

7. Uuuuuuargs, mir dröhnt die Birne... ich dachte, des soll einfach sein.

Tja, alles eine Frage der Gewöhnung - die ersten Versuche mit dem Programmieren waren meist auch nicht einfach, oder?

Ist aber halb so wild: Der Quellcode im asmtut0.zip dürfte einfach zu verstehen sein, mit Hilfe des SDKs kann man schon mal etwas damit rumprobieren.

War auch viel Theorie, wichtig ist vorerst, das man den Compiler/Linker in Gang bekommt und einen Überblick erhält.

Im nächsten Teil gehts zur Sache: Fenster, Message/Input-Handling,...
Mail an den Autor: webmeister@deinmeister.de

Hauptseite Programmieren Win32Asm Downloads Software Hardware Cartoons+Co Texte Sitemap