消息和消息队列
Messages and Message Queues
chengxb根据MSDN的相关内容翻译整理 chengxb@126.com 2007-01-06
本文介绍消息和消息队列,以及在应用程序中如何使用它们。
概述Overviews
关于消息和消息队列 About Messages and Message Queues
与基于DOS的应用程序不同,基于Windows的应用程序是事件驱动的。Windows应用程序不通过显式的函数调用来获取输入,相反,它们等待系统将输入传递给自己。
系统为应用程序的各个窗口传递所有的输入,每个窗口都有个函数,叫做窗口过程(window procedure),系统在窗口有输入的时候就调用其窗口过程。关于窗口过程的更多信息,请参见Window Procedures。
Microsoft Windows XP:如果一个顶级窗口超过几秒钟对消息停止响应,系统就认为窗口不再响应,在这种情况下,系统隐藏这个窗口并用一个与此窗口具有相同Z order,位置,大小和可是化属性的影子窗口(ghost window)来替代。允许用户对窗口移动,调整大小,甚至关闭应用程序。然而,这些是能够执行的仅有几个操作,因为应用程序实际上已经停止响应了。如果是在调试模式下,则系统并不产生影子窗口。
本节讨论下面几个话题:
- Windows Messages
- Message Types
- Message Routing
- Message Handling
- Message Filtering
- Posting and Sending Messages
- Message Deadlocks
- Broadcasting Messages
- Query Messages
Windows消息 Windows Messages
系统以消息的形式向窗口过程传递用户输入。消息由系统和应用程序共同产生。系统在每个输入事件发生时产生消息――例如,当用户击键、移动鼠标,或点击某个控件如滚动条。系统也在应用程序引起系统变化的时候产生消息,例如当应用程序改变系统字体资源池或调整自己某个窗口的大小。应用程序可以通过产生消息引导自己的窗口执行某个任务或与应用程序中的其它窗口通讯。
系统向窗口发送的消息包含四个参数:窗口句柄,消息标识符,两个消息参数值。窗口句柄标识消息是发送给哪个窗口的。
消息标识符是一个命名常量,起到标识消息的作用。当窗口过程接收到一个消息的时候,它使用消息标识符来决定如何处理这个消息,例如,WM_PAINT标识符告诉窗口过程,窗口的客户区已经变化,需要重绘。
消息参数指定了窗口过程处理消息时需要使用的数据或数据的位置。消息参数的值和意义由消息决定。消息参数可以包含一个整数,标志位的集合,指向一个数据结构的指针,等等。当一个消息不使用消息参数的时候,它们一般将其设为NULL。一个窗口过程必须检查消息标识符以决定该如何解读消息参数。
消息类型 Message Types
This section describes the two types of messages:
系统定义的消息System-Defined Messages
系统在和应用程序通讯的时候send或post一个系统定义的消息。它使用这些消息来控制应用程序的执行并提供用户的输入信息和其它信息供应用程序处理。一个应用程序也可以send或post系统定义消息,应用程序通常使用这些消息来控制由preregistered window classes创建的窗口。
每个系统定义的消息有一个唯一的消息标识符和一个常量标识符(在SDK的头文件中定义),用于表明消息的意义。例如,WM_PAINT常量请求窗口绘制自己的内容。
符号化常量指定了系统定义消息的分类。常量标识符的前缀表明了可以处理此消息的窗口类型。下表是前缀和相关消息分类的列表。
前缀 | Message category | 消息分类 |
ABM | Application desktop toolbar | 应用程序桌面工具条 |
BM | Button control | 按钮控件 |
CB | Combo box control | 组合框控件 |
CBEM | Extended combo box control | 扩展的组合框控件 |
CDM | Common dialog box | 通用对话框 |
DBT | Device | 设备 |
DL | Drag list box | 托放列表框 |
DM | Default push button control | |
DTM | Date and time picker control | 日期实际选择控件 |
EM | Edit control | 编辑框控件 |
HDM | Header control | 头控件 |
HKM | Hot key control | 热键控件 |
IPM | IP address control | IP地址控件 |
LB | List box control | 列表框控件 |
LVM | List view control | List view 控件 |
MCM | Month calendar control | 日历控件 |
PBM | Progress bar | 进度条控件 |
PGM | Pager control | 分页控件 |
PSM | Property sheet | 属性表控件 |
RB | Rebar control | |
SB | Status bar window | 状态栏窗口 |
SBM | Scroll bar control | 滚动栏控件 |
STM | Static control | 静态文本控件 |
TB | Toolbar | 工具栏 |
TBM | Trackbar | |
TCM | Tab control | |
TTM | Tooltip control | 提示 |
TVM | Tree-view control | 树形视图 |
UDM | Up-down control | 上下控件 |
WM | General window | 通用窗口 |
General window messages覆盖了很多信息和请求,包括鼠标和键盘输入,菜单和对话框输入,窗口创建和管理,动态数据交换(DDE)等等。
应用程序定义的消息Application-Defined Messages
应用程序可以创建只由自己窗口使用的消息,也可以创建用来和其它应用程序中窗口通讯的消息。如果应用程序创建自己的消息,则窗口接收消息的窗口过程必须解释这个消息并提供相应的操作。
消息标识符按照如小规则定义:
l 系统保留从0x0000至0x03ff(WM_USER-1)的值用于系统定义的消息。应用程序不能使用这些值作为私有消息。
l 0x0400(WM_USER)至0x7fff的值可以作为私有窗口类的消息标识符。
l 如果你的应用程序有版本4.0的标记,你可以使用从0x8000至0xbfff的值作为私有消息。
l 当应用程序调用RegisterWindowMessage函数注册一个消息的时候,系统返回在0xc000至0xffff之间的消息标识符。此函数返回的消息标识符在系统中是唯一的。通过使用此函数以避免与其它应用程序中不同含义的相同消息标识符发生冲突。
消息路由Message Routing
系统使用两个方法来路由信息到窗口过程:post消息到一个先进先出的消息队列,这个队列是一个系统定义的内存对象用来临时存储消息,或者将消息直接send给一个窗口过程。
被post到消息队列的消息被称为队列化的消息,包括用户通过鼠标或键盘的输入,例如WM_MOUSEMOVE, WM_LBUTTONDOWN, WM_KEYDOWN, 以及 WM_CHAR消息。其它队列化的消息包括计时器,重绘,退出消息。大多数其它消息被直接send给窗口过程,被称为非队列化消息。
队列化消息Queued Messages
系统可以在同一时间显示任意数量的窗口,为了路由鼠标和键盘输入信息到正确的窗口,系统使用消息列队。
系统维护仅有的一个系统消息队列,并为每个GUI线程维护一个线程消息队列。为避免为非GUI线程创建消息队列的开支,所有的线程在初始创建的时候都没有消息队列,系统只在线程第一次调用User或GDI函数的时候为线程创建消息队列。
无论何时用户移动鼠标、点击鼠标按钮或敲击键盘,鼠标或键盘的设备驱动程序转换用户输入为消息并将其放置到系统消息队列。系统从消息队列一次移走一个消息,检测消息以知道目的窗口,然后post消息到创建目的窗口的线程的消息队列。线程的消息队列为线程创建的窗口接收所有的鼠标和键盘消息。线程从自己的消息队列中移走消息,并指挥系统send消息到正确的窗口过程以处理。
除了WM_PAINT消息,WM_TIMER消息和WM_QUIT消息,系统始终post消息到消息队列的末尾。这保证了窗口按照先入先出的正确顺序处理。然而,WM_PAINT消息,WM_TIMER消息以及WM_QUIT消息,被保留在队列中,并只在队列中没有其它消息的时候才被处理。另外,同一个窗口的多个WM_PAINT消息会被合并成单一的WM_PAINT消息,合并多个失效区域为一个单一的区域。合并WM_PAINT消息减少了窗口重绘的次数。
系统通过填充MSG结构然后将其复制到消息队列的方式来post消息到线程的消息队列。MSG中的信息包括:消息的目的窗口句柄,消息标识符,两个消息参数,消息被post的时间,鼠标位置。线程可以通过使用PostMessage 或PostThreadMessage函数来 post消息到自己的消息队列或其它线程的消息队列。
应用程序可以用GetMessage函数从自己的消息队列中移走消息。使用PeekMessage函数仅仅检查一个消息而不将其从消息队列中移走。这两个函数都用消息的信息来填充MSG结构。
在从自己的消息队列中移走一个消息之后,应用程序可以使用DispatchMessage函数指挥系统以send消息给窗口过程进行处理。DispatchMessage函数使用一个指向MSG结构的指针,这个MSG结构先前已经使用GetMessage 或 PeekMessage函数进行了填充。DispatchMessage传递窗口句柄、消息标识符、两个消息参数给窗口过程,但是它并不传递消息被post的时间和鼠标位置。应用程序在处理消息的时候可以通过调用GetMessageTime 和 GetMessagePos函数取得时间和鼠标位置这些信息。
开发人员可以调用SetMessageExtraInfo函数将一个值与当前线程消息队列关联起来。之后可以调用GetMessageExtraInfo函数取得与用GetMessage 或PeekMessage函数获得的最后一个消息所关联的值。
非队列化消息Nonqueued Messages
非队列化消息被send给目的窗口的窗口过程,绕过了系统消息队列和线程消息队列。系统通常send非队列化消息来通知窗口对其有影响的某个事件发生了。例如,当用户激活一个应用程序窗口,系统send给窗口一系列消息,包括:WM_ACTIVATE, WM_SETFOCUS, 和 WM_SETCURSOR。这些消息通知窗口自己已经被激活了,键盘输入已经被导向了自己。鼠标光标已经被移到了窗口的边框范围之内。非队列化消息也可以由应用程序调用一些系统函数而触发,如,系统在应用程序调用SetWindowPos函数移动窗口之后会send WM_WINDOWPOSCHANGED消息。
一些 send 非队列化消息的函数是 BroadcastSystemMessage , BroadcastSystemMessageEx, SendMessage, SendMessageTimeout, 和 SendNotifyMessage 。来自 “ ITPUB博客 ” ,链接:http://blog.itpub.net/777154/viewspace-927530/,如需转载,请注明出处,否则将追究法律责任。
转载于:http://blog.itpub.net/777154/viewspace-927530/