Tutorial 7: Maus Eingaben
Wir werden lernen, wie man Eingaben mit der Maus in unserer Fenster-Prozedur empfängt und verarbeitet. Das Beispielprogramm wird auf das Drücken der linken Maustaste warten und an genau dieser Stelle einen Text-String in der Client Area ausgeben.
Laden Sie hier das Beispiel herunter.
Theorie:
Wie bei Tastatureingaben, Windows erkennt und sendet Meldungen über Maus-Aktivitäten welche relevant fürs jeweilige Fenster ist. Diese Aktivitäten beinhalten linke und rechte Mausklicks, Bewegung des Mauscursors über das Fenster, Doppelklicks. Anders als bei Tastatureingaben, welche an das Fenster gesendet werden, welches den Eingabefokus besitzt, Maus-Nachrichten werden zu jedem Fenster gesendet, über das der Mauscursor ist, egal ob dieses aktiv ist oder nicht. Zusätzlich gibt es auch Maus-Nachrichten für den Bereich, der nicht zur Client Area gehört. Aber in der Regel können wir diese glücklicherweise ignorieren. Wir können uns auf die konzentrieren, die sich auf die Client Area beziehen.
Es gibt zwei Nachrichten für jeden Maustaste: WM_LBUTTONDOWN, WM_RBUTTONDOWN und WM_LBUTTONUP, WM_RBUTTONUP. Für eine Maus mit drei Tasten gibt es zudem noch WM_MBUTTONDOWN und WM_MBUTTONUP. Wenn der Mauscursor sich über der Client Area bewegt, sendet Windows eine WM_MOUSEMOVE Nachricht an das Fenster unter dem Cursor.
Ein Fenster kann Doppelklick Nachrichten empfangen, WM_LBUTTONDBCLK oder WM_RBUTTONDBCLK,
wenn und nur wenn die Fensterklasse das CS_DBCLKS-Stil Flag hat, ansonsten empfängt das Fenster nur eine Serie von Maustaste 'gedrückt und wieder losgelassen' Nachrichten.
Für all diese Nachrichten enthält der Wert von lParam die Position des Mauscursors. Das untere Word enthält die X-Koordinate und das obere Word die Y-Koordinate relativ zu oberen linken Ecke der Client Area des Fensters. wParam indiziert den Status der Maustasten und der Shift und Strg Tasten.
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
MouseClick db 0 ; 0=kein Klick bisher
.data?
hInstance HINSTANCE ?
CommandLine LPSTR ?
hitpoint POINT
.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 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_LBUTTONDOWN
mov eax,lParam
and eax,0FFFFh
mov hitpoint.x,eax
mov eax,lParam
shr eax,16
mov hitpoint.y,eax
mov MouseClick,TRUE
invoke InvalidateRect,hWnd,NULL,TRUE
.ELSEIF uMsg==WM_PAINT
invoke BeginPaint,hWnd, ADDR ps
mov hdc,eax
.IF MouseClick
invoke lstrlen,ADDR AppName
invoke TextOut,hdc,hitpoint.x,hitpoint.y,ADDR AppName,eax
.ENDIF
invoke EndPaint,hWnd, ADDR ps
.ELSE
invoke DefWindowProc,hWnd,uMsg,wParam,lParam
ret
.ENDIF
xor eax,eax
ret
WndProc endp
end start
Analyse:
.ELSEIF uMsg==WM_LBUTTONDOWN
mov eax,lParam
and eax,0FFFFh
mov hitpoint.x,eax
mov eax,lParam
shr eax,16
mov hitpoint.y,eax
mov MouseClick,TRUE
invoke InvalidateRect,hWnd,NULL,TRUE
Die Fenster-Prozedur wartet auf einen Klick der linken Maustaste. Wenn Sie WM_LBUTTONDOWN erhält, enthält lParam die Koordinaten des Mauscursors in der Client-Area. Die Koordinaten werden in einer Variable des Typen POINT gespeichert, welcher folgendermaßen definiert ist:
POINT STRUCT
x dd ?
y dd ?
POINT ENDS
und setzt das Flag, MouseClick, auf TRUE, was bedeutet, dass mindestens ein Links-Mausklick in der Client-Area getätigt wurde.
mov eax,lParam
and eax,0FFFFh
mov hitpoint.x,eax
Da die X-Koordinate im unteren Word von lParam ist und die Elemente der POINT-Struktur alle 32-bit groß sind, müssen wir das obere Word von EAX mit Nullen belegen, bevor wir es in hintpoint.x speichern.
shr eax,16
mov hitpoint.y,eax
Da die Y-Koordinate im oberen Word von lParam enthalten ist, müssen wir es ins untere Word von EAX kopieren, bevor wir es in hitpoint.y speichern. Wir tun das, indem wir EAX um 16 Bit nach rechts schieben. Nachdem wir die Mausposition gespeichert haben, setzen wir das Flag MouseClick auf TRUE, damit der Code zum Zeichnen in der WM_PAINT Sektion weiss, dass zumindest ein Klick in der Client Area stattgefunden hat und den String an der Mausposition ausgeben kann. Als nächstes rufen wir die InvalidateRect-Funktion auf, um das Fenster zu einem Neuzeichnen seiner kompletten Client Area zu zwingen.
.IF MouseClick
invoke lstrlen,ADDR AppName
invoke TextOut,hdc,hitpoint.x,hitpoint.y,ADDR AppName,eax
.ENDIF
Der Code zum Zeichnen in der WM_PAINT Sektion muss überprüfen, ob MouseClick gleich TRUE ist, da wenn das Fenster erzeugt wird, es eine WM_PAINT Nachricht erhält, zu dessen Zeitpunkt noch kein Mausklick aufgetreten ist und somit auch kein String in die Client Area gezeichnet werden sollte. Wir initialisieren MouseClick mit FALSE und ändern diesen Wert auf TRUE wenn ein Mausklick auftritt.
Wenn mindestens ein Mausklick aufgetreten ist, wird der String an der Mausposition in die Client Area gezeichnet. Beachten Sie, dass lstrlen aufgerufen wird, um die Länge des anzuzeigenden Strings zu ermitteln und die Länge als letzter Parameter an die TextOut-Funktion gesendet wird.
Deutsche Übersetzung: Joachim Rohde
Die original Win32Asm-Tutorials stammen von Iczelion's Win32 Assembly HomePage