第四章 第一个窗口程序 1 (windows的消息机制)

  • 格式:doc
  • 大小:48.00 KB
  • 文档页数:8

标 题: 【原创】windows下32位汇编语言学习笔记 第四章 第一个窗口程序 (windows的消息机制) 作 者: jasonnbfan 时 间: 2009-05-05,22:59:57 链 接: http://bbs.pediy.com/showthread.php?t=88027

windows下32位汇编语言学习笔记 第四章 第一个窗口程序 1 (windows的消息机制) FirstWindow程序代码很简单,只有一个地方要说下 _WinMain 函数里的下面2行代码, 把当前进程句柄赋值给WNDCLASSEX的hInstance,这里不能使用mov @stWndClass.hInstance,hInstance,因为mov指令不支持2块内从间的直接赋值。 所以先把hInstance压栈再弹出到@stWndClass.hInstance push hInstance pop @stWndClass.hInstance

当然也可以这样做 mov ecx,hInstance mov @stWndClass.hInstance,ecx

WIN32的消息机制 windows系统是一个消息驱动的OS,操作通过处理各种消息来响应用户的操作。从第一个windows程序就可以看出来,大部分的代码都是处理消息的。要开发windows程序,不管你用什么开发工具什么语言,掌握消息机制的原理都是非常必要的。

对于每一个带有窗口的线程,系统都会给他非配一个自己的消息队列,用于处理消息派送(Dispatch)。每个线程都用自己的消息循环来接受消息。每个线程列队默认管理最大10000个消息,修改注册表下面的键值可以修改列队中的消息数。建议的最小值是4000 HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows NT\CurrentVersion\Windows\USERPostMessageLimit.

线程列队不是一个公开的数据结构(THREADINFO),其中包括登记消息队列(Posted-message queue),消息发送队列(Send-message queue),消息应答队列(reply-message queue),虚拟输入队列(virtualized-input queue),唤醒标志(wake flag),以及用来扫描线程局部输入状态的若干变量。(Windows核心编程)

消息列队提取优先级 1.检查QS_SENDMESSAGE 标志 GetMessage 不处理Send消息,如果队列中没有其他send消息,关闭QS_SENDMESSAGE标志,GetMessage()不返回检查其他消息。

2.检查QS_POSTMESSAGE 标志 GetMessage 从此列队取出消息处理并由DisPatch分发到指定窗口回调函数处理。GetMessage返回True,没有其他post消息关闭标志。

3.检查QS_QUIT 标志 如果被PostQuitMessage()打开,则GetMessage返回False退出消息循环,并且关闭QS_QUIT标志 4.检查QS_INPUT 标志 GetMessage 从此列队取出消息处理由TranslateMessage()处理键盘鼠标消息,然后由DisPatch分发到指定窗口回调函数处理没有其他消息关闭标志.

5.检查QS_PAINT 标志 处理同2 6.检查QS_TIME 标志 首先复位计时器,GetMessage返回True,如果没有计数器,关闭QS_TIME标志。 优先级很清楚,send优先级最高,最低的是time。 Windows定义了很多消息都以WM_开头,都是用#DEFINE 定义的常量,用户可以定义自己的消息,windows规定用户的消息从WM_USER 0x0400开始。

BOOL PostMessage(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam); 往进程的消息列队发送消息PostMessage,这个函数往指定进程的消息列队发送一个消息,发送完毕立即返回。调用函数无法知道发送的消息是否能被处理。如果这个指定窗口未处理完自己消息列队的所有消息前就推出了,就会处理不到post的消息。

PostMessage发送的消息参数不能包含指针参数,MSDN的说明是: “如果发送一个低于WM_USER范围的消息给异步消息函数(PostMessage.SendNotifyMessage,SendMesssgeCallback),消息参数不能包含指针。否则,操作将会失败。函数将再接收线程处理消息之前返回,发送者将在内存被使用之前释放。”

我的理解是,就算目标进程知道你发来的是个指针地址,但是2个进程之间的寻址空间是独立的,互相不可访问,怎么能获取发送进程内存空间里的数据呢?

可以试下,把例子Send.asm修改一下看看。 WM_USER 值是 0x0400,用户自定义消息从这里开始,前面的是windows保留的消息。这里必须把WM_SETTEXT消息改成自定义消息,否则PostMessage根本不会发送WM_SETTEXT消息给目标线程。 invoke PostMessage,hWnd,WM_USER + 1,0,addr szText

