WIN32汇编语言教程:第04章 第一个窗口程序 · 4.4 实验(2)
该子程序中首先用FindWindow函数来查找记事本程序是否已经运行,记事本程序的窗口类名称为“Notepad”,FindWindow可以用窗口类当做第一个参数来查找,如果找到,返回的是记事本程序的主窗口句柄,否则返回0。
要发送的是模拟键盘按键的消息WM_CHAR,这样就好像在记事本中人工键入字符,但直接向记事本主窗口发送WM_CHAR消息是不行的,要向记事本窗口客户区中的编辑子窗口发送消息才行,所以程序中又用从位置获取子窗口句柄的函数ChildWindowFromPoint来获得编辑子窗口的句柄。
锁定了最后的目标即记事本中的编辑子窗口后,程序用PostMessage向它发送消息,根据字符串的长度,用一个循环每次发送一个WM_CHAR消息,WM_CHAR消息的wParam和lParam的含义是:
wParam = chCharCode // wParam是键值
lParam = lKeyData // lParam是键数据(重复次数)
程序中用mov eax,al将键值扩展到参数所需的32位,当做wParam参数发送,lParam为1,表示键的重复次数为1次,这样一来,记事本中就源源不断地显示出MsgWindow程序的运行轨迹了。
MsgWindow程序增加的第三部分是在每个函数的前后增加了显示状态的语句,它们只是简单地把一个字符串发送到记事本中去:
;定义一些字符串
szCreateWindow1 db 'Creating Window...',0dh,0 szCreateWindow2 db 'CreateWindow end',0dh,0 szShowWindow1 db 'Showing Window...',0dh,0 szShowWindow2 db 'ShowWindow end',0dh,0 szUpdateWindow1 db 'Updating Window...',0dh,0 szUpdateWindow2 db 'UpdateWindow end',0dh,0 szGetMsg1 db 'Getting Message...',0dh,0 szGetMsg2 db '[%04x]Message gotten',0dh,0 szDispatchMsg1 db 'Dispatching Message...',0dh,0 szDispatchMsg2 db 'DispatchMessage end',0dh,0 ... invoke _SendtoNotepad,addr szCreateWindow1 invoke CreateWindowEx,... invoke _SendtoNotepad,addr szCreateWindow2 invoke _SendtoNotepad,addr szShowWindow1 invoke ShowWindow,hWinMain,SW_SHOWNORMAL invoke _SendtoNotepad,addr szShowWindow2 invoke _SendtoNotepad,addr szUpdateWindow1 invoke UpdateWindow,hWinMain invoke _SendtoNotepad,addr szUpdateWindow2 ...
上面代码中的粗体部分就是相对于FirstWindow程序增加的内容,好了,现在DOS控制台上键入nmake将MsgWindow程序编译出来,然后打开记事本,再运行MsgWindow.exe,如果记事本上出现一大堆的东西,就说明实验可以开始了!
4.4.2 开始实验
实验1. 验证收到消息的顺序
打开记事本,然后运行MsgWindow程序,记事本上出现的内容为:
Creating Window... WndProc: [0024]WM_GETMINMAXINFO 00000000 0012fda4 WndProc: [0081]WM_NCCREATE 00000000 0012fd8c WndProc: [0083]WM_NCCALCSIZE 00000000 0012fdc4 WndProc: [0001]WM_CREATE 00000000 0012fd68 CreateWindow end Showing Window... WndProc: [0018]WM_SHOWWINDOW 00000001 00000000 WndProc: [0046]WM_WINDOWPOSCHANGING 00000000 0012fec0 WndProc: [0046]WM_WINDOWPOSCHANGING 00000000 0012fec0 WndProc: [001c]WM_ACTIVATEAPP 00000001 00000450 WndProc: [0086]WM_NCACTIVATE 00000001 00000000 WndProc: [000d]WM_GETTEXT 000001fe 0012f52c WndProc: [0006]WM_ACTIVATE 00000001 00000000 WndProc: [0007]WM_SETFOCUS 00000000 00000000 WndProc: [0085]WM_NCPAINT 00000001 00000000 WndProc: [000d]WM_GETTEXT 000001fe 0012f52c WndProc: [0014]WM_ERASEBKGND e3010449 00000000 WndProc: [0047]WM_WINDOWPOSCHANGED 00000000 0012fec0 WndProc: [0005]WM_SIZE 00000000 00450064 WndProc: [0003]WM_MOVE 00000000 004b0038 ShowWindow end Updating Window... WndProc: [000f]WM_PAINT 00000000 00000000 UpdateWindow end Getting Message...
以WndProc带头的是在窗口过程中收到的消息,显然,和4.2.4节中讲述的是一致的,在调用CreateWindowEx的时候,窗口过程就开始接收消息,里面有重要的WM_CREATE,然后在ShowWindow的时候,Windows向窗口过程发送了很多的消息,而UpdateWindow只给窗口过程发送了一条WM_PAINT消息,接下来就进入了消息循环。
可以看到,GetMessage函数是程序主动上交空闲时间的办法之一,因为显示出Getting Message....以后,程序就等着那里了,这表示程序的空闲时间并不浪费在消息循环中,而是在GetMessage函数内部由Windows自己分配了。
接下来把鼠标移过MsgWindow窗口,在记事本上看到了什么?用户一个小小的动作就够窗口过程忙的——我们看到了多次重复的下列内容:
WndProc: [0084]WM_NCHITTEST 00000000 00830096 WndProc: [0020]WM_SETCURSOR 001b0304 02000001 [0200]Message gotten Dispatching Message... WndProc: [0200]WM_MOUSEMOVE 00000000 0038005e DispatchMessage end Getting Message...
首先,Windows在GetMessage没有返回的时候就调用了两次窗口过程,分别是处理WM_NCHITTEST和WM_SETCURSOR,它们并不经过消息循环;然后,GetMessage取到[0200]消息并返回,0200是WM_MOUSEMOVE消息的编号;接下来,DispatchMessage函数开始工作,在这个函数的内部,消息被Windows发送给窗口过程处理,最后DispatchMessage返回,然后开始新的GetMessage。
最后在MsgWindow上单击“关闭”按钮,看发生了什么:
[00a1]Message gotten Dispatching Message... WndProc: [00a1]WM_NCLBUTTONDOWN 00000014 003d0097 WndProc: [0215]WM_CAPTURECHANGED 00000000 00000000 WndProc: [0112]WM_SYSCOMMAND 0000f060 003d0097 WndProc: [0010]WM_CLOSE 00000000 00000000 WndProc: [0046]WM_WINDOWPOSCHANGING 00000000 0012fad8 WndProc: [0047]WM_WINDOWPOSCHANGED 00000000 0012fad8 WndProc: [0086]WM_NCACTIVATE 00000000 00000000 WndProc: [0006]WM_ACTIVATE 00000000 00000000 WndProc: [001c]WM_ACTIVATEAPP 00000000 00000450 WndProc: [0008]WM_KILLFOCUS 00000000 00000000 WndProc: [0002]WM_DESTROY 00000000 00000000 WndProc: [0082]WM_NCDESTROY 00000000 00000000 DispatchMessage end Getting Message... [0012]Message gotten
GetMessage收到的是按下鼠标的WM_NCLBUTTONDOWN的消息,由DispatchMessage转给窗口过程处理后,窗口过程将它转手给了DefWindowProc,DefWindowProc根据鼠标的位置得出结论:用户按的是“关闭”按钮,放开鼠标后,它就给窗口过程发送WM_CLOSE消息,当窗口过程调用DestroyWindow后,窗口被摧毁,窗口过程最后收到的是WM_DESTROY消息和WM_NCDESTROY消息,而消息循环中GetMessage最后收到的是0012号WM_QUIT消息,消息循环结束。