Win32 汇编学习 (7):鼠标输入消息

本贴最后更新于 2236 天前,其中的信息可能已经时移俗易

这次我们将学习如何在我们的窗口过程函数中处理鼠标按键消息。例子演示了如何等待鼠标左键按下消息,我们将在按下的位置显示一个字符串。

理论:

和处理键盘输入一样,WINDOWS 将捕捉鼠标动作并把它们发送到相关窗口。这些活动包括左、右键按下、移动、双击、滚轮消息 WM_WHEEL 等。WINDOWS 并不像处理键盘输入那样把所有的鼠标消息都导向有输入焦点的窗口,任何鼠标经过的窗口都将接收到鼠标消息,无论有否输入焦点。另外,窗口还会接收到鼠标在非客户区移动的消息(WM_NCMOVE),但大多数的情况下我们都会将其忽略掉。当鼠标在某窗口客户区移动时,该窗口将接收到 WM_MOUSEMOVE 消息。一个窗口若想处理 WM_LBUTTONDBCLKWM_RBUTTONDBCLK,那么它的窗口类必须有 CS_DBLCLKS 风格,否则它就会接受到一堆的按键起落(WM_XBUTTONDOWNWM_XBUTTONUP)的消息。 对于所有的消息,窗口过程函数传入的参数 lParam 包含了鼠标的位置,其中低位为 x 坐标,高位为 y 坐标,这些坐标值都是相对于窗口客户区的左上角的值,wParam 中则包含了鼠标按钮的状态。

例子:

.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

.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 

分析:

.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 

窗口过程处理了 WM_LBUTTONDOWN 消息,当接收到该消息时,lParam 中包含了相对于窗口客户区左上角的坐标,我们把它保存下来,放到一个结构体变量(POINT)中,该结构体变量的定义如下:

POINT STRUCT 
    x   dd ? 
    y   dd ? 
POINT ENDS 

然后我们把标志量 MouseClick 设为 TRUE,这表明至少有一次在客户区的左键按下消息。

        mov eax,lParam 
        and eax,0FFFFh 
        mov hitpoint.x,eax 

由于 lParam 是一个 32 位长的数,其中高、低 16 位分别包括了 y、x 坐标所以我们做一些小处理,以便保存它们。

        shr eax,16 
        mov hitpoint.y,eax 

保存完坐标后我们设标志 MouseClickTRUE,这是在处理 WM_PAINT 时用来判断是否有鼠标左键按下消息。然后我们调用 InvalidateRect 函数迫使 WINDOWS 重新绘制客户区。

        .IF MouseClick 
            invoke lstrlen,ADDR AppName 
            invoke TextOut,hdc,hitpoint.x,hitpoint.y,ADDR AppName,eax 
        .ENDIF 

绘制客户区的代码首先检测 MouseClick 标志位,再决定是否重绘。因为我们在首次显示窗口时还没有左键按下的消息,所以我们在初始时把该标志设为 FALSE,告诉 WINDOWS 不要重绘客户区,当有左键按下的消息时,它会在鼠标按下的位置绘制字符串。注意在调用 TextOut 函数时,其关于字符串长度的参数是调用 lstrlen 函数来计算的。

  • B3log

    B3log 是一个开源组织,名字来源于“Bulletin Board Blog”缩写,目标是将独立博客与论坛结合,形成一种新的网络社区体验,详细请看 B3log 构思。目前 B3log 已经开源了多款产品:SymSoloVditor思源笔记

    1090 引用 • 3467 回帖 • 297 关注
  • asm
    15 引用 • 5 回帖
  • 读书笔记
    8 引用 • 3 回帖
  • Windows

    Microsoft Windows 是美国微软公司研发的一套操作系统,它问世于 1985 年,起初仅仅是 Microsoft-DOS 模拟环境,后续的系统版本由于微软不断的更新升级,不但易用,也慢慢的成为家家户户人们最喜爱的操作系统。

    215 引用 • 462 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...