Die WinAPI Plattform

Tutorials

Tutorial Nummer 06

(06.) Texte einlesen

In dem letzten Tutorial habe ich dir gezeigt, wie man einzelne, spezielle Tasten abfragt. Aber diese Art der Abfrage war nur für Tasten gedacht, die keinen wirklichen ASCII Code haben. Nach dem Prinzip wäre es viel zu aufwändig solche Eingaben in Text umzuformen. In diesem Tutorial lernst du, wie man Texte von der Tastatur einliest.

#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;
   
   const char szAppName[] = "Texte einlesen";
   
   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))
   {

Nun erkläre ich dir, wozu diese TranslateMessage Funktion gut ist. Jedesmal wenn eine TranslateMessage Funktion die Nachricht WM_KEYDOWN zu gesicht bekommt (wird durch &msg mit übergeben), dann prüft die Funktion, ob das eingegebene Zichen ein gültiges und druckbares ASCII Zeichen ist. Wenn dem so ist, dann fügt TranslateMessage hinter die WM_KEYDOWN Nachricht eine WM_CHAR Nachricht ein. Der ASCII Code des Zeichens steht dann bei der Behandlung dieser Nachricht im wParam.

      TranslateMessage(&msg);
      DispatchMessage(&msg);
   }
   return msg.wParam;
}

LRESULT CALLBACK WndProc(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{

Der eingegebene Text wird in cBuffer gespeichert. Man kann höchstens 100 Zeichen eingeben. Man könnte diesen Buffer beliebig vergrößern. Da wir für den String keine Kennzeichnung des Endes haben (wie ein abschließendes '\0'), müssen wir die aktuelle Schreibposition in einer extra Variablen speichern.

   static RECT     rect;
   static char     cBuffer[100];
   static int      iActLen;
   
   switch (message)
   {
   case WM_SIZE:
      {

Da wir nicht direkt an den Rand zeichnen möchten, gönnen wir uns Rechts und Links einen Rand von 20 Pixeln.

         rect.left   = 20;
         rect.right  = LOWORD(lParam) - 20;
         rect.bottom = HIWORD(lParam);
         return 0;
      }

Die WM_CHAR Nachricht kommt immer nach der WM_KEYDOWN Nachricht. Hier wird die Taste nicht durch einen Virtual Keycode übergeben, sondern in Form eines ASCII Zeichens. Die WM_CHAR Nachricht wird nur bei Tasten gesendet, die sich als ASCII Zeichen darstellen lassen.

   case WM_CHAR:
      {
         switch (wParam)
         {

'\r' entspricht der Entertaste. Dies soll die Eingabe löschen, also setzen wir die Anzahl der aktuellen Zeichen auf Null. Dies bewirkt, dass beim nächsten Zeichenvorgang nichts gezeichnet wird. Mit InvalidateRect veranlassen wir das Neuzeichnen des Anwendungsbereiches, damit die vorherigen Zeichen gelöscht werden. In diesem Fall soll der Anwendungsbereich vor dem Zeichnen mit der Hintergrundfarbe ausgefüllt werden, deshalb geben wir im letzten Paramter der InvalidateRect Funktion ein TRUE an.

         case '\r':
            iActLen = 0;
            InvalidateRect(hWnd, NULL, TRUE);
            break;

Das '\b' Zeichen representiert die Backspachetaste. Wir subtrahieren iActLen also um eins. Natürlich nur, wenn Zeichen vorhanden sind, die man löschen kann (also iActLen größer als Null ist).

         case '\b':
            if (iActLen <= 0)
               break;
            
            iActLen--;
            InvalidateRect(hWnd, NULL, TRUE);
            break;

Die anderen Steuerzeichen ignorieren wir einfach ('\t' = Tabulator, '\n' = Zeilenvorschub, 27 = ASCII Code für Escape).

         case '\t':
         case '\n':
         case  27 :
            break;

Alle anderen Zeichen werden dem Buffer an der Position iActLen hinzugefügt. Natürlich nur, wenn wir noch freien Speicher in cBuffer besitzen. Wenn dem nicht so ist, wird die Eingabe einfach abgelehnt.

         default:
            if (iActLen < sizeof(cBuffer))
            {
               cBuffer[iActLen++] = wParam;
               InvalidateRect(hWnd, NULL, FALSE);
            }
            break;
         }
         return 0;
      }
   case WM_PAINT:
      {
         PAINTSTRUCT  ps;
         HDC          hDC;
         
         hDC = BeginPaint(hWnd, &ps);
         {

Die DrawText Funktion zeichnet den Text mit der entsprechenden Länge auf den Bildschirm. Jedoch kann man, wenn man das Fenster klein genug macht noch über den Rand des Fensters hinausschreiben.

       
            DrawText(hDC, cBuffer, iActLen, &rect, 
                          DT_SINGLELINE | DT_VCENTER);
         }
         EndPaint(hWnd, &ps);
         return 0;
      }

In der WM_KEYDOWN Nachricht wird geprüft, ob die Escapetaste gedrückt wurde, dies hätte man zwar auch in WM_CHAR machen können, aber so wird es nocheinmal wiederholt.

   case WM_KEYDOWN:
      {
         if (wParam == VK_ESCAPE)
         {

Wenn die Escapetaste gedrückt wurde, dann soll das Programm beendet werden. Dies erreichen wir, indem wir uns selber eine Nachricht vom Typ WM_CLOSE schicken. Wir bearbeiten die Nachricht zwar nirgend wo, aber die DefWindowProc Funktion wird alles weitere in die Wege leiten. So auch das Senden der Nachricht WM_DESTROY, der das Programm letztendlich beendet.

            SendMessage(hWnd, WM_CLOSE, 0, 0);
         }
         return 0;
      }
   case WM_DESTROY:
      {
         PostQuitMessage(0);
         return 0;
      }
   }
   return DefWindowProc(hWnd, message, wParam, lParam);
}

Das war's. Nun kannst du ganze Texte von der Tastatur einlesen. Zum Schluss erzähle ich dir noch ein bisschen über die Hintergründe der Tastenbearbeitung: Ein Computer hat ja bekannter Maßen nur eine Tastatur. Nun ist Windows aber ein Multitask fähiges Betriebssystem, das heisst, dass mehrere Programme gleichzeitig laufen können. Da man die Tastatur nicht auf die Programme aufteilen kann, muss man einen Mechanismus finden, der genau einem Programm (und sogar Fenster, aber dazu später mehr) die Tastatur zuteilt. Dies geschieht in Windows über den Fokus. Das Programm, das den Fokus hat, bekommt auch die Tastatur Nachrichten. Dieser Fokus wird durch die blaue Titelleiste und manchmal auch durch ein blinkendes Caret gekennzeichnet. Der blinkende Balken ist in Windows das Caret und nicht der Cursor, dies ist nämlich der Mousezeiger.

Übungen

  1. Schreibe ein Programm, das nur so viele Zeichen zur Eingabe akzeptiert, wie auch in den Anwendungsbereich des Fensters passen. Bedenke auch, dass man die Größe des Fensters verändern kann.
  2. Schreibe ein Programm, das als Eingabe nur Ziffern und mathematische Zeichen akzeptiert.

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