1.消息队列
消息队列用于存放消息的一个队列,消息在队列中先入先出。所有窗口程序都具有消息队列,程序可以从队列中获取消息。
2.消息队列的类型
系统消息队列 - 由系统维护的消息队列,存放系统产生的消息,例如鼠标、键盘等。
程序消息队列 - 属于每一个应用程序(线程)的消息队列,由应用程序(线程)维护。
3.消息队列机制
所有的消息都先进系统的消息队列,系统每隔一段时间就发到每个进程的消息队列。
例如:
当鼠标、键盘产生消息时,会将消息存放到系统消息队列。系统会根据存放的消息,找到对应窗口(进程)的消息队列。
将消息投递到程序(进程)的消息队列。
那么系统是怎么准确地发到某个进程的消息队列呢?
首先通过消息的窗口句柄,找到保存窗口数据的内存地址,然后拿到当前程序实例句柄(即进程)。
PostMessage,是把消息发到系统队列,为什么?
因为如果不发到系统队列,直接发到进程队列,那么可能造成后产生的消息先处理。
SendMessage,把消息既不发到系统消息队列也不发到进程消息队列,那么发到哪里?
发到消息的窗口处理函数(其实内部是直接调用窗口处理函数),内部简单执行过程:
SendMessage(...)
{
进过一系列匹配找到窗口处理函数
return WndProc(...);
}
4.队列消息和非消息队列的关系
(1)使用关系
队列消息:消息的发送和获取,都是通过消息队列完成。
非队列消息:消息的发送和获取,是直接调用消息的窗口处理完成。
(2)队列消息 - 消息发送后,先放入系统队列,然后通过消息循环,从队列中获取。
GetMessage - 从消息队列中获取消息(只能从本进程的队列获取消息)
PostMessage - 将消息投递到系统队列
常见队列消息:WM_PAINT、键盘、鼠标、定时器等。
(3)非消息队列 - 消息发送时,直接查找消息接收窗口的窗口处理函数,调用处理函数,完成消息处理。
SendMessage - 直接将消息发送给窗口的处理函数,并等候处理结果。
常见非队列消息:WM_CREATE、WM_SIZE等。
5.消息循环
GetMessage/PeekMessage - 从程序的消息队列中获取消息。
TranslateMessage - 检查获取的消息,如果是按键消息,产生一个字符消息WM_CHAR,并放入程序的消息队列。
DispatchMessage - 根据消息找到窗口处理函数,调用窗口处理函数,完成消息处理。
重点讲一下GetMessage/PeekMessage,目前我们所了解的是GetMessage会到进程的消息队列获取消息,没有就阻塞;如果有,获取消息后返回。
(1)GetMessage在本程序(进程)消息队列查找消息,如果队列有消息,检查消息是否满足指定条件(HWND,ID范围),不满足就不会取出消息,
否则从队列取出消息并返回。
(2)如果本程序(进程)消息队列没有消息,向系统消息队列获取属于本程序的消息。如果系统消息队列的当前消息属于本程序,系统将消息转发到
本程序的消息队列。
(3)如果系统消息队列没有消息,检查所有窗口需要重新绘制的区域,如果发现有需要绘制的区域,产生WM_PAINT消息,取得消息并返回。
(4)如果没有重新绘制区域,检查定时器。如果有到时的定时器,产生WM_TIMER消息,取得并返回。
(5)如果没有到时的定时器消息,整理程序的资源、内存等。
(6)此时,GetMessage会继续等候下一条消息,而PeekMessage返回FALSE,交出程序的控制权。
注意:GetMessage如果获取到WM_QUIT,函数返回FALSE。
6.消息发送
SendMessage - 用于非队列消息的发送。发送消息到指定的窗口,并等候对象将消息处理,然后返回消息执行结果。
PostMessage - 用于队列消息的发送。将消息放到系统消息队列,立刻返回。无法获知消息是否被对方处理。