1、windows消息类型
以下四种,前三种是系统消息,范围在[0x0000, 0x03ff],第四种是用户自定义消息。
1.1 窗口消息
与窗口的内部运作有关,如创建窗口,绘制窗口,销毁窗口等。可以是一般的窗口,也可以是Dialog,控件等。
如:WM_CREATE,WM_PAINT,WM_MOUSEMOVE,WM_CTLCOLOR,WM_HSCROLL等。
1.2 命令消息
与处理用户请求有关,如单击菜单项或工具栏或控件时,就会产生命令消息。
WM_COMMAND,LOWORD(wParam)表示菜单项,工具栏按钮或控件的ID。如果是控件, HIWORD(wParam)表示控件消息类型
1.3 控件通知
控件通知消息,这是最灵活的消息格式,其Message,wParam,lParam分别为:WM_NOTIFY, 控件ID,指向NMHDR的指针。NMHDR包含控件通知的内容,可以任意扩展。
1.4 用户自定义消息
用户自定义的消息, 对于其范围有如下规定:
WM_USER: 0x0400-0x7FFF (ex. WM_USER+10)
WM_APP(winver> 4.0): 0x8000-0xBFFF (ex.WM_APP+4)
RegisterWindowMessage: 0xC000-0xFFFF
2、消息队列
Windows中有两种类型的消息队列。
2.1 系统消息队列(System Message Queue)
这是一个系统唯一的Queue,设备驱动(mouse,keyboard)会把操作输入转化成消息存在系统队列中,然后系统会把此消息放到目标窗口所在的线程的消息队列(thread-specific message queue)中等待处理。
2.2 线程消息队列(Thread-specific Message Queue)
每一个GUI线程都会维护这样一个线程消息队列。(这个队列只有在线程调用GDI函数时才会创建,默认不创建)。然后线程消息队列中的消息会被送到相应的窗口过程(WndProc)处理。
注意: 线程消息队列中WM_PAINT,WM_TIMER只有在Queue中没有其他消息的时候才会被处理,WM_PAINT消息还会被合并以提高效率。其他所有消息以先进先出(FIFO)的方式被处理。
3、队列消息和非队列消息
3.1 队列消息(Queued Messages)
消息会先保存在消息队列中,消息循环会从此队列中取消息并分发到各窗口处理,如鼠标、键盘消息。
3.2 非队列消息(NonQueued Messages)
消息会绕过系统消息队列和线程消息队列直接发送到窗口过程被处理,如: WM_ACTIVATE, WM_SETFOCUS, WM_SETCURSOR, WM_WINDOWPOSCHANGED
注意:postMessage发送的消息是队列消息,它会把消息Post到消息队列中;SendMessage发送的消息是非队列消息,被直接送到窗口过程处理。
4、PostMessage、PostThreadMessage、SendMessage
PostMessage:把消息放到指定窗口所在的线程消息队列中后立即返回;
PostThreadMessage:把消息放到指定线程的消息队列中后立即返回;
以上两者的区别:PostMessage通过指定目标窗口句柄来确定目标线程,通常情况下由窗口过程来处理消息;PostThreadMessage直接指定线程ID来确定目标线程,没有目标窗口,只能在消息循环中直接根据消息类型做相应的处理。在程序设计的选择方面,如果是UI线程,则应使用PostMessage;如果是工作线程,则应使用PostThreadMessage,不要为了接收消息而创建窗口。
SendMessage:直接把消息送到窗口过程处理,处理完了才返回。
5、GetMessage、PeekMessage
PeekMessage会立即返回,可以保留消息,有消息返回TRUE,如果没有消息返回FALSE;
GetMessage在有消息时返回,会删除消息;有消息且该消息不为WM_QUIT时返回TRUE,有消息且该消息为WM_QUIT时返回FALSE;
GetMessage不将控制传回给程序,直到从程序的消息队列中取得消息,但是PeekMessage总是立刻传回,而不论一个消息是否出现。当消息队列中有一个消息时,PeekMessage的传回值为TRUE(非0),并且将按通常方式处理消息。当队列中没有消息时,PeekMessage传回FALSE(0)。
6、TranslateMessage、TranslateAccelerator
TranslateMessage:把一个virtual-key消息转化成字符消息(character message),并放到当前线程的消息队列中,消息循环下一次取出处理。
TranslateAccelerator:将快捷键对应到相应的菜单命令。它会把WM_KEYDOWN或WM_SYSKEYDOWN转化成快捷键表中相应的WM_COMMAND或WM_SYSCOMMAND消息,然后把转化后的WM_COMMAND或WM_SYSCOMMAND直接发送到窗口过程处理,处理完后才会返回。
7、消息死锁( Message Deadlocks)
假设有线程A和B, 现在有以下下步骤
1)线程A SendMessage给线程B, A等待消息在线程B中处理后返回
2)线程B收到了线程A发来的消息,并进行处理, 在处理过程中,B也向线程A SendMessgae,然后等待从A返回。
因为此时, 线程A正等待从线程B返回, 无法处理B发来的消息, 从而导致了线程A,B相互等待, 形成死锁。多个线程也可以形成环形死锁。
可以使用 SendNotifyMessage或SendMessageTimeout来避免出现死锁。
8、Windows事件驱动机制
因为DOS和Windows这两种操作系统的运行机制是截然不同的,DOS下的任何程序都是使用顺序的、过程驱动的程序设计方法。这种程序都有一个明显的开始、明显的过程以及一个明显的结束,因此通过程序就能直接控制程序事件或过程的全部顺序。即使是在处理异常时,处理过程也仍然是顺序的、过程驱动的结构。而Windows的驱动方式则是事件驱动的,即程序的流程不是由事件的顺序来控制,而是由事件的发生来控制,所有的事件是无序的,所为一个程序员,在编写程序时,并不知道用户会先按下哪个按纽,也就不知道程序先触发哪个消息。因此我们的主要任务就是对正在开发的应用程序要发出的或要接收的消息进行排序和管理。事件驱动程序设计是密切围绕消息的产生与处理而展开的,一条消息是关于发生的事件的消息。