Um zu verstehen, warum man in C einen Standard eingeführt hat bedarf es ein wenig mehr der Geschichte zu dieser Sprache.C C++ C/C++ Programmieren C Kurs Programmierung Systemprogrammierung Win32 api Win-Api Windows Systemprogrammierung C Programmierung Win32 api Win-Api Windows Systemprogrammierung Kapitel 2: Nachrichten

In dem vorhergehenden Beispiel haben Sie sicherlich bemerkt, dass die Nachrichten-Warteschlange und das Abholen und Auswerten der Nachrichten das Kernstück eines WIN32-Programms ist.


Was ist eine Nachricht
Eine Nachricht ist zunächst nichts anderes als ein konstanter Integerwert, welcher in der Headerdatei <winuser.h> folgendermaßen deklariert ist:

…
#define WM_QUERYOPEN 19
#define WM_QUEUESYNC 35
#define WM_QUIT 18
#define WM_RENDERALLFORMATS 774
#define WM_RENDERFORMAT 773
#define WM_SETCURSOR 32
#define WM_SETFOCUS 7
…

Bei einem Blick auf die unzählige Anzahl der Nachrichten, die es gibt, kann man froh über die Funktion DefWindowProc(hWnd, umsg, wParam, lParam) sein, die für Sie die nicht behandelten Nachrichten übernimmt und weiterverar-beitet.

Genau genommen ist eine Nachricht auch wieder nichts anderes als eine Datenstruktur, die wie folgt definiert ist:

typedef struct tagMSG {     // msg
    HWND   hwnd;
    UINT   message;
    WPARAM wParam;
    LPARAM lParam;
    DWORD  time;
    POINT  pt;
} MSG;

Diese Nachrichten bestehen aus dem Fensterhandle hwnd, der ID (message) der Nachricht selbst, die einen der eben erwähnten konstanten Integerwerten entspricht. Weiterhin befinden sich in der Struktur MSG die Daten (wParam und lParam), welche der Funktion WndProc() mitgegeben werden. Mit time wird auch der Zeitpunkt der Nachricht, seit dem Start von Windows in Millisekunden vergangen sind, mitgeschickt. In der POINT-Struktur befinden sich die x-, y-Koordinaten für den Mauszeiger zum Zeitpunkt des Absendens der Nachricht.

Das folgende Beispiel soll demonstrieren, wie Sie auf Nachrichten in einer Prozedur reagieren können. In diesem Beispiel werden einige einfache Nachrichten ausgewertet. Ob Sie jetzt eine Taste gedrückt oder losgelassen haben, die Fensterposition oder Größe verändert haben, die linke oder rechte Maustaste betätigt und losgelassen haben, bei jeder dieser Aktionen wird der Titel des Fensters mit entsprechendem Ereignis verändert. Den Titel des Fensters können Sie mit der Funktion SetWindowText() verändern.

BOOL SetWindowText(
  HWND hWnd,         // Fenster-Handle
  LPCTSTR lpString   // Adresse des Strings
);

Als ersten Parameter übergeben Sie der Funktion den Fenster-Handle und als zweiten einen nullterminierten String, welcher der neue Fenstertitel sein soll. Natürlich werden Sie in der Praxis nicht so auf die Nachrichten reagieren, aber damit Sie auch sehen, dass auch tatsächlich ein Ereignis eingetroffen ist, stellt dies eine einfache Demonstration da.

#include <windows.h>

LRESULT CALLBACK WndProc(HWND, UINT, WPARAM, LPARAM);

LPCSTR lpszAppName = "AppName";
LPCSTR lpszTitle   = "Meine erste Applikation";

