理解Windows消息循环机制

理解消息循环和整个消息传送机制对Windows编程十分重要。如果对消息处理的整个过程不了解,在windows编程中会遇到很多令人困惑的地方。

什么是消息(Message)

每个消息是一个整型数值,如果查看头文件(查看头文件了解API是一个非常好的习惯和普遍的做法)可以发现如下一些宏定义:

#define WM_INITDIALOG                   0x0110
#define WM_COMMAND                      0x0111

#define WM_LBUTTONDOWN                  0x0201
//...


Windows通信中,至少一些基本Windows通信,几乎都要用到消息。如果你想让窗口或控件(实质上,控件是特殊的窗口)执行何种动作,你应该传送
一个消息给它;如果另一个窗口想让你执行何种操作,它可以传送一个消息给你。如果一个事件,如敲击键盘、移动鼠标、点击按钮等,系统将消息传送给窗口,如
果你是这些窗口之一,你将接收到消息执行相应的操作。

每个Windows消息共有两个参数,wParam和lParam。最初的
wParam是16位(Win16时代)的,lParam是32位的。在Win32中,两个参数都是32位的。并不是所有的消息都是用这两个参数,每个消
息使用它们的方式也不尽相同。如WM_CLOSE消息会忽略上述两个参数;再如WM_COMMAND消息使用上述两个参数,wParam包含”两个”
值,HIWORD(wParam)是通知信息(如果可用),LOWORD(wParam)是发送消息的控件或菜单的ID,lParam是发送消息的控件的
HWND(窗口句柄),如果这个值为NULL,表示这个消息不是由控件发送的。

HIWORD()和LOWORD()是Windows定义的宏,分别取出一个32位整型值的高字和低字。在Win32中,一个”字”是一个16位整型,DWORD(Double WORD)是32位整型。


以用PostMessage()或SendMessage()发送消息。PostMessage()把一个消息放入消息队列(Message
Queue)后立即返回,也就是当调用PostMessage(),函数执行完成返回时,很可能消息尚未处理。SendMessage()直接将消息发送
到窗口,直到这个消息处理完成才返回。如果要关闭一个窗口,可以给它发送一个WM_CLOSE消息,像PostMessage(hwnd,
WM_CLOSE, 0, 0); 效果跟点击窗口右上角的(关闭)按钮是一样的。注意这里的wParam和lParam的值都是0,因为前面提到过,WM_CLOSE消息会忽略上述两个参数。

对话框(Dialogs)


果使用对话框,为跟控件通信,你需要向控件发送消息。你或者可以使用GetDlgItem()函数根据控件的ID取得控件的句柄,然后调用
SendMessage()函数发送消息;或者使用SendDlgItemMessage()组合了上面的步骤。传入一个窗口句柄和子控件的ID能够取得
子控件的句柄,用这个句柄发送消息。跟SendDlgItemMessage()类似的API如GetDlgItemText()能够对所有的窗口进行操
作,而不仅仅是对话框。

什么是消息队列(Message Queue)


设一个场景:系统正在处理WM_PAINT消息,就在这时用户在键盘上敲击了一些按键,这时会发生什么呢?系统应该中断绘图操作然后处理按键消息还是应该
丢弃按键的消息?很明显这些都是不合理的,因此我们引入了消息队列,当消息发送过来,将消息加入消息队列,当一个消息被处理时,将其从消息队列移除。这样
确保消息不会丢失,当你正在处理一个消息时,其它到来的消息可以加入到消息队列直到被处理。

什么是消息循环(Message Loop)

while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
    TranslateMessage(&Msg);
    DispatchMessage(&Msg);
}

上面代码的执行过程为:
1. 消息循环调用GetMessage()从消息队列中查找消息进行处理,如果消息队列为空,程序将停止执行并等待(程序阻塞)。
2. 事件发生时导致一个消息加入到消息队列(例如系统注册了一个鼠标点击事件),GetMessage()将返回一个正值,这表明有消息需要被处理,并且消息已经填充到传入的MSG参数中;当传入WM_QUIT消息时返回0;如果返回值为负表明发生了错误。
3. 取出消息(在Msg变量中)并将其传递给TranslateMessage()函数,这个函数做一些额外的处理:将虚拟键值信息转换为字符信息。这一步实际上是可选的,但有些地方需要用到这一步。
4. 上面的步骤执行完后,将消息传递给DispatchMessage()函数。DispatchMessage()函数将消息分发到消息的目标窗口,并且查找目标窗口过程函数,给窗口过程函数传递窗口句柄、消息、wParam、lParam等参数然后调用该函数。
5. 在窗口过程函数中,检查消息和其他参数,你可以用它来实现你想要的操作。如果不想处理某些特殊的消息,你应该总是调用DefWindowProc()函数,系统将按按默认的方式处理这些消息(通常认为是不做任何操作)。
6. 一旦一个消息处理完成,窗口过程函数返回,DispatchMessage()函数返回,继续循环处理下一个消息。


