Fasm---Win32汇编学习9
本课中我们将学习如何在我们的窗口过程函数中处理鼠标按键消息。示例程序演示了如何等待左键按下消息,我们将在按下的位置显示一个字符串。
和处理键盘输入一样,WINDOWS将捕捉鼠标动作并把它们发送到相关窗口。这些活动包括左、右键按下、移动、双击等(译者注:新式鼠标还包括滚轮消息WM_WHEEL)。WINDOWS并不像处理键盘输入那样把所有的鼠标消息都导向有输入焦点的窗口,任何鼠标经过的窗口都将接收到鼠标消息,无论有否输入焦点。另外,窗口还会接收到鼠标在非客户区移动的消息(WM_NCMOVE),但大多数的情况下我们都会将其忽略掉。 对鼠标的每一个按钮都有两个消息:WM_LBUTTONDOWN,WM_RBUTTONDOWN 。对于三键鼠标还会有WM_MBUTTONDOWN和WM_MBUTTONUP消息,当鼠标在某窗口客户区移动时,该窗口将接收到WM_MOUSEMOVE消息。一个窗口若想处理WM_LBUTTONDBCLK或 WM_RBUTTONDBCLK,那么它的窗口类必须有CS_DBLCLKS风格,否则它就会接受到一堆的按键起落(WM_XBUTTONDOWN或WM_XBUTTONUP)的消息。 对于所有的消息,窗口过程函数传入的参数lParam包含了鼠标的位置,其中底位为x座标,高位为y座标,这些座标值都是相对于窗口客户区的左上角的值,wParam中则包含了鼠标按钮的状态。
format PE GUI 4.0
include 'win32ax.inc'
;************************代码********************************
struct MPOINT
x dd ?
y dd ?
ends
macro memmov [dst, src]
{
common
push [src]
pop [dst]
}
entry $
invoke GetModuleHandle,NULL
mov [hInstanse], eax
invoke GetCommandLine,NULL
mov [szCommand], eax
stdcall _WinMain,hInstanse, NULL, [szCommand], SW_SHOWDEFAULT
invoke ExitProcess,NULL
proc _WinMain hInstance:DWORD, hPrevInstance:DWORD, lpCmdLine:DWORD, nCmdShow:DWORD
local @wc : WNDCLASSEX
local @msg : MSG
invoke RtlZeroMemory,addr @wc,sizeof.WNDCLASSEX
invoke LoadIcon,NULL, IDI_WINLOGO
mov [hIcon], eax
invoke LoadCursor,NULL, IDC_ARROW
mov [hCursor], eax
mov [@wc.cbSize], sizeof.WNDCLASSEX
mov [@wc.style], CS_HREDRAW or CS_VREDRAW
mov [@wc.lpfnWndProc], _WndProc
mov [@wc.cbClsExtra], NULL
mov [@wc.cbWndExtra], NULL
memmov @wc.hInstance, hInstance
memmov @wc.hIcon, hIcon
memmov @wc.hCursor, hCursor
mov [@wc.hbrBackground], COLOR_WINDOW+1
mov [@wc.lpszMenuName], NULL
mov [@wc.lpszClassName], szClassName
;注册窗口类
invoke RegisterClassEx,addr @wc
;建立窗口
invoke CreateWindowEx, NULL, szClassName, szWndName,/
WS_OVERLAPPEDWINDOW,/
CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,/
NULL, NULL, [hInstanse], NULL
mov [hWnd], eax
invoke ShowWindow,[hWnd],SW_SHOWNORMAL
invoke UpdateWindow,[hWnd]
GetMsg:
invoke GetMessage,addr @msg, NULL, 0, 0
or eax, eax
jz EndMsg
invoke TranslateMessage,addr @msg
invoke DispatchMessage,addr @msg
jmp GetMsg
EndMsg:
mov eax, [@msg.wParam]
ret
endp
proc _WndProc uses ebx esi edi,hWnd:DWORD, wMsg:DWORD, wParam:DWORD, lParam:DWORD
local @hdc:DWORD
local @ps : PAINTSTRUCT
cmp [wMsg], WM_DESTROY
je Quit
cmp [wMsg], WM_LBUTTONDOWN
je LBUTTON
cmp [wMsg], WM_PAINT
je PAIT
invoke DefWindowProc,[hWnd],[wMsg],[wParam],[lParam]
ret
LBUTTON:
mov eax, [lParam]
and eax, 0000FFFFh ;使其高2个字节为0。因为高两个字节为y座标。
mov [MousePoint.x], eax
mov eax, [lParam]
shr eax, 16 ;向右移动2个字节,此时记录为我们的Y座标
mov [MousePoint.y], eax
mov [MouseClick], TRUE
invoke InvalidateRect,[hWnd], NULL, TRUE
jmp endWnd
PAIT:
invoke BeginPaint,[hWnd], addr @ps
mov [@hdc], eax ;保存设备环境
cmp [MouseClick], 0
je _exit
invoke TextOut,[@hdc],DWORD [MousePoint.x],DWORD [MousePoint.y], szClassName,13
_exit:
invoke EndPaint,[hWnd], addr @ps
jmp endWnd
Quit:
invoke PostQuitMessage,NULL
endWnd:
xor eax,eax
ret
endp
;///////////////////////////数据////////////////////////////////////////////////
szClassName db 'first Windows',0
szWndName db '我的第一个程序',0
szCommand dd ?
hIcon rd 1
hInstanse rd 1
hCursor rd 1
hWnd rd 1
MousePoint MPOINT <>
MouseClick db 0
section '.import' data import readable writeable
library kernel32, 'kernel32.dll',/
user32, 'user32.dll',/
gdi32, 'gdi32.dll'
include 'api/kernel32.inc'
include 'api/user32.inc'
include 'api/gdi32.inc'
分析:
窗口过程处理我们的WM_LBUTTONDOWN,因为在上面说了,任何鼠标经过的窗口都将接收到鼠标消息。窗口还会接收到鼠标在非客户区移动的消息(WM_NCMOVE),但大多数情况下都将其忽略。对鼠标的每一个按钮有两个消息。WM_LBUTTONDOWN和WM_RBUTTONDOWN消息。对于三键鼠标还有WM_MBUTTONDOWN和WM_MBUTTONUP消息。当鼠标在某窗口客户区移动时,鼠标接受到WM_MOUSEMOVE消息。一个窗口若想处理WM_LBUTTONCLICK和WM_RBUTTONCLICK消息的话,则窗口类必须有CS_DBLCLKS风格。否则它会接受一堆按键起落的消息。(WM_XBUTTONDOWN或WM_XBUTTONUP)的消息。 对于所有的消息,窗口过程函数传入的参数lParam包含了鼠标的位置,其中底位为x座标,高位为y座标,这些座标值都是相对于窗口客户区的左上角的值,wParam中则包含了鼠标按钮的状态。
那么我们很明白了。对于鼠标的消息,那么窗口过程函数传入的参数lParam参数包含了鼠标的位置,其中低位为x座标,高位为y座标。这些座标值都是相对于窗口客户区左上角。wParam中则包含了鼠标按钮的状态。那么我们的程序要实现的就是在鼠标移动到任意位置后,左键按下后,在我们的窗口客户区显示一个字符串。
那么很显然,我们在处理WM_LBUTTONDOWN 的时候只要将鼠标的X和Y座标保存下来,然后这时候调用InvalidateRect函数设置我们的窗口客户区为无效区域,此时windows就会给我们的程序发送WM_PAINT消息,此时我们在WM_PAINT消息中做一个判断就是我们的程序到底是否触发了WM_LBUTTONDOWN消息,那么如何判断,当然需要我们在处理WM_LBUTTONDOWN消息的时候做一个标记。。
很简单,大家看代码,很多我已经标示注释了。
mov eax, [lParam]
and eax, 0000FFFFh ;使其高2个字节为0。因为高两个字节为y座标。
mov [MousePoint.x], eax
mov eax, [lParam]
shr eax, 16 ;向右移动2个字节,此时记录为我们的Y座标
mov [MousePoint.y], eax
mov [MouseClick], TRUE ;设置标记为TRUE,等下在WM_PAINT消息中要判断。
invoke InvalidateRect,[hWnd], NULL, TRUE
jmp endWnd
PAIT:
invoke BeginPaint,[hWnd], addr @ps
mov [@hdc], eax ;保存设备环境
cmp [MouseClick], 0
je _exit
invoke TextOut,[@hdc],DWORD [MousePoint.x],DWORD [MousePoint.y], szClassName,13
_exit:
invoke EndPaint,[hWnd], addr @ps
jmp endWnd
首先BeginPaint取得设备环境的句柄。因为我们程序需要通过设备环境来实现重绘。说白了设备环境就是管理设备的工具。我们就是在设备环境上来进行绘图。 然后取得设备环境的句柄后,通过GDI函数来进行访问以及设置。说白了GDI函数就是通过操作设备环境的。
好,我们此时保存后,进行判断我们之前的标记。因为我们在处理WM_LBUTTONDOWN消息的时候将MouseClick设置为true。所以这时我们只要判断它是否为0,就可以了。如果为0,则说明没有处理WM_LBUTTONDOWN消息,然后直接跳到EndPaint来关闭设备环境的句柄。 如果不为0,说明处理WM_LBUTTONDOWN消息了,那么通过Text输出我们的字符串,为了保险,我们在输出完在将其MouseClick设置为0。 从而进行下一次的循环。。