窗口消息
#define WM_LBUTTONDOWN 0x0201
有一些消息具有与它们相关的数据,比如 WM_LBUTTONDOWWN 消息包含了鼠标当前位置的X坐标与Y坐标。
消息循环
MSG msg;
GetMessage(&msg, NULL, 0, 0);
这个函数从队列头部删除第一条消息,如果队列是空的,直到另一个功能块的消息进行排队。事实上,GetMessage阻塞并不会使你的程序没有反应。如果没有消息,程序不做任何事情。如果你需要执行后台处理,你可以创建额外的线程继续运行,而GetMessage函数等待另一条消息。(查看写窗口过程)
TranslateMessage(&msg);
DispatchMessage(&msg);
TranslateMessage函数与键盘输入关联,它把键盘的键击(键按下,按键放开)转换成字符,你不需要真正的知道这个函数是如何工作的,只需要知道在调用DispatchMessage函数之前调用它。如果你需要更深入的了解,MSDN文档的链接会给你更多的信息。
- 操作系统在消息队列放置一个WM_LBUTTONDOWN消息。
- 你的程序调用GetMessage函数。
- GetMessage中队列中提取WM_LBUTTONDOWN并填充MSG结构。
- 你的程序调用TranslateMessage和DispatchMessage函数。
- 在DispatchMessage,操作系统调用你的窗口过程。
- 你的窗口函数可以响应消息或忽略它。
当窗口函数返回,它将返回到DispatchMessage,下一条消息再回到消息循环。只要你的程序在运行,消息将继续到达队列。因此,你需要一个循环,不断的从队列中提取消息并调度它们。你可能想到的循环执行以下操作:
//警告:实际上不要这样写循环
while (1)
{
GetMessage(&msg, NULL, 0, 0);
TranslateMessage(&msg);
DispatchMessage(&msg);
}
正如以上所写,这个循环永远不会结束。这是GetMessage函数放在循环内的返回值。正常情况下,GetMessage函数返回一个非零值。当你想要退出程序和中断消息循环,简单的调用PostQuitMessage函数。
PostQuitMessage(0);
PostQuitMessage函数将一个WM_QUIT消息放入队列,WM_QUIT是一条特殊的消息,它引起GetMessage函数返回零值,消息循环结束的信号。这是修改后的消息循环:
// 正确的消息循环
MSG msg = { };
while (GetMessage(&msg, NULL, 0, 0))
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
只要GetMessage函数返回非零值,在while循环中表达式的值就是真值,在调用PostQuitMessage函数后,表达式值为假值程序跳出循环。(这种行为有一个有趣的结果,你的窗口过程从未收到WM_QUIT消息,所以并不需要在窗口过程中为这个消息使用case语句。)
下一个明显的问题是:你应该在什么时候调用PostQuitMessage函数?我们回到本主题中的窗口关闭的问题。但我们首先要写我们的窗口过程。
发布消息和发送消息
上一节谈到进入队列的消息,在某些情况下,操作系统将绕过消息队列,直接调用窗口过程。
进行这种区分的术语有可能造成混淆:
发布(Post)一条消息意味着消息在消息队列中,并通过消息循环调度(GetMessage和DispatchMessage)。
发送(Send)一条消息意味着跳过消息队列,操作系统直接调用窗口过程。
现在,区别不是很重要。窗口过程处理所有消息,但某些消息绕过消息队列,直接进入你的窗口过程。然而,如果你的应用程序窗口之间进行通讯它可以有所作为。你可以在关于消息和消息队列(About Messages and Message Queues)的主题中找到更深入的讨论。
下一节:书写窗口过程