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.
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.