Einführung in DirectDraw Teil 2: Erstellen eines einfachen Windows-Programms
Datum: 27.1.00
Autor: Tim-Oliver Husser
Themen für Teil 2 dieses Tutorials:
Der Programmkopf
Die WinMain
Die Fensterprozedur
Der Programmkopf
Nachdem Sie in Teil 1 dieses Tutorials gelernt haben, wie man die IDE konfiguriert,
fangen wir hier in Teil 2 mit der Programmierung an.
Jetzt geht es um den Teil, der eigentlich nur für die Verwaltung zuständig
ist. Hier erfahren Sie, wie man ein neues Fenster erstellt und anzeigt, und
wie man die Windows-Nachrichten abfängt und verarbeitet.
Fangen wir also an. Zunächst brauchen wir nur eine einzige Include-Datei.
Somit wäre die erste Zeile in der main.cpp:
#include <windows.h>
Jetzt folgen zwei Definitionen. Die eine ist für den Fenstertitel, die
andere für die Fensterklasse. Den Namen für die Fensterklasse brauchen
wir im ganzen Programm zwar nur zweimal; die Arbeit mit #define macht die Sache
aber etwas übersichtlicher. Die Definitionen sind:
#define WindowTitle "DirectDraw-Tutorial"
#define WindowClassName "DDWinClass"
Jetzt folgt die Definition der Fensterprozedur. Dessen Bedeutung erläutere
ich weiter unten.
LONG FAR WINAPI WndProc (HWND , UINT , UINT , LONG) ;
Die WinMain
Als nächstes kommt das Herzstück eines jeden Windows-Programmes: die
WinMain-Prozedur. Sie ist das, was unter DOS die main-Prozedur war. Hier wird
die Fensterklasse mit Daten gefüllt, bei Windows registriert und schließlich
wird damit das neue Fenster aufgerufen. Der Funktionskopf zusammen mit den Variablendeklarationen:
int WINAPI WinMain (HINSTANCE hInstance,
HINSTANCE hPrevInstance,
LPSTR lpCmdLine,
int nCmdShow)
{
MSG msg ;
HWND hwnd ;
WNDCLASS wndclass ;
Jetzt prüfen wir, ob schon eine Instanz des Programms vorhanden ist, also
ob es schon läuft. Falls nicht, füllen wir die Fensterklasse mit Daten
und registrieren sie.
if (!hPrevInstance)
{
wndclass.style = CS_HREDRAW | CS_VREDRAW ;
wndclass.lpfnWndProc = WndProc ;
wndclass.cbClsExtra = 0 ;
wndclass.cbWndExtra = 0 ;
wndclass.hInstance = hInstance ;
wndclass.hIcon = LoadIcon (NULL , IDI_APPLICATION) ;
wndclass.hCursor = LoadCursor (NULL , IDC_ARROW) ;
wndclass.hbrBackground = (HBRUSH)GetStockObject (LTGRAY_BRUSH) ;
wndclass.lpszMenuName = NULL ;
wndclass.lpszClassName = WindowClassName ;
RegisterClass (&wndclass) ;
}
Jetzt erstellen wir das Fenster und zeigen es an:
hwnd = CreateWindow (WindowClassName ,
WindowTitle ,
WS_OVERLAPPEDWINDOW ,
CW_USEDEFAULT ,
CW_USEDEFAULT ,
CW_USEDEFAULT ,
CW_USEDEFAULT ,
NULL , NULL ,
hInstance , NULL) ;
ShowWindow (hwnd , nCmdShow) ;
UpdateWindow (hwnd) ;
Was jetzt kommt, ist der wichtigste Teil eines Windows-Programmes: der "Message-Loop".
Hier werden die von Windows kommenden Fensternachrichten abgefangen und verarbeitet.
while(1)
{
if (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if (msg.message == WM_QUIT) break;
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
Wenn Sie noch nie ein Windows-Programm geschrieben haben, werden Sie jetzt
wohl denken: "Ja, spinnt der denn? Ist ja 'ne schöne Endlosschleife!"
So ganz endlos ist diese Schleife aber nicht, trotz der eigenartigen Abbruchbedingung
der While-Schleife, die gar keine ist. Wenn Sie genau hinsehen, erkennen Sie
die Ausgang der Schleife: Ist msg.message - was auch immer das ist - gleich
WM_QUIT - was auch immer das ist - wird die Schleife verlassen.
Dann will ich Sie mal nicht so im Regen stehen lassen. Mit der Anweisung PeekMessage
wird die nächste Nachricht aus der Nachrichtenschleife abgerufen. Alle
Nachrichten, die Windows an ein Programm sendet, werden hier gespeichert und
können dann der Reihe nach abgearbeitet werden. Der erste Parameter an
PeekMessage ist ein Zeiger auf die Variable msg vom Typ MSG. Hier wird die zu
verarbeitende Nachricht gespeichert.
Jetzt wird Ihnen wahrscheinlich gerade ein Licht aufgehen. Ja, WM_QUIT ist eine
bestimmte Fensternachricht (WM = window message) , und wie der Name schon sagt,
ist sie für den Programmabbruch bestimmt.
TranslateMessage übersetzt die Nachricht und DispatchMessage leitet sie
an die Fensterprozedur, die ich gleich besprechen werde, weiter.
So, noch zwei Zeilen, dann ist die WinMain komplett:
return msg.wParam ;
} // WinMain
Die Fensterprozedur
Jetzt kommt, wie angekündigt, die Fensterprozedur. Hier ist sie erst komplett,
besprechen werde ich sie hinterher:
LONG FAR WINAPI WndProc (HWND hwnd , UINT message ,
UINT wParam , LONG lParam)
{
switch (message)
{
case WM_DESTROY:
{
PostQuitMessage (0) ;
return 0 ;
} break;
} // switch (message)
return DefWindowProc (hwnd , message , wParam , lParam) ;
} // WndProc
Fällt Ihnen was auf? Hier kommen wieder Konstanten, die mit WM_ anfangen,
vor und die Funktion übernimmt einen Parameter mit der Bezeichnung message.
Es geht also wieder um Fensternachrichten.
Die Prozedur ist eigentlich ganz einfach. Ist message gleich WM_DESTROY, wird
das Programm durch den Aufruf von PostQuitMessage beendet.
Etwas trickreich ist die Art, wie Windows Nachrichten verarbeitet. Wird das
Programm z.B. durch einen Klick auf das Kreuz oben rechts beendet, schickt Windows
zuerst eine Nachricht an das entsprechende Programm, daß es sich zerstören
soll (WM_DESTROY). Ist das Programm damit einverstanden, schickt es Windows
eine Nachricht (PostQuitMessage), daß es beendet werden will. Windows
schickt dann eine neue Nachrichgt zum Beenden (WM_QUIT).
Verwirrt? Ist nicht schlimm, mit den Windows-Nachrichten haben wir von jetzt
an nicht mehr viel am Hut.
Damit ist Ihr erstes Windows-Programm fertig. Sollten Sie Probleme mit den Code
oder mit etwas anderem haben, schreiben Sie mir einfach eine eMail.
Das Projekt können Sie hier
herunterladen.
In Teil 3 des Tutorials
lernen Sie, wie man ein einfaches DirectDraw-Programm erstellt.