首页 > 代码库 > windows 消息队列
windows 消息队列
许多朋友都不会使用MSDN LIBRARY(虽然MSDN 跟 MSDN LIBRARY 不是一回事,但是下文中还是简写为MSDN),其实它是非常重要的东西。
其实所有的说明都可以在MSDN中查到。
环境:VS2008 + SP1补丁 MSDN LIBRARY 2008 + SP1 补丁
打开 这个版本的 MSDN 后,可以看到如图的界面
在最左下角,我们可以看到 Contents 、Index 、Help Favorites 等选项卡,较为重要的就是 Contents 和 Index ,其中 Contents 是将MSDN 中的内容 以 相关性 进行 分类 ,而 Index 主要是用来进行 具体函数功能 查询的,比如 我们切换到 Index 选项卡页面,然后在上面 looking for 里输入 任意一个函数名: CreateWindow ,然后一双击 下面列举出来的 CreateWindow ,就可以看到 右侧 有了函数 的信息了。其实一个函数是可能有多种平台都对它进行了实现的,比如这个函数,WINCE平台下也可以使用,这就需要使用 上图中 左下角 的 Index Results 功能了,这里会列举出所有的相关信息。
其实我想大家应该都会这种方法 ,关于 消息映射 ,如果想要 系统的了解一下 应该怎么办呢? 只看 GetMessage 这样的函数 是看不出什么来的,我们需要定位到 MSDN 中对 消息映射的讲解与说明的地方,系统的学习一下,然后才能了解。其实其它的功能也有,比如 关于窗口的,关于控件的,关于MFC的,关于C语言和C++语言还有C#等语言的,.NET类库,关于系统方面的,比如线程,进程,系统服务,动态链接库,其实MSDN中都可以找到说明的,只是很多人都不知道如何去找。
下面请看下面两图,然后依次展开 图中的 节点 ,注意要选择 Contents
好,我相信大家已经看到了消息和消息队列的部分了。打开看看吧。
可以看到它大概分为 这么几个部分 : OverViews 概览,里面又有关于和使用两个小分支,关于是比较重要的部分,描述了关于 消息队列中的 常用的概念 及认识 ,使用主要是举了一些例子,再下面就 Reference了,主要包括 函数列表、函数中使用到的通知与结构体。
下面我们就主要针对 概览中的 关于部分 做一些 介绍吧。
与基于DOS系统的应用程序不一样,基于WINDOWS的应用程序是事件驱动的。 基于WINDOWS的应用程序不会使用显示的调用函数来获得输入(就像C语言中的SCANF似的)。取而代之的是 它们等待操作系统把输入传递给它们。
应用程序的所有输入都会由操作系统传递给程序中的大量窗口。每个窗口都有一个函数,叫过窗口过程。操作系统会去调用这个窗口过程当窗口有输入的时候。窗口过程处理输入并返回控制权给操作系统。
XP系统中,如果一个顶层窗口停止对消息的响应超过几秒种,系统会认为它处理未响应状态。在这种情况下,系统会将它隐藏并用一个幽灵窗口来替换它,这个幽灵窗口跟这个顶层窗口拥有相同的Z Order,位置,大小和外观。这使得用户可以移动它,修改其大小,甚至关于应用程序。但是,只有这些动作才对这个顶层窗口有效,因为这个应用程序实际上是没有响应的。当在调试模式时,系统不会产生幽灵窗口。
Windows Messages 窗口消息
系统是以消息的形式将输入传递给窗口过程的。消息是由系统自身和应用程序自身产生的。每个输入事件都会让操作系统自身产生一个消息,例如,当用户按下键盘、移动鼠标或点击了一个滚动条控件时,系统都会产生相应的消息。当有应用程序做了系统方面的修改后,系统也会产生消息,比如当用户修改了系统颜色(不会修改吧,右击桌面空白区域,属性,外观,高级,就在这里面可以设置)时,系统就会产生
WM_SYSCOLORCHANGE 消息。
应用程序也可以产生消息来指引它自己的窗口去执行任务或者是跟其它应用程序中的窗口通信。
系统在发一个消息给窗口过程时,消息是带有四个参数的,窗口句柄、消息标识符、两个参数。窗口句柄标识着这个消息是属于哪个窗口的,系统使用它来决定 哪个窗口过程应该 接收这个消息。消息标识符是一个常量 ,当窗口过程接收到一个消息时,它使用消息标识符来决定怎么处理这个消息。例如,消息标识符 WM_PAINT 告诉窗口过程 窗口的客户区已经发生了变化,需要进行重画。消息参数指定数据或数据存在的位置,这些数据可以被窗口过程使用。至于两个参数是什么意思,这是要取决于具体的消息的,它可以是一个整数,也可以是一个指向某种结构体对象的指针等等。当消息不需要使用参数时,通常将消息参数置为NULL,窗口过程必须检验消息标识符来决定怎么解释消息的参数。
Message Types 消息类型
消息有两种类型: 系统定义好的消息 和 应用程序自定义的消息
当操作系统与应用程序通信时,操作系统会向应用程序发送或投递系统定义的消息。操作系统使用这些消息来控制 应用程序的操作,并且 向应用程序提供输入和其它信息 以供 应用程序处理。应用程序也可以发送或投递系统定义的消息。应用程序通常使用这些消息来控制控件窗口的操作,控件窗口就是使用操作系统预先注册好的窗口类来创建的窗口,比如常见的 BUTTON ,EDIT 等。
每个系统定义的消息都有一个独一无二的标识符和用来描述其功能的符号常量,例如,WM_PAINT 这个常量 要求窗口重画它的内容。
符号常量指定了一些集合 ,这些都是系统预先定义好的,很多消息都是前缀:
Prefix | 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 |
LB | List box control |
LVM | List view control |
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 消息覆盖了一个很大的范围,包括鼠标和键盘消息,菜单和对话框输入,窗口创建和管理,动态数据交换。
应用程序自定义的消息:应用程序可以创建消息 以便它自己的窗口使用 或者 与其它进程中的窗口通信。如果应用程序创建了它自己的消息,那么它的窗口过程函数一定要接收这个消息 ,还要解释它们并对它们做出正确的处理。
用户自定义消息细节 部分请参考MSDN 。
下面是本文章中 较为重要的 内容:
Message Routing 消息路由
一个操作系统中 有 多个 进程 ,每个进程最少有一个主线程 ,当然也可以有其它线程,每个线程都可以创建N个窗口,我们首先要搞清这个对象关系 。
操作系统使用两种方法来将消息路由给窗口过程:
1、投递消息到一个 先进先出的 数据结构,这就是消息队列,它是操作系统定义的一个内存块,用来临时存储消息。
2、将消息直接发送到窗口过程。
被投递到消息队列中的消息叫做 队列消息。队列消息主要是 鼠标消息、键盘消息、字符消息。其它的队列消息还有 计时器消息(WM_TIMER)、绘画消息(WM_PAINT)、退出消息(WM_QUIT) 。大多数的其它的消息都是 直接 发送到 窗口过程的,它们叫做 非队列消息。
上面我们介绍了 队列消息 与 非队列消息的 概念。下面具体来看看它们的含意
Queued Messages : 队列消息
操作系统在同一时间 可以显示 任意多个窗口。要把鼠标和键盘等输入事件路由到合适的窗口,操作系统使用了消息队列。
操作系统维护一个 独立的 系统消息队列 并且 为每个界面线程 维护 一个线程特有的消息队列。
为了避免对非界面线程创建消息队列带来的开销,所有的线程在被初始化后是没有消息队列的,只有在这个线程第一次调用用户函数或GDI函数时 系统才会为这个线程创建 消息队列。
现在我们基本上可以明白一件事,消息队列是跟线程相关的,一个GUI线程只有一个消息队列,但是它却可以创建很多个窗口。
当用户移动鼠标、点击鼠标或者按键盘时,键盘或鼠标驱动程序会将输入事件转换成消息,然后将它设置到系统的消息队列中,系统会移除系统消息队列中的消息,一次只移除一个,然后检查他们的目标窗口,然后将消息投递给 创建了 目标窗口的 线程的消息队列。线程消息队列接收所有由他创建的窗口的鼠标和键盘消息。线程移除消息从它的消息队列中然后指示操作系统把它们发送到合适的窗口过程以便处理。
除了WM_PAINT WM_TIMER WM_QUIT 三个消息之外,操作系统总是把消息投递到消息队列的最后,这保证窗口以队列的顺序接收输入消息。这三个消息 只有在 线程的消息队列中没有 其它消息时 才会被派发给窗口过程。除此之外,发送给同一个窗口的 多个WM_PAINT 消息最终会被合并成一个WM_PAINT消息,这个过程会将所有无效区域进行合并,这样做主要是为了提高效率。
队列消息常用函数就是POSTMESSAGE
Nonqueued Messages 非队列消息
非队列消息是直接发送到窗口过程的消息,它绕过了系统消息队列和线程消息队列。常用函数就是SENDMESSAGE。
好,我只是拣了一些重要的部分进行了 直译,没有仔细思量。
下面来总结一下:
1、一个操作系统中会存在多个进程
2、每个进程最少有一个主线程,当然也可以拥有其它线程
3、每个线程都可以创建多个窗口。
4、一个操作系统只有一个系统消息队列
5、每个GUI线程也只有一个线程消息队列
6、操作系统会记录所有的信息,比如哪个线程属于哪个进程,哪个窗口属于哪个线程。
7、消息分为队列消息和非队列消息
8、队列消息就是 硬件驱动会将输入事件封装成消息 添加到 系统消息队列,然后操作系统会一个接一个的根据消息中的窗口句柄,找到这个窗口所属于的线程,然后将消息 POST给 线程消息队列,然后线程消息队列是由 一些函数 去获取的,如 GETMESSAGE PEEKMESSAGE,这函数获取一个消息,线程消息队列就移除一个消息,然后程序获取了消息后,需要调用 DispatchMessage ,这个函数会将这个消息发送到窗口的窗口过程函数,然后窗口过程函数负责处理这个消息。
9、非队列消息主要是指 操作系统或应用程序调用 SENDMESSAGE 将消息 直接 发送到 窗口过程处理函数中,不会经过两个队列的处理。
OK啦。有了这些基础后,大家再把MSDN 中的 内容 读上一遍,我估计就差不多了。还有一些细节问题我也没有说,比如 一些其它常用的函数,消息过滤等,大家自己读读。
本文出自 “千千阙歌” 博客,请务必保留此出处http://qianqianquege.blog.51cto.com/8004200/1548512
windows 消息队列