Die WinAPI Plattform

Tutorials

(05.) Tastaturabfragen

In diesem Tutorial zeigen ich dir, wie man in Windows Tastaturbefehle bearbeitet. Zu diesem Zweck wird der folgende Code im Fenster des Programms immer den Status der Pfeiltasten anzeigen. Um das Platzieren von Text noch ein wenig zu üben, wird der Text auch wieder schön zentriert ausgegeben. Aber zuerst folgt wieder der Grundbau.

#include <windows.h>

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

int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
                   PSTR szCmdLine, int iCmdShow)
{
   MSG          msg;
   HWND         hWnd;
   WNDCLASS     wc;
   
   char szAppName[] = "Tastaturabfragen";
   
   wc.cbClsExtra         = 0;
   wc.cbWndExtra         = 0;
   wc.hbrBackground      = (HBRUSH) GetStockObject(WHITE_BRUSH);
   wc.hCursor            = LoadCursor(NULL, IDC_ARROW);
   wc.hIcon              = LoadIcon(NULL, IDI_APPLICATION);
   wc.hInstance          = hInstance;
   wc.lpfnWndProc        = WndProc;
   wc.lpszClassName      = szAppName;
   wc.lpszMenuName       = NULL;
   wc.style              = CS_HREDRAW | CS_VREDRAW;
   
   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)
{

Dann deklarieren wir wieder eine statische RECT Struktur, in der wir die Größe des Anwendungsbereiches speichern werden. Danach ein statisches bool Array, in dem wir die Zustände der Pfeiltasten speichern (0 = nach Links, 1 = nach Oben, 2 = nach Rechts, 3 = nach Unten).

   static RECT   rect;
   static bool   ArrowKeys[4];
   
   switch (message)
   {

Nun fangen wir wieder die WM_SIZE Nachricht ab, um die Größe des Anwendungsbereiches in unserer Rect Stukrur zu speichern.

   case WM_SIZE:
      {
         rect.right  = LOWORD(lParam);
         rect.bottom = HIWORD(lParam);
         return 0;
      }

Danach bearbeiten wir die WM_KEYDOWN Nachricht, die wird jedesmal geschickt, wenn der Benutzer eine Taste herunterdrückt (wenn er sie gedrückt hält, dann kommen mehrere Nachrichten dieses Typs) und das Fenster den Fokus hat (also eine blaue Titelleiste, oder den Cursor).

   case WM_KEYDOWN:
      {

Jetzt prüfen wir, welche Taste gedrückte wurde. Dies steht in wParam in Form eines virtual Keycodes, dass heißt, das auf jeder Tastatur der Pfeil nach oben den Code VK_UP bekommt, auch wenn die Taste ganz woanders liegt. Wir benutzen die Konstanten VK_LEFT, VK_UP, VK_RIGHT und VK_DOWN, um den jeweiligen Tastenstatus abzufragen.

         switch (wParam)
         {
         case VK_LEFT:

Nun setzten wir das Element in unserem Array, das die Tastenaktionen speichert, auf true, das heißt, die Taste ist gerade gedrückt. Da der Anwendungsbereich neu gezeichnet werden muss, brechen wir die switch Abfrage mit break ab.

            ArrowKeys[0] = true;
            break;
            
         case VK_UP:
            ArrowKeys[1] = true;
            break;

         case VK_RIGHT:
            ArrowKeys[2] = true;
            break;

         case VK_DOWN:
            ArrowKeys[3] = true;
            break;

Alle anderen Tasten wollen wir ignorieren, deshalb brechen wir die Funktion im default Statement ab. Wenn die switch Abfrage jedoch mit break abgebrochen wurde, wird noch die InvalidateRect Funktion aufgerufen.

Der erste Parameter von InvalidateRect ist der Handle zu dem Fenster, dass neu gezeichnet werden soll. Der zweite Parameter gibt ein Rechteck in diesem Fenster an. Wenn man hier nicht Null angibt, dann wird nur der angegebene Bereich gelöscht und neugezeichnet. Wir geben der Einfachheit halber NULL an, was bedeutet, dass der ganze Anwendungsbereich neu gezeichnet werden soll. Mit dem dritten Parameter geben wir an, dass Windows das Fenster vor dem Neuzeichnen nicht löschen (sprich mit der Hintergrundfarbe ausmalen) soll, damit verhindern wir ein wenig das Flackern der Schrift.

         default:
            return 0;
         }
         InvalidateRect(hWnd, NULL, FALSE);
         return 0;
      }

Und so haben wir alle vier Pfeiltasten behandelt. Jetzt müssen wir nur noch die Arrayelemente wieder auf false setzten, wenn die Taste wieder gelöst wird. Das funktioniert ziemlich ähnlich, nur das wir diesmal nicht die Windows Nachricht WM_KEYDOWN bearbeiten, sondern WM_KEYUP.

      case WM_KEYUP:
      {
         switch (wParam)
         {
         case VK_LEFT:
            ArrowKeys[0] = false;
            break;
            
         case VK_UP:
            ArrowKeys[1] = false;
            break;
            
         case VK_RIGHT:
            ArrowKeys[2] = false;
            break;
            
         case VK_DOWN:
            ArrowKeys[3] = false;
            break;
            
         default:
            return 0;
         }
         InvalidateRect(hWnd, NULL, FALSE);
         return 0;
      }

So, nun ist in ArrowKeys für jede Pfeiltaste eine 1 (true), wenn die Taste gedrückt und eine 0 (false), wenn sie gelöst ist. Jetzt müssen wir das nur noch auf den Bildschirm bringen. Dazu bearbeiten wir die WM_PAINT Nachricht und legen eine PAINTSTRUCT Struktur und eine hDC Variable an.

   case WM_PAINT:
      {
         PAINTSTRUCT  ps;
         HDC          hDC;

Und auch wieder eine SIZE Struktur, in der wir die Größe der Schrift speichern werden.

         SIZE         size;

Dann legen wir ein char Array mit 40 Elementen an, in dem der Text gespeichert werden soll. Anschließend noch eine int Variable in der wir die Stringlänge speichern werden. Danach binden wir hDC noch an einen Gerätekontext.

         char szKeyStatus[40];
         int  iKeyLength;
         
         hDC = BeginPaint(hWnd, &ps);

Nun benutzten wir eine for Schleife, um alle 4 Tastenzustände auf den Bildschirm zu bringen. Die erste Zeile in der Schleife ist schon neu, hier benutzen wir die wsprintf Funktion um formatierten Text in einen String zu schreiben (wsprintf ist die Windows Version von sprintf). Der erste Parameter ist das Array, in dem der String gespeichert werden soll. Der zweite ist der String mit den Formatzeichen (%i), die weiteren Parameter sind von den Formatzeichen abhängig. Der Rückgabewert ist die Anzahl der geschreibenen Zeichen.

         for (int i = 0; i < 4; i++)
         {
            iKeyLength = wsprintf(szKeyStatus, "Status Taste %i: %i",
                                  i, ArrowKeys[i]);

Die nächste Funktion dürfte dir schon bekannt sein. Mit GetTextExtentPoint32 bestimmen wir die Höhe und Breite des Textes.

            GetTextExtentPoint32(hDC, szKeyStatus, iKeyLength, &size);

Nun folgt die TextOut Funktion, die Berechnung zur horizontalen (X) Zentrierung ist bekannt, nur bei der vertikalen Berechnung gehen wir diesmal ein wenig anders vor, denn wir haben ja vier Zeilen und nicht wie zuvor nur eine Zeile. Also berechnen wir zuerst wieder den Mittelpunkt des Anwendungsbereiches und subtrahieren dann zwei mal die Höhe einer Zeile. Dann kann die erste Zeile gedruckt werden. Da aber jede folgende Zeile eins nach unten rutschen muss, addieren wir noch i mal die Höhe einer Zeile (da i am Anfang 0 ist, bleibt es bei der ersten Zeile dabei).

            TextOut(hDC, rect.right / 2 - size.cx / 2, rect.bottom / 2 -
                    2 * size.cy + i * size.cy, szKeyStatus, iKeyLength);
         }
         EndPaint(hWnd, &ps);
         
         return 0;
      }
   case WM_DESTROY:
      {
         PostQuitMessage(0);
         return 0;
      }
   }
   return DefWindowProc(hWnd, message, wParam, lParam);
}

So, nun sind wir auch schon am Ende dieses Tutorials angekommen. Hier hast du gelernt, wie man über den Tastencode und den Nachrichten WM_KEYDOWN und WM_KEYUP Tastaturbefehle bearbeitet. Ich hoffe du hattest viel Spaß und hast etwas gelernt.Wenn du meinst, dass irgend eine Stelle in diesem Tutorial ungenau ist, oder etwas nicht erklärt wird, dann schreibe mir einfach eine E-Mail.

webmaster@win-api.de