Iczelion - 07 - Maus Eingaben

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