xyblack技术地带

为了梦想奋斗...............

Fasm---Win32汇编学习8

    Fasm---Win32汇编学习8

在本节课中,我们将学习windows是如何处理键盘消息的。

理论:

    因为大多数pc机只有一个键盘,所以所有运行的windows程序都必须共用它。Windows负责把击键消息发送到具有输入焦点的应用程序中。尽管屏幕上可能存在多个程序窗口,但是一个时刻仅有一个窗口有输入焦点。有输入焦点的那个应用程序总是高亮显示的。实际上你可以从两个角度来看键盘消息:一是您可以把它看成是一大堆按键消息的集合,在这种情况下,当你按下一个键的时候,windows就会发送一个WM_KEYDOWN给有输入焦点的那个应用程序,提醒它有一个键被按下。当你释放一个键的时候,windows又会发送一个WM_KEYUP消息,告诉一个键被释放。你可以把每个键当成一个按钮;另一种情况是:你可以把键盘看成是字符输入的设备。当你按下一个字符'a'键的时候,windows会发送一个WM_CHAR消息给有输入焦点的应用程序,告诉它'a'键被按下。实际上windows内部发送WM_KEYDOWN和WM_KEYUP消息,而这些消息通过TranslateMessage翻译成WM_CHAR消息。windows窗口过程函数决定是否处理所收到的消息,一般来说你不大会去处理WM_KEYDOWN和WM_KEYUP消息,在消息循环中TranslateMessage会把上述消息转换成WM_CHAR消息。在我们的课程中只处理WM_CHAR消息。

 

 

format PE GUI 4.0
include 'win32ax.inc'
       
macro memmov [dst, src]
{
            common
            push [src]
            pop [dst]
}

;************************数据********************************
szClassName db 'first Windows',0
szWndName db '我的第一个程序',0
szCommand dd ?
char dd 20h
hIcon rd 1
hInstanse rd 1
hCursor rd 1
hWnd rd 1


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
mov [@wc.lpszMenuName], NULL
mov [@wc.lpszClassName], szClassName
;注册窗口类
invoke RegisterClassEx,addr @wc
;建立窗口

invoke CreateWindowEx, NULL, szClassName, szWndName,/
WS_OVERLAPPEDWINDOW,/
100, 100, 600, 400,/
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
local @rect: RECT
cmp [wMsg], WM_DESTROY
jz Quit
cmp [wMsg], WM_CHAR
jz key
cmp [wMsg], WM_PAINT
jz PAIT
invoke DefWindowProc,[hWnd],[wMsg],[wParam],[lParam]
ret


key:
push [wParam]
pop    [char]
invoke InvalidateRect,[hWnd],NULL,TRUE
jmp endWnd

PAIT:
invoke BeginPaint,[hWnd], addr @ps
mov [@hdc], eax ;保存设备环境
invoke TextOut,[@hdc],0,0,char,1
invoke EndPaint,[hWnd], addr @ps
jmp endWnd

Quit:
invoke PostQuitMessage,NULL
jmp endWnd


endWnd:
xor eax,eax
ret
endp


;///////////////////////////输入表////////////////////////////////////////////////


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'

我们现在先编译下这段程序代码,我们可以看到,我们按下键盘的某个键时,我们的窗口上就会显示某个键。这段代码其实和我们上节课程的代码也没什么太大的区别。我们来看。刚刚我们在上面已经说了,我们按下某个键的时候,Windows会发送WM_KEYDOWN和WM_KEYUP消息,然后我们通过消息循环获得消息后,然后此时通过TranslateMessage将我们上面的WM_KEYDOWN和WM_KEYUP消息翻译成WM_CHAR消息,我们一把你在窗口过程函数中处理的也就是我们的WM_CHAR消息。。

WM_CHAR消息的附件参数。wParam是我们的字符码,所以我们只要处理WM_CHAR消息的时候将这个字符码保存下来,然后通过InvalidateRect设置我们的窗口为无效区域,此时windows就会给我们的消息队列中发送WM_PAINT消息。之前上节课也已经说了。windows只要检测到相关窗口存在无效区域,则就会发送WM_PAINT消息。此时我们通过在WM_PAINT消息中设置无效区域为有效,就实现了重绘操作。

我们来看InvaildateRect函数

BOOL InvalidateRect(

HWND hWnd,    // handle of window with changed update region
CONST RECT *lpRect,    // address of rectangle coordinates
BOOL bErase    // erase-background flag
);   

 

首先第一个参数是窗口句柄,也就是要改变的更新的窗口。

第二阁参数是 一个Rect结构的指针,这个指我们客户区我们想要设置无效的一个正方形指针。如果该值设置为NULL的话,则整个客户区都无效。bErase参数指定我们是否要擦除背景。也就是windows在调用WM_PAINT消息的时候会通过BeginPaint函数时把背景擦除。我们此处的做法是通过:保存所有有关重绘客户区的数据,然后发送WM_PAINT消息,处理该消息的程序然后根据相关数据重新绘制客户区。尽管那么做事有点弓背,但是windows要处理那么庞大的消息群,没有一定的规矩可不行。实际上我们完全可以通过GetDc来获得设备环境的句柄,然后通在设备环境绘制字符,然后再用RealeateDc释放设备环境的句柄,但是这样有一个问题是,我们此时绘制字符完成后。但是如果之后windows接受到WM_PAINT消息的时候,显然我们绘制的字符也就消失了。为了让字符一直显示,我们必须把它放在WM_PAINT消息中处理。

invoke TextOut,[@hdc],0,0,char,1

在调用InvalidateRect时,WM_PAINT消息被发送到了WINDOWS窗口处理过程,程序流程转移到处理WM_PAINT消息的程序段,然后调用BeginPaint得到设备上下文的句柄,再调用TextOut在客户区的(0,0)处输出保存的按键字符。这样无论您按什么键都能在客户区的左上角显示,不仅如此,无论您怎么缩放窗口(迫使WINDOWS重新绘制它的客户区),字符都会在正确的地方显示,所以必须把所有重要的绘制动作都放到处理WM_PAINT消息的程序段中去。

阅读更多
个人分类: 汇编/逆向工程....
想对作者说点什么? 我来说一句

没有更多推荐了,返回首页

加入CSDN,享受更精准的内容推荐,与500万程序员共同成长!
关闭
关闭