深度剖析消息传递机制.docx
《深度剖析消息传递机制.docx》由会员分享,可在线阅读,更多相关《深度剖析消息传递机制.docx(13页珍藏版)》请在冰豆网上搜索。
深度剖析消息传递机制
深度解析VC中的消息传递机制
摘要:
Windows编程和Dos编程,一个很大的区别就是,Windows编程是事件驱动,消息传递的。
所以,要学好Windows编程,必须
对消息机制有一个清楚的认识,本文希望能够对消息的传递做一个全面的分析。
一、什么是消息?
消息系统对于一个win32程序来说十分重要,它是一个程序运行的动力源泉。
一个消息,是系统定义的一个32位的值,他唯一的定
义了一个事件,向Windows发出一个通知,告诉应用程序某个事情发生了。
例如,单击鼠标、改变窗口尺寸、按下键盘上的一个键
都会使Windows发送一个消息给应用程序。
消息本身是作为一个记录传递给应用程序的,这个记录中包含了消息的类型以及其他信息。
例如,对于单击鼠标所产生的消息来
说,这个记录中包含了单击鼠标时的坐标。
这个记录类型叫做MSG,MSG含有来自windows应用程序消息队列的消息信息,它在
Windows中声明如下:
typedefstructtagMsg
{
HWNDhwnd; //接受该消息的窗口句柄
UINTmessage; //消息常量标识符,也就是我们通常所说的消息号
WPARAMwParam; //32位消息的特定附加信息,确切含义依赖于消息值
LPARAMlParam; //32位消息的特定附加信息,确切含义依赖于消息值
DWORDtime; //消息创建时的时间
POINTpt; //消息创建时的鼠标/光标在屏幕坐标系中的位置
}MSG;
消息可以由系统或者应用程序产生。
系统在发生输入事件时产生消息。
举个例子,当用户敲键,移动鼠标或者单击控件。
系统也
产生消息以响应由应用程序带来的变化,比如应用程序改变系统字体,改变窗体大小。
应用程序可以产生消息使窗体执行任务,
或者与其他应用程序中的窗口通讯。
二、消息中有什么?
我们给出了上面的注释,是不是会对消息结构有了一个比较清楚的认识?
如果还没有,那么我们再试着给出下面的解释:
hwnd32位的窗口句柄。
窗口可以是任何类型的屏幕对象,因为Win32能够维护大多数可视对象的句柄(窗口、对话框、按钮、编辑
框等)。
message用于区别其他消息的常量值。
这些常量可以是Windows单元中预定义的常量,也可以是自定义的常量。
消息标识符以常量
命名的方式指出消息的含义。
当窗口过程接收到消息之后,他就会使用消息标识符来决定如何处理消息。
例如、WM_PAINT告诉窗
口过程窗体客户区被改变了需要重绘。
符号常量指定系统消息属于的类别,其前缀指明了处理解释消息的窗体的类型。
wParam通常是一个与消息有关的常量值,也可能是窗口或控件的句柄。
lParam通常是一个指向内存中数据的指针。
由于WParam、lParam和Pointer都是32位的,因此,它们之间可以相互转换。
三、消息标识符的值
系统保留消息标识符的值在0x0000在0x03ff(WM_USER-1)范围。
这些值被系统定义消息使用。
应用程序不能使用这些值给自己的
消息。
应用程序消息从WM_USER(0X0400)到0X7FFF,或0XC000到0XFFFF;WM_USER到0X7FFF范围的消息由应用程序自己使用;0XC000到
0XFFFF范围的消息用来和其他应用程序通信,我们顺便说一下具有标志性的消息值:
WM_NULL---0x0000空消息。
0x0001----0x0087主要是窗口消息。
0x00A0----0x00A9非客户区消息
0x0100----0x0108键盘消息
0x0111----0x0126菜单消息
0x0132----0x0138颜色控制消息
0x0200----0x020A鼠标消息
0x0211----0x0213菜单循环消息
0x0220----0x0230多文档消息
0x03E0----0x03E8DDE消息
0x0400WM_USER
0x8000WM_APP
0x0400----0x7FFF应用程序自定义私有消息
四、消息有的分类
其实,windows中的消息虽然很多,但是种类并不繁杂,大体上有3种:
窗口消息、命令消息和控件通知消息。
窗口消息大概是系统中最为常见的消息,它是指由操作系统和控制其他窗口的窗口所使用的消息。
例如CreateWindow、
DestroyWindow和MoveWindow等都会激发窗口消息,还有我们在上面谈到的单击鼠标所产生的消息也是一种窗口消息。
命令消息这是一种特殊的窗口消息,他用来处理从一个窗口发送到另一个窗口的用户请求,例如按下一个按钮,他就会向主窗口
发送一个命令消息。
控件通知消息是指这样一种消息,一个窗口内的子控件发生了一些事情,需要通知父窗口。
通知消息只适用于标准的窗口控件如
按钮、列表框、组合框、编辑框,以及Windows公共控件如树状视图、列表视图等。
例如,单击或双击一个控件、在控件中选择部
分文本、操作控件的滚动条都会产生通知消息。
她类似于命令消息,当用户与控件窗口交互时,那么控件通知消息就会从控件窗
口发送到它的主窗口。
但是这种消息的存在并不是为了处理用户命令,而是为了让主窗口能够改变控件,例如加载、显示数据。
例如按下一个按钮,他向父窗口发送的消息也可以看作是一个控件通知消息;单击鼠标所产生的消息可以由主窗口直接处理,然
后交给控件窗口处理。
其中窗口消息及控件通知消息主要由窗口类(即直接或间接由CWND类派生类)处理。
相对窗口消息及控件通知消息而言,命令消
息的处理对象范围就广得多,它不仅可以由窗口类处理,还可以由文档类,文档模板类及应用类所处理。
由于控件通知消息很重要的,人们用的也比较多,但是具体的含义往往令初学者晕头转向,所以我决定把常见的几个列出来供大
家参考:
按扭控件
BN_CLICKED用户单击了按钮
BN_DISABLE按钮被禁止
BN_DOUBLECLICKED用户双击了按钮
BN_HILITE用/户加亮了按钮
BN_PAINT按钮应当重画
BN_UNHILITE加亮应当去掉
组合框控件
CBN_CLOSEUP组合框的列表框被关闭
CBN_DBLCLK用户双击了一个字符串
CBN_DROPDOWN组合框的列表框被拉出
CBN_EDITCHANGE用户修改了编辑框中的文本
CBN_EDITUPDATE编辑框内的文本即将更新
CBN_ERRSPACE组合框内存不足
CBN_KILLFOCUS组合框失去输入焦点
CBN_SELCHANGE在组合框中选择了一项
CBN_SELENDCANCEL用户的选择应当被取消
CBN_SELENDOK用户的选择是合法的
CBN_SETFOCUS组合框获得输入焦点
编辑框控件
EN_CHANGE编辑框中的文本己更新
EN_ERRSPACE编辑框内存不足
EN_HSCROLL用户点击了水平滚动条
EN_KILLFOCUS编辑框正在失去输入焦点
EN_MAXTEXT插入的内容被截断
EN_SETFOCUS编辑框获得输入焦点
EN_UPDATE编辑框中的文本将要更新
EN_VSCROLL用户点击了垂直滚动条消息含义
列表框控件
LBN_DBLCLK用户双击了一项
LBN_ERRSPACE列表框内存不够
LBN_KILLFOCUS列表框正在失去输入焦点
LBN_SELCANCEL选择被取消
LBN_SELCHANGE选择了另一项
LBN_SETFOCUS列表框获得输入焦点
五、队列消息和非队列消息
从消息的发送途径来看,消息可以分成2种:
队列消息和非队列消息。
消息队列又可以分成系统消息队列和线程消息队列。
系统消息队列由Windows维护,线程消息队列则由每个GUI线程自己进行维护,为避免给non-GUI现成创建消息队列,所有线程产生
时并没有消息队列,仅当线程第一次调用GDI函数时,系统给线程创建一个消息队列。
队列消息送到系统消息队列,然后到线程消
息队列;非队列消息直接送给目的窗口过程。
对于队列消息,最常见的是鼠标和键盘触发的消息,例如WM_MOUSERMOVE,WM_CHAR等消息,还有一些其它的消息,例如:
WM_PAINT
、WM_TIMER和WM_QUIT。
当鼠标、键盘事件被触发后,相应的鼠标或键盘驱动程序就会把这些事件转换成相应的消息,然后输送到
系统消息队列,由Windows系统去进行处理。
Windows系统则在适当的时机,从系统消息队列中取出一个消息,根据前面我们所说
的MSG消息结构确定消息是要被送往那个窗口,然后把取出的消息送往创建窗口的线程的相应队列,下面的事情就该由线程消息队
列操心了,Windows开始忙自己的事情去了。
线程看到自己的消息队列中有消息,就从队列中取出来,通过操作系统发送到合适的
窗口过程去处理。
一般来讲,系统总是将消息Post在消息队列的末尾。
这样保证窗口以先进先出的顺序接受消息。
然而,WM_PAINT是一个例外,同一
个窗口的多个WM_PAINT被合并成一个WM_PAINT消息,合并所有的无效区域到一个无效区域。
合并WM_PAIN的目的是为了减少刷
新窗口的次数。
非队列消息将会绕过系统消息队列和线程消息队列,直接将消息发送到窗口过程。
系统发送非队列消息通知窗口。
例如,当用户激
活一个窗口,系统发送WM_ACTIVATE,WM_SETFOCUS,andWM_SETCURSOR。
这些消息通知窗口它被激活了。
非队列消息也可以由当
应用程序调用系统函数产生。
例如,当程序调用SetWindowPos,系统发送WM_WINDOWPOSCHANGED消息。
一些函数也发送非队列消息
,例如下面我们要谈到的函数。
六、消息的发送
了解了上面的这些基础理论之后,我们就可以进行一下简单的消息发送与接收。
把一个消息发送到窗口有3种方式:
发送、寄送和广播。
发送消息的函数有SendMessage、SendMessageCallback、SendNotifyMessage、SendMessageTimeout;寄送消息的函数主要有
PostMessage、PostThreadMessage、PostQuitMessage;广播消息的函数我知道的只有BroadcastSystemMessage、
BroadcastSystemMessageEx。
SendMessage的原型如下:
LRESULTSendMessage(HWNDhWnd,UINTMsg,WPARAMwParam,LPARAMlParam)
这个函数主要是向一个或多个窗口发送一条消息,一直等到消息被处理之后才会返回。
不过需要注意的是,如果接收消息的窗口
是同一个应用程序的一部分,那么这个窗口的窗口函数就被作为一个子程序马上被调用;如果接收消息的窗口是被另外的线程所
创建的,那么窗口系统就切换到相应的线程并且调用相应的窗口函数,这条消息不会被放进目标应用程序队列中。
函数的返回值
是由接收消息的窗口的窗口函数返回,返回的值取决于被发送的消息。
PostMessage的原型如下:
BOOLPostMessage(HWNDhWnd,UINTMsg,WPARAMwParam,LPARAMlParam)该函数把一条消息放置到创建hWnd窗口的线程的消息
队列中,该函数不等消息被处理就马上将控制返回。
需要注意的是,如果hWnd参数为HWND_BROADCAST,那么,消息将被寄送给系
统中的所有的重叠窗口和弹出窗口,但是子窗口不会收到该消息;如果