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.