DarkWinAPI by Darkleo.com
Kurze Erläuterung wie Windows funktioniert
Windows teilt uns alle Ereignisse über Nachrichten mit. Da ist auch der
Zeichenvorgang keine Ausnahme. Windows bestimmt also, wann wir zeichnen
sollen. Es gibt viele Gründe, warum der Anwendungsbereich neu gezeichnet
werden soll, also ungültig geworden ist. Um nur ein paar Beispiele zu
nennen: Fenster vergrößert, war von einem anderen Fenster bedeckt oder das
Fenster wird gescrollt. Natürlich können wir auch selber entscheiden, dass wir
zeichnen möchten. In dem Zeichenvorgang muss der Anwendungsbereich wieder als
gültig erklärt werden, sonst wird Windows dein Programm mit
WM_PAINT
Nachrichten bombadieren, da es meint, dass dieser Bereich
nochmal neu gezeichnet werden muss.
#define STRICT #include <windows.h> LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM); const char szAppName[] = "Textausgabe im Anwendungsbereich"; int WINAPI WinMain( HINSTANCE hInstance, HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow) { MSG msg; HWND hWnd; WNDCLASS wc; wc.style = CS_HREDRAW | CS_VREDRAW; wc.lpfnWndProc = WndProc; wc.cbClsExtra = 0; wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hCursor = LoadCursor(NULL, IDC_ARROW); wc.hIcon = LoadIcon(NULL, IDI_APPLICATION); wc.hbrBackground = (HBRUSH) GetStockObject(WHITE_BRUSH); wc.lpszClassName = szAppName; wc.lpszMenuName = NULL; RegisterClass(&wc); hWnd = CreateWindow( szAppName, szAppName, WS_OVERLAPPEDWINDOW, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, NULL, NULL, hInstance, NULL); ShowWindow(hWnd, iCmdShow); UpdateWindow(hWnd); while (GetMessage(&msg, NULL, 0, 0)) { TranslateMessage(&msg); DispatchMessage(&msg); } return msg.wParam; } LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam) { switch (message) {
Diesmal bearbeiten wir außer der WM_DESTROY
Nachricht noch die
WM_PAINT
Nachricht (im
MSDN: WM_PAINT
). Wenn wir diese Nachricht hier nicht bearbeiten
würden, würde DefWindowProc
das übernehmen und nichts weiter tun,
als den ungültigen Bereich wieder als gültig erklären.
case WM_PAINT: {
Damit man in Windows auf dem Bildschirm zeichnen darf, muss man sich erst
dafür "anmelden". Man bekommt dann einen Zeichenbereich zugewiesen. Alle
Informationen, die mit dieser "Anmeldung" zusammen hengen werden in der
PAINTSTRUCT
Struktur (im
MSDN: PAINTSTRUCT
) gespeichert. Danach deklarieren wir noch
eine Variable vom Typ HDC
, welche den Handle auf unseren
Zeichenbereich speichern soll. hDC
heißt Handle [to a] Device
Context, was soviel bedeutet wie Handle auf einen "Geräte Kontext".
hDC
ist eigentlich redundant (überflüssig), da eine Membervariable der
PAINTSTRUCT
Struktur genau diesen Handle speichert. Jedoch versucht
man so den Code lesbarer zu machen.
PAINTSTRUCT ps; HDC hDC;
Die folgende Zeile speichert den Text, den wir ausgeben wollen, in dem Array
szText
.
const char szText[] = "Hallo, dies ist der Text.";
Mit der BeginPaint
Funktion (im
MSDN: BeginPaint
) teilen wir Windows mit, dass wir in unseren
Anwendungsbereich zeichnen möchten. Der erste Parameter der BeginPaint
Funktion legt das Fenster fest, in dem wir zeichnen wollen. Der zweite Parameter
ist ein Zeiger auf unsere PAINTSTRUCT
Funktion, in der Windows die
Daten bezüglich des Zeichenbereiches speichern wird. Der Rückgabewert ist der
Handle auf den Zeichenbereich. Die BeginPaint
Funktion übermalt,
wenn es so gewollt war, den zu erneuernden (ungültigen) Zeichenbereich mit dem
Hintergrund Füllmuster. Der Zeichenbereich wird danach wieder als gültig
erklärt. Dieses als gültig markieren ist wichtig, denn solange noch ein Bereich
als ungültig markiert ist, wird uns Windows immer wieder eine WM_PAINT
Nachricht schicken. Wenn wir die WM_PAINT
Nachricht nicht
bearbeiten, erledigt dies die DefWindowProc
Funktion für uns.
hDC = BeginPaint(hWnd, &ps); {
Nun ist alles zum Zeichnen vorbereitet. Wir müssen nur noch die TextOut
Funktion (im
MSDN: TextOut
) aufrufen, die für uns den Text auf den
Bildschirm bringt. Der erste Parameter ist, wie bei allen Funktionen der GDI (Graphics
Device Interface, also alle Funktionen, die etwas auf einem Ausgabegerät
ausgeben sollen), der Handle auf den Zeichenbereich (Device Context). Der zweite
und dritte Parameter legen wir die X und Y Koordinaten relativ zu der linken,
oberen Ecke des Anwendungsbereiches fest und der linken oberen Ecke des Textes.
Würden wir hier 0, 0
angeben, so wäre der Text ganz oben, links in
der Ecke. Der vierte Parameter ist ein Zeiger auf den Text, der ausgegeben
werden soll. Der letzte Parameter ist die Länge der Zeichenkette.
TextOut(hDC, 50, 50, szText, sizeof(szText) - 1);
Das ist vorerst alles, was wir auf dem Bildschirm ausgeben möchten. Am Ende
jeder Zeichenoperation muss man Windows mitteilen, dass wir nichts mehr zeichnen
möchten. Die EndPaint
Funktion (im
MSDN: EndPaint
) macht genau dieses und gibt den Device Context
wieder frei.
} EndPaint(hWnd, &ps);
Da wir die WM_PAINT
Nachricht bearbeitet haben, müssen wir die
Funktion mit return 0;
verlassen.
return 0; } case WM_DESTROY: { PostQuitMessage(0); return 0; } } return DefWindowProc(hWnd, message, wParam, lParam); }
Das obige Programm erstellt ein Fenster und gibt darauf Text aus.
BeginPaint
ist übriegens nicht die einzige Möglichkeit einen
Devicecontext zu bekommen. Man kann noch GetDC
und
CreateDC
benutzen, jedoch ist BeginPaint
die am häufigsten
benutzte Möglichkeit. Die anderen sind nur in speziellen Situationen notwendig
und sinnvoll.