【转】windows消息和消息队列详解
转载出处:http://blog.csdn.net/bichenggui/article/details/4677494
windows消息和消息队列
与基于MS - DOS的应用程序不同,Windows的应用程序是事件(消息)驱动的。它们不会显式地调用函数(如C运行时库调用)来获取输入,而是等待windows向它们传递输入。 windows系统把应用程序的输入事件传递给各个窗口,每个窗口有一个函数,称为窗口消息处理函数。窗口消息处理函数处理各种用户输入,处理完成后再将控制权交还给系统。窗口消息处理函数一般是在注册一个窗口的时候指定的。你可以从典型的SDK程序中窗口消息处理函数是怎么声明和实现的。
对于Windows XP系统:如果顶层窗口停止响应消息超过几秒钟,系统会认为窗口无回应。在这种情况下,系统将隐藏这个窗口,然后生成一个影子(ghost)窗口覆盖在它上面。这个影子窗口具有着相同的Z轴顺序,位置,大小,显示属性。影子窗口允许用户将其移动,调整大小,甚至关闭(关闭的是停止响应的window)。此时只有这几个动作是被允许的,在调试模式下,系统不会生成影子窗口。
本节讨论以下主题:
Windows消息
1. 消息类型
2. 消息传递
3. 消息处理
4. 消息过滤
5. post message和send message
6. 消息死锁
7. 广播消息
8. 查询消息
1. Windows消息
windows通过消息的形式向窗口传递用户输入。消息可以由系统和应用程序生成。该系统会为每个输入事件产生相应的消息,
例如,用户点击鼠标,移动鼠标或滚动条,或是应用程序改变了系统的某些属性,比如说系统更改了字体资源,改变了某个窗口的
大小。 不仅如此,应用程序可以生成消息,通告发送消息指定它的窗体去执行某些任务或者是与其他的应用程序交互。
windows系统将消息发送到一个窗口消息处理函数时传递四个参数:窗口句柄,消息标识符,两个DWORD值(消息参数)。
窗口句柄标识了该消息的目的窗口。windows使用它来确定是哪个窗口的的窗口消息处理函数收到该消息。
一个消息标识符是一个有名字的常量,用来表明消息的意义。当一个窗口处理函数收到一条消息,它根据判断消息标识符来决定如何处理该消息,例如,消息标识符WM_PAINT消息告诉窗口程序窗口的客户区已发生变化,必须重绘。 消息参数(DWORD值)指定传递的数据或是数据的地址。消息参数可以是一个整型值,一个指针值。也可以为NULL。
一个窗口过程必须根据消息标识符来确定如何解释消息参数。
2. windows 消息类型
本节描述消息的两种类型:
(1) 系统定义的消息
(2) 应用程序定义的消息
系统定义的消息
操作系统向应用程序发送消息来和应用程序通讯。操作系统通过消息控制应用程序的运行,向应用程序传递用户输入以及一些其他有用的信息。
应用程序也可以发送系统定义的消息,应用程序通过这些消息去控制使用注册窗口类创建的控件的窗口的运行。
每个系统定义的消息都有一个唯一的消息标识符和相应的符号常量(在windows SDK的头文件里定义)。符号常量通常会表明系统定义的消息所属的类别。不同的前缀表明不同的类别。一下是常见的分类:
Prefix Message category
WM General window(一般的窗口)
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 (列表视图控件)
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 ()
(2)应用程序定义的消息
应用程序可以通过创建自定义的消息,用来和自己的窗口和其他进程通讯。如果应用程序创建了自己的消息,窗口处理函数可以解析这些信息,并作出相应的处理。
消息标识符值的取值范围:
该系统保留了一个消息范围,从0x0000到0x03FF(0x03FF等于WM_USER -1)范围. 这个范围内的值为系统定义的消息。应用程序不能使用这些值作为自己的自定义消息。
从0x0400(数值WM_USER)到0x7FFF的值是为应用程序保留的。应用程序可以使用这个范围内的值来定义自己的消息。
如果你的操作系用的版本(windows version)主版本为4.0版,你还可以使用0x8000(WM_APP)到0xBFF之间的值来定义自己的消息。
除此之外,应用程序还可以调用RegisterWindowMessage函数注册一个消息时,操作系统会返回一个介于0xC000和0xFFFF之间的一个消息标识符。并且保证这个返回值是系统唯一的。因此,可以避免和其他应用程序使用的消息相冲突。
3. 消息派发
windows使用两种方法将消派发到一个窗口消息处理函数:一是将消息放到消息队列(先进先出队列),二是不放到消息队列,直接发送到窗口消息处理函数,让窗口处理函数来处理消息。
派发到消息队列的消息被称为排队消息(Queued messages)。它们主要是用户输入事件,比如说鼠标或键盘消息盘,有WM_MOUSEMOVE消息,WM_LBUTTONDOWN,WM_KEYDOWN,和WM_CHAR消息。还有一些其他的,包括WM_TIMER,WM_PAINT,以及WM_QUIT。大多数其他的消息息,这是直接发送到窗口过程,被称为非队列消息(non queued messages)。
(1) 队列(Queued)消息
windows可同时显示任意数量的窗口。此时,系统使用消息队列来将键盘和鼠标事件正确的派发到正确的窗口。
windows维护着一个系统消息队列,以及分别为每个GUI线程维护一个各自的线程消息队列。为了避免非GUI线程的创建线程消息队列的开销,所有线程创建初始化时,均不创建消息队列。只有当线程第一次调用GDI函数时,系统才会为线程创建消息队列。所以那些非GUI线程是没有消息队列的。
每当用户移动鼠标,点击按钮或键盘时,鼠标或键盘的设备驱动程序会将输入转换成消息,并将消息放在系统消息队列里。删windows会检查自己的消息队列,如果消息队列不为空,则每次取出并删除一个消息,然后确定消息的目标窗口,然后把消息放到创建这个窗口的线程的线程消息队列里。线程的消息队列接收由线程创建的窗口的所有的鼠标和键盘消息。然后线程会从队列中删除信息,并告诉系统把它们派发到对应的窗口消息处理函数。
除了WM_PAINT, WM_TIMER和WM_QUIT消息以外,系统总是派发放在在消息队列的末尾的消息。这将保证让一个窗口以first-in, first-out的顺序接收消息。WM_PAINT,WM_TIMER,和WM_QUIT消息,会一直被保存在队列中,只有在队列中没有其他消息时才会被派发到窗口消息处理函数。此外,同一个窗口的多个WM_PAINT消息被合并成一个WM_PAINT消息,客户区的所有无效部分也会被合并。这样是为了减少窗口重绘客户区的次数。
windows向线程消息队列传递消息时,首先会填充一个MSG结构,然后将这个MSG结构复制到消息队列。MSG中的信息包括:目标窗口,消息标识符,两个消息参数,消息派发时的时间,鼠标光标位置。一个线程可以使用PostMessage或PostThreadMessage功能向自己的消息队列或者是其他线程的消息队列发送消息。
应用程序可以使用GetMessage函数从自己的消息队列中删除消息。查看而不删除消息,用的是PeekMessage函数。
PeekMessage函数会返回一个带有消息信息的MSG结构。
从消息队列中删除消息后,应用程序可以使用DispatchMessage函数指示系统将消息发送到一个窗口消息处理函数。 DispatchMessage的参数是是前一次调用GetMessage或PeekMessage获得的MSG结构的指针。 DispatchMessage会传递窗口句柄,消息标识符,这两个消息参数这些信息给窗口消息处理函数,它不会传递消息派发时间以及鼠标光标位置。应用程序可以在处理消息时调用的GetMessageTime和GetMessagePos来获得这些信息。
线程可以使用WaitMessage函数,交出自己的控制权,当它的消息队列中没有消息时,调用WaitMessage函数会挂起线程,直到自己的消息队列里有消息时才返回。
您可以调用SetMessageExtraInfo函数来关联一个值给当前线程的消息队列。然后调用GetMessageExtraInfo函数来获取由GetMessage或PeekMessage函数得到的最后一条消息相关联的值。你可以去msdn上看更多的关于这几个函数的信息。
(2) 非队列(Nonqueued)消息
Nonqueued消息被立即送往目的地的窗口消息处理函数,绕过了系统的消息队列和线程消息队列。系统通常会发送nonqueued消息,来通知那些会影响窗口的事件。例如,当用户激活一个新的应用程序窗口时,系统会发送一些列消息到窗口,包括WM_ACTIVATE,WM_SETFOCUS,WM_SETCURSOR。这些消息通知窗口被激活,键盘输入被定向到窗口,并且鼠标光标也移到窗口的边界内。
Nonqueued消息也有可能来源于应用程序调用系统函数。例如,系统调用SetWindowPos函数移动一个窗口后会发送WM_WINDOWPOSCHANGED消息。 一些函数也发送nonqueued消息, 有BroadcastSystemMessage,BroadcastSystemMessageEx,SendMessage,SendMessageTimeout,和SendNotifyMessage。 关于这些函数的详细信息,你可以查阅MSDN。
消息处理
应用程序必须删除并处理发送到它的线程消息队列的消息。单线程应用程序通常在它的WinMain函数的消息循环,删除和分发消息到适当的窗口进行处理。多线程应用程序可以在每一个线程创建一个窗口的消息循环。以下章节描述了一个消息
循环如何工作,并讲述窗口消息处理函数的作用:
(1)消息循环
(2)窗口处理函数
消息循环
一个简单的消息循环包含调用以下三个函数:GetMessage,TranslateMessage,和DispatchMessage。请注意,如果有一个错误,GetMessage返回-1 -因此,需要测试它的返回值,来判断为-1的情况
代码片段:
...
MSG msg;
BOOL bRet;
while( (bRet = GetMessage( &msg, NULL, 0, 0 )) != 0)
{
if (bRet == -1)
{
// handle the error and possibly exit
}
else
{
TranslateMessage(&msg);
DispatchMessage(&msg);
}
}
GetMessage函数从队列中获取消息,并将消息内容复制到一个MSG结构。它返回一个非零值,除非遇到WM_QUIT消息,此种返回FALSE并结束消息循环。在一个单线程应用程序,结束消息循环往往是在关闭应用程序的第一步。应用程序可以调用PostQuitMessage函数来响应WM_DESTROY,结束消息循环。
如果您指定一个窗口句柄作为GetMessage的第二个参数,那么GetMessage只获取在消息队列里和这个窗口有关的消息。 GetMessage也可以在队列中筛选消息,只获取指定范围内的消息。如需有关消息过滤的详细信息,请参考消息过滤。
线程的消息循环必须包括TranslateMessage,如果线程需要接受键盘字符的输入。每次用户按下一个键,该系统产生相应的虚拟键消息(WM_KEYDOWN和WM_KEYUP)。虚拟键消息包含一个虚拟键码,标识的是被按下的键,而不是它相关的字符值。要获得此值,消息循环必须包含TranslateMessage,用来将虚拟键消息翻译成字符消息(WM_CHAR)的并放到应用程序的消息队列里。经过若干次循环后,WM_CHAR消息会被并派发到一个窗口。
DispatchMessage函数将消息发送到到与MSG结构中的窗口句柄关联的窗口。如果窗口句柄是HWND_TOPMOST,DispatchMessage则将消息发送到操作系统所有的顶层窗口。如果窗口句柄是NULL,DispatchMessage不做任何事。
一个应用程序的主线程初始化后,系统就启动应用程序的消息循环,并创造至少一个窗口。一旦启动,消息循环持续从该线程的消息队列中删除消息,并派发他们到相应的窗口。GetMessage函数从消息列表中获取到WM_QUIT消息时,消息循环结束。
一个消息队列只需要一个消息循环,即使一个应用程序包含有多个窗口。 DispatchMessage总是调度消息到正确的窗口,这是因为每个队列中的消息是MSG结构,它包含着消息所属的窗口的句柄。
您可以以多种方式来修改消息循环。例如,您可以从队列中删除消息,但是不派发他们。当发送有些不带有目的地窗口的消息时这非常有用。您也可以使用GetMessage只获取指定的消息,这是有用的,如果你必须你暂时绕过正常的消息队列FIFO顺序。
应用程序使用快捷键时,必须能够将键盘消息转换为命令消息。因此,应用程序的消息循环必须包括TranslateAccelerator函数调用。关于快捷键的更多信息,请参见键盘加速器。
如果一个线程使用一个无模式对话框,那么消息循环必须包括IsDialogMessage函数,以便该对话框可以接收键盘输入。
(2)窗口消息处理函数
窗口消息函数接收和处理的所有发送到窗口的消息。每个窗口类有一个窗口消息处理函数,用该类创建的每个窗口使用同一窗口消息处理函数。
该系统将消息发送到一个窗口的程序,并传递消息的相关信息到窗口消息处理函数,窗口消息处理函数检查消息标识符,根据传过来的参数识别并处理不同的消息,
一个窗口过程通常不会忽略一个消息。如果消息没有被处理,必须被发送给系统默认的窗口消息处理函数,这是否通过调用DefWindowProc函数,来执行一个默认的处理,并返回一个处理的结果。窗口程序必须然后返回该值作为自己的消息处理的结果。大多数窗口消息处理函数只处理一少部分消息,并将其他的返回给系统默认的窗口消息处理函数。
因为窗口消息处理函数被所有属于同一个窗口类的窗口共享,它可以处理几个不同的窗口的消息。要确定具体的窗口消息,窗口消息处理函数可以检查消息结构里的窗口句柄。
【转】windows消息和消息队列详解的更多相关文章
- WCF技术剖析之十七:消息(Message)详解(下篇)
原文:WCF技术剖析之十七:消息(Message)详解(下篇) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话)]]< ...
- WCF技术剖析之十七:消息(Message)详解(中篇)
原文:WCF技术剖析之十七:消息(Message)详解(中篇) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话)]]在上篇中 ...
- WCF技术剖析之十七:消息(Message)详解(上篇)
原文:WCF技术剖析之十七:消息(Message)详解(上篇) [爱心链接:拯救一个25岁身患急性白血病的女孩[内有苏州电视台经济频道<天天山海经>为此录制的节目视频(苏州话)]]消息交换 ...
- MVVMLight消息通知实现机制详解(二)
接上文 MVVMLight消息通知实现机制详解(一) 该工具的内部主要逻辑是以字典模式进行储存持有订阅对象设置的传入参数Type类型.Key值.Action.Target(订阅对象本身) 在发生订阅事 ...
- MVVMLight消息通知实现机制详解(一)
最近对委托.事件的订阅使用的太多,订阅与被订阅之间的绑定约束非常...麻烦,所以翻了下MVVMLight源码找出这段可以拿出来用的部分,详情见下: 一.开发中遇到的问题: 场景1:ClassA中存在事 ...
- 数据结构图文解析之:队列详解与C++模板实现
0. 数据结构图文解析系列 数据结构系列文章 数据结构图文解析之:数组.单链表.双链表介绍及C++模板实现 数据结构图文解析之:栈的简介及C++模板实现 数据结构图文解析之:队列详解与C++模板实现 ...
- (转)windows 下安装配置 Nginx 详解
windows 下安装配置 Nginx 详解 本文转自https://blog.csdn.net/kingscoming/article/details/79042874 nginx功能之一可以启动一 ...
- SVN的Windows和Linux客户端操作详解
SVN的Windows和Linux客户端操作详解 作者:尹正杰 版权声明:原创作品,谢绝转载!否则将追究法律责任. 一.Windows客户端操作 1.安装SVN客户端 a>.去官网下载svn软件 ...
- 引用 Windows Server 2003 FTP服务器配置详解
引用 昆神之星 的 Windows Server 2003 FTP服务器配置详解 1.FTP文件传输协议,主要用于计算机之间文件传输,是互联网上仅次于www的第二大服务.本文主要演示如何在Window ...
- windows下的定时任务设置详解
windows下的定时任务设置详解 一.总结 一句话总结: 1.php.exe是什么? 就是php中自带的一个exe,不是我们写的,这个exe是可以执行其他的PHP的 二.windows下的定时任务设 ...
随机推荐
- poj1087 A Plug for UNIX(网络流最大流)
http://poj.org/problem?id=1087 好久没遇见过这么坑的题了这个题真是挫的够可以的.题目大意:你作为某高管去住宿了,然后宾馆里有几种插座,分别有其对应型号,你携带了几种用电器 ...
- JavaWeb 学习0010-今日问题 2016-12-3
2016-12-3 1. 今天要做的第一个问题就是,怎么把网页变得好看点: addStudent/listStudent页面都有改动 其中 list页面还有了<c:if 的语句这是还没练习过得知 ...
- TCP协议学习记录 (二) Ping程序
简单的实现了一个ping程序,没有做icmp差错报文的检查. 支持自定义字节数,支持发包个数 #pragma pack(4) #define ECHO_REQUEST 8 #define DATASI ...
- CDN系统对网站的性能有极大的提升
CDN系统对网站的性能有极大的提升 打开一个网站 我们只是请求了一个页面.单个页面,实际上所有文件都是一个新的请求.以新浪为例一共发起了35个请求. 这35个请求中.只有一个动态内容.其它均为静态.这 ...
- 空MVC项目找不到System.Web.Optimization的处理办法
install-package Microsoft.AspNet.Web.Optimization Create the bundle in Global.asax Application_Start ...
- 准备开发一款开源NES模拟器
前几天无意见看到一些NES的开发资料,勾起了小时候暑假的无数美好回忆,做为一名码农,萌发了自己写一款模拟器的想法.一来可以加深自己对整个系统/游戏的理解,一来可以实现小时候的梦想:),希望可以坚持下来 ...
- 【转】如何理解c和c++的复杂类型声明
转自:http://blog.chinaunix.net/space.php?uid=22889411&do=blog&id=59667 曾经碰到过让你迷惑不解.类似于int * (* ...
- cesium+ geoserverTerrainProvide+png展示3D高程图展示
一.发布png到geoserver的imagepyramid // translate png to tif epsg:4326 File png = new File(pngPathStr); Bu ...
- Centos7 密码重置
1.在grub启动页面,按e编辑启动选项 2.找到Linux 16的那一行,将ro改为rw init=/sysroot/bin/sh 3.按下 Control+x ,使用修改后的设置启动,出现命令行 ...
- VBA中常用技巧
常量定义 Public Const i as Integer = 1 自定义类型 Type mytype i as Integer b as Boolean s as String end ...