| Vorheriges Kapitel (Kurzer geschichtlicher Überblick) | Nächstes Kapitel (Ermittlung der Windows-Version) |
Der Windows-Kern kennt drei Formen: Win16, Win32 und Win32s.
In diesem Tutorial wird ausschließlich die Win32-Programmierung behandelt.
Alle Windows-Versionen, die auf dem 32-Bit-Kern basieren, laufen im Protected
Mode.
Win32 kennt nur noch ein einziges Speichermodell: FLAT. Das bedeutet, das jedem
Programm die virtuelle Speichermenge von 4 GB als ein Segment zur Verfügung
gestellt wird. In diesem einen Segment befinden sich sowohl Code als auch Daten
des Programms.
Die Adressierung erfolgt über die 32-Bit-Register des Prozessors. Adressangaben sind dann in aller Regel Offsets zum Beginn des 4-GB-Segments. Mit einem 32-Bit-Zeiger/Register kann jedes Byte innerhalb des Segments adressiert werden. Dadurch entfällt das Jonglieren mit Segmenten. Die Segmentregister sind jedoch noch nicht sinnlos. In ihnen befinden sich sogenannte Selektoren, die auf Deskriptoren verweisen. In einem Deskriptor wird, wie der Name schon andeutet, ein Segment beschrieben. Dabei geht es um die Anfangsadresse im physikalischen Speicher, Größe des Segments und gewisse Zugriffsrechte, die als Speicherschutzmechanismen dienen sowie einige andere Daten.
Die Kommunikation mit dem Betriebssystem erfolgt nicht wie unter DOS über Interrupts, sondern über Funktionen, die vom Windows-API (Application Programming Interface) zur Verfügung gestellt werden.
Für die Funktionen gibt es abgesehen von einer Ausnahme nur noch eine
verbindliche Aufrufkonvention: STDCALL.
Dies ist eine Mischform der Aufrufkonventionen C und Pascal. Bei der C-
Aufrufkonvention werden die Parameter von rechts nach links auf den Stack gelegt
und der Aufrufer muss den Stack bereinigen. Bei Pascal ist es genau umgekehrt:
die Parameter von links nach rechts auf den Stack und die Prozedur bereinigt den
Stack. Bei STDCALL werden die Parameter von rechts nach links auf den Stack
gelegt, aber die aufgerufene Funktion ist für die Stackbereinigung
verantwortlich. Die einzige Funktion, bei der die C-Aufrufkonvention verwendet
werden muss, heißt wsprintf (wsprintf() kann eine variable Parameteranzahl
verarbeiten, weiß aber nicht im Voraus wieviele es sein werden und kann
daher auch nicht die Stackbereinigung vornehmen).
Bei der Programmierung in Windows ist zu beachten, dass Windows intern die Register EBX, EDI, ESI und EBP verwendet. Sie können diese Register in Ihren Prozeduren verwenden, sollten aber darauf achten, dass die Inhalte dieser Register nach dem Funktionsaufruf die gleichen wie vor dem Funktionsaufruf sind.
Ein mögliches Rahmenprogramm könnte so aussehen:
.386 .MODEL FLAT, STDCALL .DATA .DATA? .CONST .CODE label END label |
Auch wenn es keine verschiedenen Segmente mehr gibt, so kann das Programm doch
in logische Abschnitte unterteilt werden.
Im DATA-Abschnitt werden die initialisierten Daten abgelegt, im DATA? die
uninitialisierten Daten. Der Unterschied bei diesen beiden Abschnitten besteht
darin, dass durch Datendefinitionen im DATA?-Abschnitt die EXE-Datei nicht
vergrößert.
Die Anweisung msg db 10000 dup (?) sorgt
dann dafür, dass zur Laufzeit diese Speichermenge zur Verfügung
gestellt wird, macht die EXE-Datei aber nicht um 10000 Byte
größer.
Bei Windows handelt es sich um ein objektorientiertes Betriebssystem. Es wurde allerdings nur begrenzt mit objektorientierten Programmiersprachen entwickelt. Der Großteil von Windows wurde in C geschrieben, in den systemnahen und zeitkritischen Bereichen (Interruptbehandlung, Taskmanagement) wurde auch ein wenig mit Assembler gearbeitet. Die COM-Objekte (OCX), die teilweise durch Windows, teilweise durch Anwendungsprogramme bereitgestellt werden, sind mit hoher Wahrscheinlichkeit durch OOP erzeugt worden.
Jedes Fenster, jeder Button und jedes andere optisch greifbare Element ist ein Objekt. Dabei sind im Objekt Fenster eine Reihe weiterer Objekte enthalten. Jedes Objekt das andere Objekte beinhaltet ist für diese Objekte das Elternobjekt (Parent). Die enthaltenen Objekte werden als Kindobjekte (Child-Objekt) bezeichnet.
Der Objektbegriff zieht sich auch durch die nicht sichtbaren Bereiche des Systems. So gibt es Dateiobjekte, Prozessobjekte, Speicherobjekte usw.
Bei der Windows-Programmierung muss zwischen Anwendungen im Nutzermodus und Anwendungen im Kernelmodus unterschieden werden. Programme, die im Kernelmodus laufen haben vollen Zugriff sowohl auf die Hardware als auch auf die Kerneldatenstrukturen und Kernelfunktionen. Beispiele für Programme im Kernelmodus sind Gerätetreiber. Durch die Systemnähe fallen hier Programmierfehler besonders schwer ins Gewicht, weil sie das gesamte System zum Absturz bringen können.
In diesem Tutorial wird es nur um die Entwicklung von Nutzermodusanwendungen gehen. Programmfehler werden hier insofern weniger übel genommen als sie i. d. R. nur die jeweilige Anwendung abstürzen lassen, aber nicht das gesamte System mit in den Abgrund reißen. Das soll natürlich nicht andeuten, dass man bei Anwendungsprogrammen weniger Sorgfalt walten lassen kann.
Für Kernelmodusanwendungen gibt es von Microsoft das DDK (Driver Development Kit) kostenfrei zum Download. Dieses enthält Dokumentationen, Header-Dateien (für die Programmiersprache C) und Beispiele zur Treiberentwicklung.
Wie schon weiter oben gesagt kommuniziert man unter Win32 über Funktionsaufrufe. Die wichtigsten dieser Funktionen für Programme im Nutzermodus befinden sich in den Dateien user32.dll (Objektbereitstellung zur Interaktion mit dem Nutzer), kernel32.dll (Prozess- und Speichermanagement) und gdi32.dll (Graphic Device Interface, Interaktion mit Ein/Ausgabegeräten [Tastatur, Bildschirm, Drucker]), die von Windows bereitgestellt werden. Diese Dateien stellen die grundsätzliche Schnittstelle zu Windows zur Verfügung. Multimedia, Netzwerkzugriffe, spezielle grafische Objekte und Funktionen für Gerätetreiber werden durch andere DLLs oder OCXe bereitgestellt.
Durch das GDI wird für jedes grafische Objekt ein Device Context bereitgestellt. Über diesen Device Context kann auf die grafischen Eigenschaften (Farbe, Font etc.) des Objekts zugegriffen werden. Jedes Objekt, grafisch oder nicht-grafisch, wird über ein Handle eindeutig identifiziert.
Bei der Verwendung der API-Funktionen kommen jede Menge Strukturen und Konstanten zum Einsatz. Bei MASM32 werden diese Angaben hauptsächlich in der Datei windows.inc hinterlegt. Diese Datei unterliegt einer ständigen Pflege und Erweiterung durch die beiden Hauptautoren von MASM32.
Desweiteren sollte eingestellt werden, dass der Quelltext case-sensitiv
kompiliert wird (Beachtung der Groß/Kleinschreibung). In der
Standardeinstellung sind die beiden Bezeichner hwnd und HWND identisch. Unter
Windows ist HWND ein Datentyp (Handle eines Fensters). Oft wird die
Variable dieses Typs hwnd genannt, was bei nicht case-sensitiver
Interpretation zwangsweise zu Problemen führt. Dieses Vorgehen
ist an vielen anderen Stellen identisch.
Im Gegensatz zu DOS kommuniziert auch das Betriebssystem mit dem Programm. Diese Kommunikation läuft über sogenannte Callback-Funktionen. Diese Funktionen werden durch Sie geschrieben. Sie unterscheiden sich dabei grundsätzlich nicht von anderen selbstdefinierten Funktionen. Oft wird aber ein spezieller Returncode erwartet, der von Windows ausgewertet wird. Da Windows die Funktion u. U. mit Parametern aufruft, muss natürlich auch in der Funktionsdefinition die Korrektheit der Parameter sichergestellt sein.
Ein Beispiel für eine Callback-Funktion ist die in praktisch jedem Windows- Programm zu findende sogenannte Window- oder Fensterprozedur. Dabei sendet Windows an das Programm zu jedem auftauchenden Ereignis (Mausbewegung, Tastendruck, Menüpunktauswahl etc) in einer Endlosschleife eine Nachricht. Es gibt eine ganze Reihe von Nachrichten, die in windows.inc als Konstanten verfügbar sind. Im Abschnitt Ein Basisprogramm finden sie eine Anwendung mit Fensterprozedur.
In MASM32 wird sehr viel mit Funktionsprototypen gearbeitet. Gleichzeitig bietet
MASM32 mit der Anweisung invoke das notwendige Mittel, um einen
Funktionsaufruf gegen seinen Prototypen abzugleichen. Die Prototypendefinitionen
für die einzelnen DLL-Funktionen liegen in Include-Dateien, die vom
Dateinamen die Beziehung zur DLL erkennen lassen (kernel32.inc, gdi32.inc etc).
Diese Dateien müssen selbstverständlich vor Verwendung der
entsprechenden Funktion ins Programm eingebunden sein.
Auch wenn es in diesem Tutorial um MASM gehen soll, wird das erste Programm, eine einfache Message-Box auch in einer TASM-Variante vorgestellt.
TITLE Win32 MessageBox erzeugen
IDEAL
P386
MODEL FLAT,STDCALL
EXTRN MessageBoxA:PROC
EXTRN ExitProcess:PROC
DATASEG
cTitle db "Meine Messagebox",0
cMessage db "Klick mich bis ich verschwinde",0
CODESEG
start:
call MessageBoxA,0,offset cMessage,offset cTitle,67
call ExitProcess,0
END start
|
tasm32 /ml msgbox tlink32 /Tpe /aa /c msgbox.obj,msgbox,,import32
Der Schalter /ml teilt TASM32 mit, das alle Symbole case-sensitiv behandelt werden sollen. Der Aufruf von TLINK32 ist deutlich umfangreicher als unter DOS: mit /Tpe wird angegeben, dass eine .exe-Datei erzeugt werden soll, keine DLL. /aa bedeutet, dass eine echte Windows-Anwendung erzeugt werden soll. /c ist wieder für das case-sensitive Linken verantwortlich. Im Anschluss folgen noch die Angaben Objektdatei,EXE-Datei,MAP-Datei(hier leer) und die Import-Bibliothek.
Die Version für MASM sieht so aus:
TITLE Win32 MessageBox erzeugen
.386
.MODEL FLAT,STDCALL
option casemap:none
includelib c:\masm32\lib\user32.lib
includelib c:\masm32\lib\kernel32.lib
MessageBoxA PROTO :Dword, :Dword, :Dword, :Dword
ExitProcess PROTO :Dword
.DATA
cTitle db "Meine Messagebox",0
cMessage db "Klick mich bis ich verschwinde",0
.CODE
start:
invoke MessageBoxA,0,ADDR cMessage,ADDR cTitle,67
invoke ExitProcess,0
END start
|
ml /c /coff msgbox.asm link /Subsystem:windows msgbox
ML erzeugt normalerweise direkt aus der Quelldatei eine EXE-Datei. Bei Windows- Programmen sind allerdings noch andere Angaben notwendig. Mit dem Parameter /c wird ml.exe angewiesen, nur eine .obj-Datei zu erzeugen. Link.exe bekommt dann die Information, dass es sich um ein Programm für die Windows-Umgebung handelt.
Abgesehen von der MASM-eigenen Form der Direktiven fallen fünf Dinge ins
Auge.
Anders als bei TASM wird die Beachtung der Groß/Kleinschreibung nicht
durch Kommandozeilenparameter angegeben sondern mit der Direktive option
casemap:none (obwohl auch ein Kommandozeilenschalter bei ml.exe möglich
ist: /Cp).
Mit includelib werden die Importbibliotheken angegeben, die die beiden
Funktionen MessageBoxA (user32.lib) und ExitProcess (kernel32.lib)
enthalten.
Kurz danach werden die beiden Prototypen für die Funktionen erzeugt. Wie
Sie sehen, werden nur die Datentypen angegeben, keine konkreten
Variablennamen.
Der Aufruf der Funktionen erfolgt nicht mit CALL sondern über INVOKE. Dabei
hängen die Prototypendefinition und INVOKE unmittelbar zusammen. INVOKE
funktioniert nur, wenn die Prototypen vorhanden sind. Der Aufruf würde auch
mit CALL funktionieren, dann müssten aber die Parameter einzeln auf den
Stack gepusht werden und zweitens wäre die mit INVOKE mögliche
Parameterprüfung außer Kraft gesetzt.
Der fünfte Unterschied besteht in der Verwendung von ADDR statt OFFSET.
ADDR funktioniert nur im Zusammenhang mit INVOKE. Wenn Sie einer Variablen oder
einem Register die Adresse eines Labels zuweisen wollen dann müssen Sie
weiterhin OFFSET verwenden. ADDR kann ebenso nicht mit Vorwärtsreferenzen
umgehen, man kann also nicht die Adresse eines erst später definierten
Labels ermitteln. Dafür kann man ADDR mit lokalen Variablen z. B. in
Prozeduren verwenden. OFFSET würde hier den Offset zum Segmentanfang
ermitteln, ADDR den Offset innerhalb des Stacks.
INVOKE und ADDR sind MASM-spezifisch und ebenso wie PROTO nicht mit Prozessorbefehlen zu verwechseln. Der TASM kann auch im MASM-Modus nichts mit diesen Direktiven anfangen. Bei TASM können Sie die erweiterte Form des Befehls CALL verwenden, statt ADDR bietet sich OFFSET bzw. der Assemblerbefehl lea an. Zur Prototypendefinition können Sie auf PROCDESC ausweichen.
Vergessen Sie nicht, dass es sich bei Windows um ein System mit dem ANSI- Zeichensatz handelt. Sollten Sie ihre Programme weiterhin mit einem DOS-Editor bzw. im ASCII-Zeichensatz schreiben, so werden Sie bei Sonderzeichen wie bspw. den Umlauten zu einer fehlerhaften Zeichenanzeige kommen.
Gemessen am ersten Programm im Grundlagentutorial (Hello World) war dieses Programm nicht nur kürzer sondern erlaubte sogar noch einen gewissen Teil an Nutzerinteraktion, auf die freilich nur mit Beenden des Programms reagiert wurde. Trotzdem konnte die Messagebox sowohl mit der Maus als auch der Tastatur bedient werden, ohne auch nur eine Zeile zur Maus- und Tastatursteuerung zu schreiben.
Das übliche Windows-Programm verfügt über ein Hauptfenster mit einem Menü, einer Titelleiste mit einigen Schaltflächen sowie, ganz wichtig, einem Zweck.
Im Folgenden stelle ich Ihnen ein Programm vor, das durchaus als Basis für Ihre eigenen Entwicklungen dienen kann. Es wird ein einfaches Fenster mit der üblichen Systemschaltfläche links oben und den drei bekannten Standardschaltflächen zur Fenstergrößenänderung rechts oben erzeugt.
;fenster.asm .386 .MODEL flat,stdcall option casemap:none INCLUDE c:\masm32\include\windows.inc INCLUDE c:\masm32\include\user32.inc INCLUDE c:\masm32\include\kernel32.inc INCLUDELIB c:\masm32\lib\user32.lib INCLUDELIB c:\masm32\lib\kernel32.lib WndProc PROTO :dword, :dword, :dword, :dword .DATA szCaption db "Das erste Fenster",0 szClassName db "My_Class",0 .DATA? ;uninitialisierte Daten hInstance HINSTANCE ? hIcon HANDLE ? hCursor HANDLE ? hWnd HWND ? cCmdline LPSTR ? .CODE start: call mymain invoke ExitProcess,0 mymain PROC LOCAL wwd:dword,wht:dword,wtx:dword,wty:dword,wc:WNDCLASSEX invoke GetModuleHandle,NULL mov hInstance,eax invoke GetCommandLine mov cCmdline,eax mov wtx,CW_USEDEFAULT ;oder konkrete Angaben: 0 mov wty,CW_USEDEFAULT ;0 mov wwd,CW_USEDEFAULT ;400 mov wht,CW_USEDEFAULT ;400 mov wc.cbSize,SIZEOF WNDCLASSEX mov wc.style,CS_BYTEALIGNCLIENT or CS_BYTEALIGNWINDOW mov wc.lpfnWndProc,OFFSET WndProc mov wc.cbClsExtra,NULL mov wc.cbWndExtra,NULL push hInstance pop wc.hInstance mov wc.hbrBackground,COLOR_BTNFACE+1 mov wc.lpszMenuName,NULL mov wc.lpszClassName,OFFSET szClassName invoke LoadIcon,NULL,IDI_APPLICATION mov hIcon,eax mov wc.hIcon,eax mov wc.hIconSm,eax invoke LoadCursor,NULL,IDC_ARROW mov hCursor,eax mov wc.hCursor,eax invoke RegisterClassEx,ADDR wc invoke CreateWindowEx,WS_EX_LEFT,ADDR szClassName,ADDR szCaption, WS_OVERLAPPEDWINDOW,wtx,wty,wwd,wht,NULL,NULL,hInstance,NULL mov hWnd,eax invoke ShowWindow,hWnd,SW_SHOWNORMAL invoke UpdateWindow,hWnd call MsgLoop ret mymain ENDP MsgLoop PROC LOCAL msg:MSG Startloop: invoke GetMessage,ADDR msg,NULL,0,0 or eax,eax je Exitloop invoke DispatchMessage,ADDR msg invoke TranslateMessage,ADDR msg jmp Startloop Exitloop: mov eax,msg.wParam ret MsgLoop ENDP WndProc PROC hWin:dword,uMsg:dword,wParam:dword,lParam:dword .IF uMsg == WM_DESTROY invoke PostQuitMessage,NULL .ELSE invoke DefWindowProc,hWin,uMsg,wParam,lParam ret .ENDIF xor eax,eax ret WndProc ENDP END start |
Das normale DOS-Programm läuft linear in einer Schleife (z. B. warten auf Tastendruck und Tastenauswertung). Ein Windows-Programm muss/kann auf eine Vielzahl von Ereignissen reagieren: Mausbewegungen, Mausklicks, Tastatureingaben, Taskwechsel, Überdeckungen durch andere Programme. Die Information über solche Ereignisse wird über Nachrichten verschickt. Jedes Programm verfügt über eine Nachrichtenschleife (Message Loop). In diese Schleife werden von Windows alle Nachrichten eingereiht die das Programm betreffen. Innerhalb des Programms werden die Nachrichten aus der Schleife entnommen und darauf reagiert. Dabei muss man nur die Nachrichten entnehmen, die tatsächlich von Interesse sind. Nachrichten die man nicht selbst verarbeitet werden an eine Standardfunktion weitergereicht, die wiederum in standardisierter Form die Nachrichten abarbeitet.
Im obigen Programm ist die Nachrichtenschleife in der Prozedur MsgLoop enthalten. Nach der Initialisierung des Programms hält es sich nur noch innerhalb der Schleife auf. Mit GetMessage() wird die nächste wartende Nachricht aus der Schleife entnommen und in der Variablen msg gespeichert. Nur wenn der Rückgabewert von GetMessage() ungleich Null ist wird die Nachricht per DispatchMassage() an die für das betreffende Fenster zuständige Callback-Prozedur weitergeleitet.
Beim Aufruf der Funktion RegisterClassEx() wird die Adresse der Struktur wc übergeben. In dieser Struktur gibt es den Member lpfnWndProc, dem die Adresse der fensterweiten Callback-Prozedur zugewiesen wird. Im Beispiel heißt die Fensterprozedur WndProc. Diese Prozedur wird nicht durch Sie, sondern von Windows aufgerufen. In dieser Prozedur wird die Nachricht daraufhin überprüft, ob Sie für uns interessant ist. Hier interessiert uns nur die Nachricht die beim Schließen des Fensters gesendet wird.
Im Programm wird darauf reagiert indem die Funktion PostQuitMessage() aufgerufen wird. Diese Funktion wiederum sendet die Nachricht WM_QUIT an das Fenster. Durch diese Nachricht erhält GetMessage() einen Returnwert von Null, was zum Verlassen der Nachrichtenschleife führt. Die Nachricht WM_DESTROY können Sie auch selbst senden indem Sie die Funktion DestroyWindow() aufrufen.
Die Parameter der CreateWindowEx()-Funktion sowie die Struktur WNDCLASSEX werden in der Referenz näher beschrieben.
Hier noch ein kleines Beispiel, das beliebig zu einem Systeminformationstool ausgebaut werden kann.
TITLE Durch CPUID ermittelte Informationen ausgeben
.586
.MODEL flat, stdcall
OPTION casemap:none
INCLUDE \masm32\include\windows.inc
INCLUDE \masm32\include\kernel32.inc
INCLUDELIB \masm32\lib\kernel32.lib
INCLUDE \masm32\include\user32.inc
INCLUDELIB \masm32\lib\user32.lib
.DATA
cModel db 12 dup (?)
db 0Dh,0Ah
db "Anzahl Funktionen: "
cFunkt db ?,0
cCaption db "CPUID-Infos",0
.CODE
start:
mov eax,0 ;hoechste bekannte Funktionsnummer und
cpuid ;Identifikationsstring ermitteln
mov dword ptr cModel,ebx
mov dword ptr cModel + 4,edx
mov dword ptr cModel + 8,ecx
add al,30h ;in ein Zeichen umwandeln
mov cFunkt,al
invoke MessageBox, NULL, ADDR cModel, ADDR cCaption, MB_OK
invoke ExitProcess,0
END start
|
Die Anweisung .586 ist hier notwendig, da CPUID erst ab dieser Prozessorklasse bereit gestellt wird.
In den Beispielprogrammen und Codefragmenten sind ihnen wahrscheinlich eine Reihe unbekannter Bezeichner aufgefallen. Die API-Dokumentation gibt die Syntax von Funktionen, den Aufbau von Strukturen und Beispielprogramme in der Mehrzahl der Fälle in der Sprache C an. Die Windows-Entwickler haben ein System erdacht, mit dem möglichst schnell erkennbar sein soll, was im Programm welches syntaktische Element darstellt (Funktion, Variable, Konstante).
Von Windows bereit gestellte Funktionen beginnen in der Regel mit einem Großbuchstaben. Setzt sich der Funktionsname aus mehreren Worten zusammen, so wird jeder Wortanfang groß geschrieben, z. B. DefWindowProc(). Ich werde dieser Konvention nur teilweise folgen - die meisten der selbstdefinierten Funktionen in den Beispielprogrammen werden mit einem Kleinbuchstaben beginnen, bereits gesehene Ausnahmen davon stellen die Funktionen MsgLoop() und WndProc() dar.
Konstanten werden komplett in Großbuchstaben geschrieben. Dieses System
sollte auch in ihren Programmen beibehalten werden, da die Bezeichner sonst nur
schwer von Variablennamen zu unterscheiden sind.
Konstanten beginnen oft mit einem bestimmten Präfix, der sie in einen
Sachzusammenhang mit der Art der Konstante bzw. ihrer Verwendung bringt.
| Präfix | Bedeutung |
|---|---|
| CS_ | Class Style - Stile einer Fensterklasse, z. B. CS_BYTEALIGNCLIENT |
| CW_ | Create Window - zur Fenstererzeugung, meist als Parameter, z. B. CW_USEDEFAULT |
| DT_ | Draw Text - Konstanten zur Textausgabe, z. B. DT_CENTER |
| IDC_ | ID Cursor - ID (Kennziffer) für eine Cursorform, z. B. IDC_WAIT (die Sanduhr) |
| IDI_ | ID Icon - ID für ein Icon, z. B. IDI_APPLICATION |
| MB_ | Message Box - Stile für Meldungsfenster, z. B. MB_YESNOCANCEL |
| WM_ | Window Message - Nachrichten an Fenster, z. B. WM_DESTROY |
| WS_ | Window Style - Konstanten für Fensterstile, z. B. WS_OVERLAPPEDWINDOW |
Konstanten werden im MASM32-Paket zu großen Teilen in der Datei windows.inc definiert. Es gibt noch Dutzende weiterer Präfixe, z. B. für Windows-Steuerelemente (ES_, SBS_, CB_) sowie eine Art Empfehlung für selbstdefinierte Steuerelemente (IDC_ - ID Control) und Menüpunkte (IDM_).
Der grundlegende Datentyp in Win32 ist das DWORD, unter Win16 war es noch das WORD. Da den Windows-Entwicklern sicher klar war, dass die Welt nicht bei 16 Bit stehen bleibt, haben sie ihre eigenen Datentypen definiert, die im Hintergrund auf einen plattformspezifischen und in C gültigen Datentyp umgesetzt wurden. Hatte man sich in seinem 16-Bit-Windowsprogramm an diese "neuen" Datentypen gehalten, dann war der Umstieg auf die 32-Bit-Welt deutlich einfacher, unter Umständen war die Sache bereits mit einer Neukompilierung erledigt. Ein ähnlicher Umstieg ist für die 64-Bit-Versionen zu erwarten. Insofern ist der Wust neuer Datentypen wie HINSTANCE, HWND, LPARAM und WPARAM schon zu verstehen. Allerdings wird dadurch in meinen Augen auch verhindert, dass man erkennt, ob man es letztlich mit einem einfachen Datentypen oder einer Struktur zu tun hat.
In eine ähnliche Kerbe schlägt die Ungarische Notation, wenn auch auf der anderen Seite der Variablendeklaration. Benannt wurde sie nach dem Microsoft-Programmierer Charles Simonyi. Dabei ist vorgesehen, dass jeder Variablenname mit Kleinbuchstaben beginnt, die den Datentyp der Variable kennzeichnen. Ganz durchgehalten hat Microsoft diese Systematik beim Umstieg auf 32 Bit allerdings auch nicht. Beachten sie bitte, dass die Notation auf den Datentypen von C beruht, nicht auf denen von MASM.
| Präfix | Bedeutung |
|---|---|
| b, f | BOOL (f steht für Flag; beachten sie, dass es noch bool und BOOLEAN gibt) |
| by | byte |
| c | char |
| cw | wide char (Unicode) |
| cx, cy | int (Längenangaben; das c steht für count; meist im Zusammenhang mit Koordinaten) |
| dw | DWORD |
| fn | Function |
| h | Handle |
| i | int |
| l | LONG |
| lp | Long Pointer |
| n | short |
| p | Pointer |
| s | String |
| sz | ASCIIZ-String, also ein String, bei dem ASCII Null das Ende anzeigt |
| w | WORD |
| x, y | int (Koordinaten) |
In der Programmiererrealität ergeben sich dann auch schon mal Kombinationen wie lpfn - Long Pointer to Function usw.
Ob sie diese Systematik auch in ihren Assemblerprogrammen verwenden ist komplett
ihnen überlassen. Vielleicht haben sie eine eigene Konvention die sie
sinnvoller oder einfacher finden, oder sie möchten die Konvention verwenden,
die sie aus ihrer Firma kennen. Die Kenntnis dieser Notation kann ihnen aber
weiterhelfen, wenn sie Quelltexte in C für Windows lesen.
Ich selbst werde in meinen Beispielen eine etwas abweichende Notation verwenden:
i für Ganzzahlen, c für Zeichenketten, h für Handles und p
für Zeiger wo es mir angebracht erscheint.
Bei komplexen Datentypen (Strukturen) gibt es keine Festlegungen, wie die
Variablen genannt werden sollen. Da Strukturbezeichner ebenfalls komplett in
Großbuchstaben geschrieben werden, können sie die jeweilige Variable
in Kleinbuchstaben schreiben oder abkürzen, z. B.
msg MSG <?> ps PAINTSTRUC <?>Bei Strukturmembern greift jedoch wieder die Ungarische Notation
LOCAL wc:WNDCLASSEX mov wc.lpfnWndProc,OFFSET WndProc
| Vorheriges Kapitel (Kurzer geschichtlicher Überblick) | Nach oben | Nächstes Kapitel (Ermittlung der Windows-Version) |
| Zum Inhaltsverzeichnis | ||
| Zur Startseite |