您的位置:首页 > 移动开发

你的第一个Windows程序——窗口消息

2013-05-25 22:47 218 查看
MSDN在线原文(英文)

窗口消息

一个GUI应用程序必须响应用户和操作系统的事件。

来自用户的事件 包括所有的使他人能够与你的程序互动的方法:鼠标点击,键盘输入,触屏手势等。
来自操作系统的事件 包括任何可以影响程序行为的“外部”程序,比如用户可以插入一个新的硬件,或者Windows可能会进入低功耗状态(睡眠或休眠)。

在程序运行时,这些事件可以在任意顺序任何时间发生,你如何构建一个执行流程不能提前预测的程序?
为了解决这个问题,Windows使用一个“消息传递”模块,操作系统和你的窗口应用程序传递消息给这个模块。消息是一个简单的数字代码,它指定一个特定的事件。例如,用户按压了鼠标左键,窗口收到以下消息代码的消息:

#define WM_LBUTTONDOWN 0x0201


有一些消息具有与它们相关的数据,比如 WM_LBUTTONDOWWN 消息包含了鼠标当前位置的X坐标与Y坐标。
为了传递一个消息给窗口,操作系统将调用注册该窗口的窗口过程(现在你知道什么是窗口过程).

消息循环

一个应用程序在它运行时,会收到数以千记的消息(想想看,每一次的击键和鼠标点击生成一条消息),而且一个应用程序有多个窗口,每个都有自己的窗口过程。程序如何获得所有这些消息并提供个正确的窗口过程?应用程序需要一个循环来获取消息并把它们发送到正确的窗口。对于创建窗口的每个线程,操作系统创建一个窗口消息队列,这个队列保存所有在这个线程创建的窗口消息。在你的程序里,队列本身是隐藏的,你不能直接操纵队列,但你可以调用GetMessage函数把消息从队列拉出来。

MSG msg;
GetMessage(&msg, NULL, 0, 0);


这个函数从队列头部删除第一条消息,如果队列是空的,直到另一个功能块的消息进行排队。事实上,GetMessage阻塞并不会使你的程序没有反应。如果没有消息,程序不做任何事情。如果你需要执行后台处理,你可以创建额外的线程继续运行,而GetMessage函数等待另一条消息。(查看写窗口过程)

GetMessage函数的第一个参数是一个MSG结构的地址,如果函数调用成功,它填充MSG结构有关的消息中的信息,包括目标窗口和消息代码。其它三个参数让你能够从消息队列中进行过滤。在几乎所有的情况下,将参数设置为0(零)。

虽然MSG结构包含消息有关的信息,你几乎不用直接检查这种结构,相反,你要把它直接传递给两个函数
TranslateMessage(&msg);
DispatchMessage(&msg);


TranslateMessage函数与键盘输入关联,它把键盘的键击(键按下,按键放开)转换成字符,你不需要真正的知道这个函数是如何工作的,只需要知道在调用DispatchMessage函数之前调用它。如果你需要更深入的了解,MSDN文档的链接会给你更多的信息。
 
DispatchMessage函数告诉操作系统调用消息的目标窗口的窗口过程,换句话说,操作系统查找窗口表的窗口句柄,发现与窗口关联的函数指针,并调用函数。
例如,假设用户按下鼠标左键,这引起一连串的事件:
操作系统在消息队列放置一个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)的主题中找到更深入的讨论。

 

下一节:书写窗口过程
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  Windows application c