Iczelion - 06 - Tastatureingaben

Tutorial 6: Tastatureingaben


Wir werden lernen, wie ein Windows-Programm Tastatureingaben entgegennimmt.

Laden Sie hier das Beispiel herunter.

Theorie:

Da normalerweise nur eine Tastatur an jedem PC angeschlossen ist, müssen sich alle laufenden Windows-Anwendungen diese teilen. Windows ist dafür verantwortlich, jeden Tastenanschlag an das Fenster zu senden welches gerade den Eingabefokus besitzt.

Obwohl verschiedene Fenster auf dem Schirm sein können, hat nur eines davon den Eingabefokus. Das Fenster, das den Eingabefokus hat, ist das einzige, welches Tastenanschläge empfangen kann. Sie können das Fenster, welches den Eingabefokus hat von anderen Fenster unterscheiden, indem Sie sich die Titelleiste anschauen. Die Titelleiste des Fensters, welches den Eingabefokus hat ist hervorgehoben.

Tatsächlich gibt es zwei grundlegende Typen von Tastatur-Nachrichten, je nach Sichtweise der Tastatur. Sie können ihre Tastatur als eine Ansammlung von Tasten betrachten. In diesem Fall, wenn eine Taste gedrückt wird, sendet Windows eine WM_KEYDOWN Nachricht an das Fenster, das den Eingabefokus besitzt, um ihm mizuteilen, dass eine Taste gedrückt wurde. Wenn Sie die Taste loslassen, sendet Windows eine WM_KEYUP Nachricht. Sie behandeln eine Taste wie einen Drückknopf. Eine andere Sichtweise der Tastatur ist, das sie ein Buchstaben-Eingabegerät ist. Wenn Sie die "a" Taste drücken, sendet Windows eine WM_CHAR Nachricht an das Fenster, das den Eingabefokus besitzt, um ihm mitzuteilen, dass der Benutzer den Buchstaben "a" gedrückt hat. In Wirklichkeit sendet Windows die Nachrichten WM_KEYDOWN und WM_KEYUP an das Fenster, das den Eingabefokus besitzt und diese Nachrichten werden als WM_CHAR Nachricht übersetzt, durch Aufruf von TranslateMessage. Die Fenster-Prozedur kann entscheiden, ob sie alle drei Nachrichten bearbeitet oder nur die, die von Interesse sind. In der Regel können Sie WM_KEYDOWN und WM_KEYUP ignorieren, da der Aufruf der TranslateMessage-Funktion in der Nachrichtenbearbeitungsschleife die Nachrichten WM_KEYDOWN und WM_KEYUP als WM_CHAR Nachricht übersetzt. Wir werden uns mit WM_CHAR in diesem Tutorial näher beschäftigen.

Beispiel:

.386 .model flat,stdcall option casemap:none WinMain proto :DWORD,:DWORD,:DWORD,:DWORD include \masm32\include\windows.inc include \masm32\include\user32.inc include \masm32\include\kernel32.inc include \masm32\include\gdi32.inc includelib \masm32\lib\user32.lib includelib \masm32\lib\kernel32.lib includelib \masm32\lib\gdi32.lib .data ClassName db "SimpleWinClass",0 AppName db "Our First Window",0 char WPARAM 20h ; der Buchstabe, den das Programm von der Tastatur erhält .data? hInstance HINSTANCE ? CommandLine LPSTR ? .code start: invoke GetModuleHandle, NULL mov hInstance,eax invoke GetCommandLine mov CommandLine,eax invoke WinMain, hInstance,NULL,CommandLine, SW_SHOWDEFAULT invoke ExitProcess,eax WinMain proc hInst:HINSTANCE,hPrevInst:HINSTANCE,CmdLine:LPSTR,CmdShow:DWORD LOCAL wc:WNDCLASSEX LOCAL msg:MSG LOCAL hwnd:HWND mov wc.cbSize,SIZEOF WNDCLASSEX mov wc.style, CS_HREDRAW or CS_VREDRAW mov wc.lpfnWndProc, OFFSET WndProc mov wc.cbClsExtra,NULL mov wc.cbWndExtra,NULL push hInst pop wc.hInstance mov wc.hbrBackground,COLOR_WINDOW+1 mov wc.lpszMenuName,NULL mov wc.lpszClassName,OFFSET ClassName invoke LoadIcon,NULL,IDI_APPLICATION mov wc.hIcon,eax mov wc.hIconSm,eax invoke LoadCursor,NULL,IDC_ARROW mov wc.hCursor,eax invoke RegisterClassEx, addr wc invoke CreateWindowEx,NULL,ADDR ClassName,ADDR AppName,\ WS_OVERLAPPEDWINDOW,CW_USEDEFAULT,\ CW_USEDEFAULT,CW_USEDEFAULT,CW_USEDEFAULT,NULL,NULL,\ hInst,NULL mov hwnd,eax invoke ShowWindow, hwnd,SW_SHOWNORMAL invoke UpdateWindow, hwnd .WHILE TRUE invoke GetMessage, ADDR msg,NULL,0,0 .BREAK .IF (!eax) invoke TranslateMessage, ADDR msg invoke DispatchMessage, ADDR msg .ENDW mov eax,msg.wParam ret WinMain endp WndProc proc hWnd:HWND, uMsg:UINT, wParam:WPARAM, lParam:LPARAM LOCAL hdc:HDC LOCAL ps:PAINTSTRUCT .IF uMsg==WM_DESTROY invoke PostQuitMessage,NULL .ELSEIF uMsg==WM_CHAR push wParam pop char invoke InvalidateRect, hWnd,NULL,TRUE .ELSEIF uMsg==WM_PAINT invoke BeginPaint,hWnd, ADDR ps mov hdc,eax invoke TextOut,hdc,0,0,ADDR char,1 invoke EndPaint,hWnd, ADDR ps .ELSE invoke DefWindowProc,hWnd,uMsg,wParam,lParam ret .ENDIF xor eax,eax ret WndProc endp end start