Receive.asm修改接受WM_SETTEXT消息的代码改成这样: eax == WM_USER + 1 invoke MessageBox,hWnd,addr lParam,addr szCaptionMain,MB_OK

用OD打开Receive.exe 运行在 0040102B处下段,运行send.exe,发送之前提示的地址(00402072)到了Receive,windows干脆直接把你传递的指针地址当做DWORD类型处理了,显示 "r @", 是00402072的ASCII码值,就算当成指针处理,00402072也是指向Receive内存空间里的,不可能是指向send内存空间里的数据。

关于WM_QUIT消息 窗口回调函数里不可能接到WM_QUIT消息。因为从消息列队里GetMessage()收到WM_QUIT消息就返回0,消息循环就会结束,所以DispatchMessage()也不可能再把这个消息分发到窗口的回调函数。这就是为什么,书里一再强调要在WM_DESTORY的消息事件里加上PostQuitMessage()的原因。如果不加,程序只是销毁窗口,但是进程任然存在。消息循环还在运行,但是因为窗口已经销毁,所以他不可能再从消息列队里取得任何消息。

使用PostQuitMessage()与PostMessage()发送消息的不同 前者把消息列队里的QS_QUIT标志打开,并且等待程序处理完消息列队里的所有消息后才结束消息循环。 后者是把WM_QUIT直接放到消息列队,消息循环取到得下一个消息是WM_QUIT就立即退出。MSDN里不建议使用PostMessage发送WM_QUIT消息,因为这样会造成程序的收尾工作无法进行,正常退出后所需要的资源释放等操作就没法执行了。

用SendMessage无法发送WM_QUIT消息,因为SendMessage并不是吧消息放入消息列队,所以,GetMessage根本无法得到SendMessage发送的消息。

BOOL PostThreadMessage(DWORD dwThreadId,UINT uMsg,WPARAM wParam,LPARAM lParam); 这个函数和PostMessage类似,都是发送完消息立即返回,不同的是这个函数向指定的ThreadId发送一条消息。这个函数发送的消息不回被分配到目标进程窗口的回调函数,因为当消息放入列队时,MSG的hwnd被设置为NULL,没有窗口句柄,DispatchMessage能把消息分配给谁呢?PostThreadMessage也可以发送WM_QUIT消息,消息会放到队列的尾端。在qs_input之前处理该消息。 PostMessage 和PostThreadMessage发送WM_QUIT消息都会造成窗口的首尾代码无法执行。用的时候需要注意下。

LRESULT SendMessage(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam) SendMessage同步发送消息,发送进程要等待目标进程窗口的回调函数处理完成消息后才能恢复运行,调用点线程在等待SendMessage返回的过程中是挂起状态,本身也无法响应任何操作。 发送进程再等待的过程中,如果系统中其他的进程向等待进程发送消息,则发送进程立即处理消息。

Windows提供了其他的4个API来进行进程间的消息发送。 LRESULT SendMessageTimeout(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam,UINT fuFlags,UINT fuTimeout,PDWORD_PTR pdwResult);

fuFlags参数由下列标志组成 SMTO_ABORTIFHUNG 如果目标进程处于挂起状态立即返回。 SMTO_BLOCK 发送进程在SendMessageTimeout返回之前不处理任何消息 SMTO_NORMAL 0值,如果不适用其他标志,就是用这个标志 SMTO_NOTIMEOUTIFNOTHUNG 如果目标进程未处于挂起状态不考虑fuTimeout限定等待值

fuTimeout参数指定等待时间单位毫秒 pdwResult 指向一段内存区域,保存返回结果。如果用SendMessageTimeout本身线程的窗口则直接调用窗口的回调函数,并且将结果保存在pdwResult中。

BOOL SendMessageCallback(HWND hwnd,UINT uMsg,WPARAM wParam,LPARAM lParam,SENDASYNCPROC lpCallback,ULONG_PTR dwData); lpCallback 参数 指向一个CALLBACK函数,定义如下 VOID CALLBACK ResultBack(HWND hwnd,UINT uMsg,ULONG_PTR dwData,LRESULT lResult);