息循环对Windows编程来说是一个非常重要的概念。窗口过程函数并不是系统自动调用的,而是由开发人员自己通过调用
DispatchMessage()间接的调用的。如果你愿意,可以调用GetWindowLong()函数通过窗口句柄查找到窗口过程函数直接调用达到
消息处理的目的。

while(GetMessage(&Msg, NULL, 0, 0) > 0)
{
    WNDPROC fWndProc = (WNDPROC)GetWindowLong(Msg.hwnd, GWL_WNDPROC);
    fWndProc(Msg.hwnd, Msg.message, Msg.wParam, Msg.lParam);
}

我尝试着写了上面的代码,它确实能工作,但这里存在各种问题,像Unicode/ANSI编码转换、定时器回调等等都这样的代码都不适合,并且很可能导致很多打断很多程序的正常运行。因此这样的代码在这里仅仅是试验,真实项目中一定不能编写这样的代码。


意这里我们用GetWindowLong()来获得相关窗口的窗口过程函数。为什么我们不直接调用WndProc()函数呢?消息循环会处理程序中所有窗
口的消息,包括像按钮、列表框等有他们自己的窗口过程函数的控件,因此我们要保证调用正确的窗口过程函数。尽管有时几个窗口调用同一个窗口过程函数,但函
数的第一个参数 (窗口的句柄) 通常用于告知窗口过程函数是那个窗口发送的消息。

代码可以看出,程序的大部分时间都在处理消息循环。窗
口会不断的处理发过来的消息,但如果要退出程序该怎么做呢?因为我们用的是while()循环,如果GetMessage()返回的是FALSE(即0)
会退出循环,程序能够执行到WinMain()结束处,即程序退出:这正是PostQuitMessage()函数完成的工作,该函数会将WM_QUIT
消息添加到消息队列的队尾,GetMessage()从消息队列取出WM_QUIT消息,填充Msg结构,返回的不是正数,而是0。与此同时,结构Msg
的成员wParam的值会被置为你传给PostQuitMessage()函数参数的值,你可以选择忽略它或做为WinMain()函数的返回值即进程的
退出代码(Exit Code)。

注意:如果发生错误,GetMessage()函数将返回-1。你应该记住这点,说不定你的程序会因此
出错。尽管GetMessage()返回值位BOOL型,但它可以返回TRUE或FALSE之外的值,因为BOOL被定义成UINT(unsigned
int)。下面的程序貌似能正常工作,但有些时候不能正常工作。

while(GetMessage(&Msg, NULL, 0, 0))

while(GetMessage(&Msg, NULL, 0, 0) != 0)

while(GetMessage(&Msg, NULL, 0, 0) == TRUE)

上面的代码都是错误的!有些程序中你会看到会使用第一中方式,使用这种方式你必须保证GetMessage()总是执行成功,否则应该使用下面这段代码:

while(GetMessage(&Msg, NULL, 0, 0) > 0)

希望你对Windows消息循环能有很好的理解,如果还没有,慢慢来,在使用过程中会逐渐理解的。

英文原文:http://winprog.org/tutorial/message_loop.html

时间: 2024-11-04 21:57:56

理解Windows消息循环机制的相关文章

Android IntentService源码理解 及 HandlerThread构建消息循环机制分析

前言:前面写了Handler的源码理解,关于Handler在我们Android开发中是到处能见到的异步通信方式.那么,在Android原生里,有那些也有到了Handler机制的呢?有很多,比如我们今天所要理解分析的IntentService就使用到了Handler.接下来,我们来深入了解一下. HandlerThread: IntentService使用到了Handler+HandlerThread构建的带有消息循环的异步任务处理机制,我们先简单看一下HandlerThread是如何工作的吧.

Win32消息循环机制等【转载】http://blog.csdn.net/u013777351/article/details/49522219

