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