把书上看到东西总结一下
一:消息循环
一个windows程序,WinMain函数作为程序入口,位于主线程。
WinMain中创建了窗口后
使用
while( GetMessage(&msg, NULL, 0, 0) )
{
TranslateMessage (&msg);
DispatchMessage (&msg);
}
开启了消息循环,注意流程如下:
windows给每个窗口都维护了一个消息队列(《windows程序设计》3.1.7)
GetMessage从队列中获取一条消息,注意该函数是阻塞式的!(罗云彬,《琢石成器:Windows环境下32位汇编语言程序设计(第3版)》4.2.3)
加入队列中没有消息,windows会让该函数在内部等待(具体等待方式为如果没有消息,windows就没收该线程的时间片,直至有消息)
如果想利用没有消息的空闲时间做点事情可以调用PeekMessage来做消息循环的工作,具体参考罗云彬书或者
《windows程序设计》 5.6.2 随机矩形
TranslateMessage 主要用于键盘消息转换,
重点来了:DispatchMessage 会将消息发给之前指定的窗口过程来处理。
即在函数内部,windows调用窗口过程(窗口过程作为回调函数存在,而不是在这个地方直接调用!)
窗口过程返回后该函数才会返回,这一点很重要!
然后开始下一轮循环!
MSDN的说法是:
The return value specifies the value returned by the window procedure. Although its meaning depends on the message being dispatched, the return value generally is ignored.
二:
工作线程SendMessage的情况
某些时候,主线程等待工作线程结束,而工作线程里面调用的一些UI相关的API内部又调用了SendMessage,
这时候就容易出现程序卡死的状况,为什么呢?
这是因为SendMessage的内部工作机制。
MSDN上面先说
"The
SendMessage function sends the specified message to a window or windows. It calls the window procedure for the specified window and does not return until the window procedure has processed the message.
"
即SendMessage是阻塞式的,调用窗口过程直至其返回后自身才返回。
如果SendMessage直接调用窗口过程,那为什么上面那种情况程序会卡死呢。
MSDN在SendMessage的Remark中说:
If the specified window was created by the calling thread, the window procedure is called immediately as a subroutine. If the specified window was created by a different thread, the system switches to that thread and calls the appropriate window procedure. Messages sent between threads are processed only when the receiving thread executes message retrieval code. The sending thread is blocked until the receiving thread processes the message.
大意是:如果目标窗口是由调用SendMessage的函数创建的,则直接调用窗口过程;如果不是,比如上面那种工作线程调用SendMessage,
则系统切换至创建窗口的线程,然后调用里面的窗口过程,此时工作线程阻塞,直至调用窗口过程的线程处理完该消息。
这样就可以理解上面那种情况为什么卡死了,因为主线程已经卡住了,没法处理其他消息,所以SendMessage无法返回。
解决方法:
1.最后不要在工作线程里搞UI操作,改为发送用户消息,让主线程去处理(上次面试时面试官这么说的)这样可能实时性在某些情况会打些折扣。
2.一定要搞,则不要使用WaitForSingleObject这种“野蛮”阻塞掉调用线程的函数,改用MsgWaitForMultipleObjects
MsgWaitForMultipleObjects有一个参考可以指定监视消息的类型,一旦该类型消息进入队列,函数会立刻返回,
函数返回时检查返回值,如果是线程结束,则继续做其他事情,如果是消息进入队列,获取并转发消息,然后继续调用MsgWaitForMultipleObjects
参考:http://blog.csdn.net/silvervi/article/details/5874212
补:PostMessage比较简单,将消息放入消息队列后立刻返回,不阻塞。
三: 关于消息响应中创建模态对话框阻塞线程消息循环的问题
比如在窗口过程用DialogBox创建模态对话框,直至调用相应的 EndDialog 后,该函数才返回。
期间,窗口过程一直不返回, 故DispatchMessage 也无法返回。
那主线程中开启的消息循环怎么办呢?一直被卡死?
一句话来说情况是这样的:模态对话框的消息并不通过主程序窗口的消息循环(见《windows程序设计》11.1.3 对话框过程)
实际上:
当创建一个模态对话框后,windows会在内部为它建立一个消息循环,在该循环中,消息发往对话框管理器,
对话框管理器会负责大部分关于对话框的消息,对话框管理器会在处理消息的过程中调用用户定义的对话框过程。
即对话框过程属于windows。当对话框关闭的时候,内建的消息循环会退出,DialogBox返回。(参考2)
这样就解决了消息循环的问题。
参考文献:
1 windows程序设计圣经,《windows程序设计》 by charles petzold
2《琢石成器:Windows环境下32位汇编语言程序设计(第3版)》,罗云彬
3 MSDN简洁版
4 http://blog.csdn.net/silvervi/article/details/5874212
5 http://www.cnblogs.com/lancidie/archive/2011/04/06/2006748.html
6 http://blog.csdn.net/bianbian17556231/article/details/5382230