Dos的过程驱动与Windows的事件驱动 在讲本程序的消息循环之前,我想先谈一下Dos与Windows驱动机制的区别: DOS程序主要使用顺序的,过程驱动的程序设计方法.顺序的,过程驱动的程序有一个明显的开始,明显的过程及一个明显的结束,因此程序能直接控制程序事件或过程的顺序.虽然在顺序的过程驱动的程序中也有很多处理异常的方法,但这样的异常处理也仍然是顺序的,过程驱动的结构. 而Windows的驱动方式是事件驱动,就是不由事件的顺序来控制,而是由事件的发生来控制,所有的事件是无序的,所为一个程

Windows 消息循环(2) - WPF中的消息循环

接上文: Windows 消息循环(1) - 概览 win32/MFC/WinForm/WPF 都依靠消息循环驱动,让程序跑起来. 本文介绍 WPF 中是如何使用消息循环来驱动程序的. 4 消息循环在 WPF 中的应用 4.1 引入 只听说过 Dispatcher ,哪里来的消息循环? 先瞧一眼 WPF 启动运行堆栈: 可以发现 PushFrameImpl 这个方法. 去看其源码,就发现了熟悉的消息循环 : 可以理解为:Dispatcher 对消息循环的操作进行了"封装" . 那,Di

Android Handler 消息循环机制

前言 一问起Android应用程序的入口,很多人会说是Activity中的onCreate方法,也有人说是ActivityThread中的静态main方法.因为Java虚拟机在运行的时候会自动加载指定类的静态共有main方法,因此个人更倾向于第二种说法. public final class ActivityThread { ......  public static void main(String[] args) {  ......  Looper.prepareMainLooper(); 

安卓中的消息循环机制Handler及Looper详解

我们知道安卓中的UI线程不是线程安全的,我们不能在UI线程中进行耗时操作,通常我们的做法是开启一个子线程在子线程中处理耗时操作,但是安卓规定不允许在子线程中进行UI的更新操作,通常我们会通过Handler机制来完成该功能,即当子线程中耗时操作完成后,在子线程中通过Handler向主线程发送消息,在主线程中的Handler的handleMessage方法中处理接受到的消息.这就是安卓中的消息机制,安卓中的消息机制主要是指Handler的运行机制,但是Handler的运行需要底层的MessageQu

【Dart学习】-- Dart之消息循环机制[翻译]

概述 异步任务在Dart中随处可见,例如许多库的方法调用都会返回Future对象来实现异步处理,我们也可以注册Handler来响应一些事件,如:鼠标点击事件,I/O流结束和定时器到期. 这篇文章主要介绍了Dart中与异步任务相关的消息循环机制,阅读完这篇文章后相信你可写出更赞的异步执行代码.你也能学习到如何调度Future任务并且预测他们的执行顺序. 在阅读这篇文章之前,你最好先要了解一下基本的Future用法. 基本概念 如果你写过一些关于UI的代码,你就应该熟悉消息循环和消息队列.有了他们才

Android消息循环机制

转载请注明出处:http://blog.csdn.net/crazy1235/article/details/51707527 Android的消息循环机制主要先关的类有: Handler Looper Message MessageQueue ActivityThread 实际上 应用程序启动的时候,会创建一个UI线程,然后该线程关联一个消息队列,相关操作封装一个个消息放入队列中,主线程会不断循环从队列中取出消息进行分发处理. 为什么用Handler 大家都知道,Android规定[访问UI只

Android HandlerThread 消息循环机制之源代码解析

关于 HandlerThread 这个类.可能有些人眼睛一瞟,手指放在键盘上,然后就是一阵狂敲.立即就能敲出一段段华丽的代码: HandlerThread handlerThread = new HandlerThread("handlerThread"); handlerThread.start(); Handler handler = new Handler(handlerThread.getLooper()){ public void handleMessage(Message

iOS runtime探究(二): 从runtime開始深入理解OC消息转发机制

你要知道的runtime都在这里 转载请注明出处 http://blog.csdn.net/u014205968/article/details/67639289 本文主要解说runtime相关知识,从原理到实践.由于包括内容过多分为下面五篇文章详细解说.可自行选择须要了解的方向: 从runtime開始: 理解面向对象的类到面向过程的结构体 从runtime開始: 深入理解OC消息转发机制 从runtime開始: 理解OC的属性property 从runtime開始: 实践Category加入属