int APIENTRY WinMain(HINSTANCE hInstance,
           HINSTANCE hPrevInstance, PSTR szCmdLine, int iCmdShow)
{
   HWND       hWnd;
   MSG        msg;
   WNDCLASSEX   wc;

   wc.cbSize        =  sizeof(WNDCLASSEX);
   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 =  lpszAppName;
   wc.lpszMenuName  =  lpszAppName;
   wc.hIconSm       =  LoadIcon(NULL, IDI_APPLICATION);

   if( RegisterClassEx(&wc) == 0)
      return 0;

   hWnd = CreateWindowEx(NULL,
                         lpszAppName,
                         lpszTitle,
                         WS_OVERLAPPEDWINDOW,
                         0,
                         0,
                         CW_USEDEFAULT,
                         CW_USEDEFAULT,
                         NULL,
                         NULL,
                         hInstance,
                         NULL);

   if( hWnd == NULL)
      return 0;

   ShowWindow(hWnd, iCmdShow);
   UpdateWindow(hWnd);

   while (GetMessage(&msg, NULL, 0, 0))
   {
      TranslateMessage(&msg);
      DispatchMessage(&msg);
   }
   return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT umsg, WPARAM wParam, LPARAM lParam)
{
   switch (umsg)
   {
   case WM_DESTROY:
      {
         PostQuitMessage(0);
         return 0;
      }
   case WM_KEYDOWN:
     {
        SetWindowText(hWnd, "Eine Taste wurde gedrückt");
        return 0;
     }
   case WM_KEYUP:
     {
       SetWindowText(hWnd, "Die Taste wurde wieder losgelassen");
       return 0;
     }
   case WM_LBUTTONDOWN:
     {
       SetWindowText(hWnd, "Die linke Maustaste wurde gedrückt");
       return 0;
     }
   case WM_LBUTTONUP:
     {
    SetWindowText(hWnd, "Die linke Maustaste wurde losgelassen");
    return 0;
     }
   case WM_RBUTTONDOWN:
     {
     SetWindowText(hWnd, "Die rechte Maustaste wurde gedrückt");
     return 0;
     }
   case WM_RBUTTONUP:
     {
   SetWindowText(hWnd, "Die rechte Maustaste wurde losgelassen");
   return 0;
     }
   case WM_MOVE:
     {
SetWindowText(hWnd, "Die Position des Fensters wurde verändert");
return 0;
     }
   case WM_SIZE:
     {
   SetWindowText(hWnd, "Die Größe des Fensters wurde verändert");
   return 0;
     }
   }
   return DefWindowProc(hWnd, umsg, wParam, lParam);
}

Hierzu nochmals zur Verdeutlichung der Vorgang bildlich, vom Eintreten eines Ereignisses zur Nachrichtenwarteschlange über das Abholen der Nachricht und der Weitergabe an die Window Prozedur:

Vorgang einer Nachrichten-Verarbeitung unter Windows

Vorgang einer Nachrichten-Verarbeitung unter Windows

Sicherlich fragen Sie sich auch, in welchem Parameter der Windows-Prozedur sich den die Nachricht befindet. Bei einem Vergleich der Windows Prozedur WndProc() und der Struktur MSG sollte sich diese Frage von selbst aufklären.

LRESULT CALLBACK WndProc(HWND hWnd, UINT umsg,
                         WPARAM wParam, LPARAM lParam)
/* -------------------------------------------------------*/
typedef struct tagMSG {     // msg
    HWND   hwnd;
    UINT   message;
    WPARAM wParam;
    LPARAM lParam;
    DWORD  time;
    POINT  pt;
} MSG;

Der konstante Integerwert der Nachricht, welche in der Headerdatei <winuser.h> deklariert sind, ist also der zweite Parameter der Windows Prozedur. Der erste Parameter entspricht dem Fenster-Handle, auf dem diese Nachricht zutrifft. Die Daten der Nachrichten befinden sich in wParam und lParam. wParam ist noch ein Relikt alter 16 Bit-Zeiten, hat aber mittlerweile ebenfalls wie lParam 32 Bits. Nicht alle Nachrichten, die eintreffen, benutzen diese Parameter und einige wiederum verwenden beide Parameter. Bei einigen Nachrichten befinden sich im Parameter wParam gleich zwei Werte. Welche dies sind, lässt sich mit den Makros HIWORD() und LOWORD() ermitteln. Mit dem Makro HIWORD() werten Sie die höheren zwei Bytes des 32 Bit-Wertes aus (0xFFFF0000) und mit LOWORD() die niedrigeren (0x0000FFFF) zwei Bytes (Ein WORD hat bei Windows 16 Bit == 2 Bytes). Auf diese Makros werden Sie noch einigemale stoßen. Bei der Nachricht WM_CLOSE sind allerdings beide Parameter gleich 0.

Eine Nachricht aus dem Programm heraus versenden können Sie entweder mit der Funktion PostMessage() womit eine Nachricht in die Nachrichtenschlange gelegt wird oder mit der Funktion SendMessage() womit die Nachricht gleich direkt an die Nachrichtenfunktion eines Fensters geschickt wird. Bei der Funktion PostMessage() erfolgt die Rückgabe ohne Wartezeit und bei SendMessage() erfolgt die Rückgabe erste wieder, wenn eine Antwort eingetroffen ist. Zuerst die Syntax zu SendMessage():

LRESULT SendMessage(
  HWND hWnd,      // Handle des Zielfensters
  UINT Msg,       // Die Nachricht, die gesendet wird
  WPARAM wParam,  // Erster Nachrichtenparameter
  LPARAM lParam   // Zweiter Nachrichtenparameter
);

Und jetzt noch die Syntax zur Funktion PostMessage():

BOOL PostMessage(
  HWND hWnd,      // Handle des Zielfensters
  UINT Msg,       // Die Nachricht, die gesendet wird
  WPARAM wParam,  // Erster Nachrichtenparameter
  LPARAM lParam   // Zweiter Nachrichtenparameter
);

Beide Funktionen werden noch des öfteren verwendet.

Weiter mit Kapitel 3: Nachrichten auswerten            zum Inhaltsverzeichnis