Analyse:

char WPARAM 20h ; der Buchstabe, den das Programm von der Tastatur erhält


Das ist die Variable, die den Buchstaben, der von der Tastatur empfangen wurde, speichert. Da der Buchstabe in WPARAM der Fenster-Prozedur übergeben wird, definieren wir die Variable einfachshalber auch vom Typen WPARAM. Der Initialisierungswert ist 20h, bzw. ein Leerzeichen, da wenn unser Fenster seine Client Area das erste Mal erneuert, noch keine Eingabe stattgefunden hat. Deshalb wollen wir statt dessen ein Leerzeichen anzeigen.

.ELSEIF uMsg==WM_CHAR push wParam pop char invoke InvalidateRect, hWnd,NULL,TRUE


Dies wird in die Fenster-Prozedur eingefügt um die WM_CHAR Nachricht zu behandeln. Es speichert den Buchstaben lediglich in einer Variable names "char" und ruft dann InvalidateRect auf. InvalidateRect erklärt das spezifizierte Rechteck in der Client Area für ungültig, wodurch Windows dazu gezwungen wird eine WM_PAINT Nachricht an die Fenster-Prozedur zu senden. Die Syntax ist wie folgt:

InvalidateRect proto hWnd:HWND,\ lpRect:DWORD,\ bErase:DWORD


lpRect ist ein Zeiger auf das Rechteck in der Client Area, welches wir für ungültig erklären wollen. Falls dieser Parameter gleich null ist, wird die gesamte Client Area als ungültig markiert.

bErase ist ein Flag, das Windows mitteilt, ob es den Hintergrund löschen muss. Falls dieses Flag gleich TRUE ist, dann wird Windows den Hintergrund des ungültigen Rechtecks löschen, sobald BeginPaint aufgerufen wird.

Die Strategie, die wir hier benutzen ist folgende: wir speichern alle notwendigen Informationen bezüglich des Zeichnens der Client Area und erzeugen eine WM_PAINT Nachricht, um die Client Area zu zeichnen. Natürlich muss der Code in der WM_PAINT Sektion vorher wissen, was von ihm erwartet wird. Das sieht nach einem umständlichen Weg aus, aber es ist der Weg von Windows.

Eigentlich können wir die Client Area während der Abarbeitung der WM_CHAR Nachricht zeichnen, indem wir GetDC und ReleaseDC aufrufen. Da besteht kein Problem. Aber der Spass beginnt, wenn unser Fenster seine Client Area neuzeichnen soll. Da der Code, der den Buchstaben zeichnet, in der WM_CHAR Sektion steht, ist unsere Fenster-Prozedur nicht in der Lage unseren Buchstaben in der Client Area neuzuzeichnen. Also kommen wir zu folgendem Entschluss: Packen Sie alle benötigten Daten und Code in WM_PAINT. Sie können eine WM_PAINT Nachricht senden, wo Sie auch gerade sind im Code, jederzeit wenn Sie die Client Area neuzeichnen wollen.

invoke TextOut,hdc,0,0,ADDR char,1


Wenn InvalidateRect aufgerufen wird, sendet es eine WM_PAINT Nachricht zurück an die Fenster-Prozedur. So wird der Code in der WM_PAINT Sektion aufgerufen. Er ruft wie gewöhnlich BeginPaint auf, um ein Handle auf den Device Context zu bekommen und ruft dann TextOut auf, was den Buchstaben bei x=0, y=0 in der Client Area ausgibt. Wenn Sie das Programm ausführen und irgend eine Taste drücken, werden Sie das Echo des Buchstaben in der oberen linken Ecke des Fensters sehen. Und wenn Sie das Fenster minimieren und danach wieder maximieren, wird der Buchstabe immer noch da sein, da der gesamte Code und die notwendigen Daten zum neuzeichnen in der WM_PAINT Sektion gesammelt sind.


Deutsche Übersetzung: Joachim Rohde
Die original Win32Asm-Tutorials stammen von Iczelion's Win32 Assembly HomePage