Windows操作统与众多的应用程序窗口的交互通常是通过消息来驱动的,当某个事件触发后,系统将这些事件封装成消息,放在系统队列当中,然后再将系统队列中属于各个应用程序的消息依次下发给应用程序的线程队列。应用程序通过一个循环来取这些消息,将这些消息进一步下发到程序的各个窗口来进行处理。
1.消息
消息,就是指Windows发出的一个通知,告诉应用程序某个事情发生了。例如,单击鼠标、改变窗口尺寸、按下键盘上的一个键都会使Windows发送一个消息给应用程序。这个消息在Windows中定位以下的结构体:
typedef struct tagMsg
{
HWND hwnd; // 接受该消息的窗口句柄
UINT message; // 消息常量标识符,也就是我们通常所说的消息号
WPARAM wParam; // 32位消息的特定附加信息,确切含义依赖于消息值
LPARAM lParam; // 32位消息的特定附加信息,确切含义依赖于消息值
DWORD time; // 消息创建时的时间
POINT pt; // 消息创建时的鼠标/光标在屏幕坐标系中的位置
}MSG;
这个结构体中携带了一系列的信息,应用程序可以根据需要来处理这些信息,来完成应用程序与操作系统之间的交互。应用程序也可以发消息实现不同页面之间的通信。
2.消息队列
2.1 系统消息队列
这是一个系统唯一的队列,设备驱动会把所有的操作输入转化成消息存在系统队列中,然后把此消息放到目标窗口所在的线程的消息队列中等待处理。
2.2 线程消息队列
windows操作系统为每一个应用程序维护了一个队列,用来存放系统消息队列所放入的消息,称之为线程消息队列。(这个队列只有在线程调用GDI函数时才会创建,默认不创建)。然后线程消息队列中的消息会通过消息循环进入到应用程序当中,这些消息由窗口过程(WndProc)来处理。
注意: 线程消息队列中的WM_PAINT,WM_TIMER消息只有在队列中没有其他消息的时候才会被处理,具体处理的逻辑是这些消息被自动放在队列的末尾,其他消息处理完毕之后才会处理这些消息。WM_PAINT消息还会被合并以提高效率。其他所有消息以先进先出(FIFO)的方式被处理。
3. 消息循环
应用程序通过循环机制从消息队列中检索消息,这些消息通过TranslateMessage翻译转换(将虚拟键消息转换为字符消息),再把它分派给适当的窗口(DispatchMessage),然后继续从消息队列中检索下一条消息,再分派给适当的窗口,依次进行。
while(GetMessage(&msg, NULL, 0, 0)) {
if(!TranslateAccelerator(msg.hWnd, hAccelTable, &msg)) {
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
4.窗口过程
每个窗口都有一个窗口过程来接收有消息循环传递给窗口的消息,它的任务就是获取消息然后响应它。窗口过程是一个回调函数;处理了一个消息后,它通常要返回一个值给Windows。
5.队列消息和非队列消息
并不是所有的消息都会放到消息队列中,有的消息会绕过消息队列直接进入到窗口过程,例如:系统发送的WM_ACTIVATE,WM_SETFOCUS等消息。又比如:应用程序通过调用SendMessage()函数也可以将消息直接发送到窗口过程。所有就有了队列消息和非队列消息之分。
6. SendMessage和PostMessage
发送消息到指定窗口一般通过两个函数完成:SendMessage和PostMessage。
两个函数的区别在于:
PostMessage函数只是向线程消息队列中添加消息,如果添加成功,则返回True,否则返回False,消息是否被处理,或处理的结果,就不知道了。
而SendMessage则有些不同,它并不是把消息加入到队列里,而是直接翻译消息和调用消息处理(线程向自己发送消息才是这样),直到消息处理完成后才返回。所以,如果我们希望发送的消息立即被执行,就应该调用SendMessage。
还有一点,就是SendMessage发送的消息由于不会被加入到消息队列中,所以通过PeekMessage或GetMessage是不能获取到由SendMessage发送的消息。
所以我们常说,PostMessage调用是异步的而SendMessage调用是同步的
7.消息事件
事件本质上是对消息的封装,是IDE编程环境为了简化编程而提供的有用的工具。这个封装是在窗口过程中实现的。每种IDE封装了许多Windows的消息,例如:
事件 | 消息 |
OnActivate | WM_ACTIVATE |
OnClick | WM_XBUTTONDOWN |
OnCreate | WM_CREATE |
OnDblClick | WM_XBUTTONDBLCLICK |
OnKeyDown | WM_KEYDOWN |
OnKeyPress | WM_CHAR |
OnKeyUp | WIN_KEYUP |
OnPaint | WM_PAINT |
OnResize | WM_SIZE |
OnTimer | WM_TIMER |