概述
Windows 是事件驱动的,事件驱动围绕着消息的产生与处理展开,事件驱动是靠消息循环机制来实现的。也可以理解为消息是一种报告有关事件发生的通知。
消息(Message)指的就是Windows 操作系统发给应用程序的一个通告,它告诉应用程序某个特定的事件发生了。
比如,用户单击鼠标或按键都会引发Windows 系统发送相应的消息。最终处理消息的是应用程序的窗口函数,如果程序不负责处理的话系统将会作出默认处理。
从数据结构的角度来说,消息是一个结构体,它包含了消息的类型标识符以及其他的一些附加信息。
系统定义的结构体MSG用于表示消息,MSG 具有如下定义形式:
123456789 typedef struct tagMSG{HWND hwnd;//hwnd 是窗口的句柄,这个参数将决定由哪个窗口过程函数对消息进行处理UINT message;//message是一个消息常量,用来表示消息的类型WPARAM wParam;//wParam 和lParam 都是32 位的附加信息,具体表示什么内容,要视消息的类型而定LPARAM lParam;DWORD time;//time 是消息发送的时间POINT pt;//pt 是消息发送时鼠标所在的位置。}MSG;
Windows 是一消息(Message)驱动式系统,Windows 消息提供了应用程序与应用程序
之间、应用程序与Windows 系统之间进行通讯的手段。应用程序要实现的功能由消息来触发,并靠对消息的响应和处理来完成。
所谓消息就是描述事件发生的信息,Windows 程序是事件驱动的,用这一方法编写程序避免了死板的操作模式,因为Windows 程序的执行顺序将取决于事件的发生顺序,具有不可预知性。
消息队列
Windows 系统中有两种消息队列
- 一种是系统消息队列
- 另一种是应用程序消息队列
计算机的所有输入设备由 Windows 监控,当一个事件发生时,Windows 先将输入的消息放入系统消息队列中,然后再将输入的消息拷贝到相应的应用程序队列中,应用程序中的消息循环从它的消息队列中检索每一个消息并发送给相应的窗口函数中。
Windows 操作系统为每个线程维持一个消息队列,当事件产生时,操作系统感知这一事件的发生,并包装成消息发送到消息队列,应用程序通过
GetMessage()
函数取得消息并存于一个消息结构体中,然后通过一个TranslateMessage()
和DispatchMessage()
解释和分发消息
123456 //Windows消息循环while(GetMessage(&msg,NULL,0,0)){TranslateMessage(&msg);DispatchMessage(&msg);}
TranslateMessage(&msg)
对于大多数消息而言不起作用,但是有些消息,比如键盘按键按下和弹起(分别对于KeyDown
和KeyUp
消息),却需要通过它解释,产生一个WM_CHAR
消息。DispatchMessage(&msg)
负责把消息分发到消息结构体中对应的窗口,交由窗口过程函数处理。GetMessage()
在取得WM_QUIT
之前的返回值都为TRUE,也就是说只有获取到WM_QUIT
消息才返回FALSE,才能跳出消息循环。
消息循环
消息循环是Windows 应用程序存在的根本,应用程序通过消息循环获取各种消息,并通过相应的窗口过程函数,对消息加以处理。
正是这个消息循环使得一个应用程序能够响应外部的各种事件,所以消息循环往往是一个Windows 应用程序的核心部分。
消息处理
取得的消息将交由窗口处理函数进行处理,对于每个窗口类Windows为我们预备了一个默认的窗口过程处理函数
DefWindowProc()
,这样做的好处是,我们可以着眼于我们感兴趣的消息,把其他不感兴趣的消息传递给默认窗口过程函数进行处理。每一个窗口类都有一个窗口过程函数,此函数是一个回调函数,它是由Windows操作系统负责调用的,而应用程序本身不能调用它。以switch语句开始,对于每条感兴趣的消息都以一个case引出。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 |
LRESULT CALLBACK WndProc ( HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam ) { //... switch(uMsgId) { case WM_TIMER://对定时器消息处理 //... return 0; case WM_LBUTTONDOWN://对鼠标左键单击消息的处理 //... return 0; default: return DefWindowProc(hwnd,uMsgId,wParam,lParam); } } |
对于每条已经处理过的消息都必须返回0,否则消息将不停的重试下去;对于不感兴趣的消息,交给DefWindowProc()函数进行处理,并需要返回其处理值。
MFC消息映射
在MFC 的框架结构下,“消息映射”是通过巧妙的宏定义,形成一张消息映射表格来进行的。
这样一旦消息发生,Framework 就可以根据消息映射表格来进行消息映射和命令传递。
- 首先在需要进行消息处理的类的头文件(.H)里,都会含有
DECLARE_MESSAGE_MAP()
宏, 声明该类拥有消息映射表格。- 然后在类应用程序文件(.CPP)实现这一表格
1 2 3 4 5 6 |
BEGIN_MESSAGE_MAP(CInheritClass, CBaseClass) ON_COMMAND(ID_EDIT_COPY,OnEditCopy) //... END_MESSAGE_MAP() |
BEGIN_MESSAGE_MAP
- 参数1:拥有消息表格的类
- 参数2:拥有消息表格的类的父类
ON_COMMAND
- 指定命令,消息的处理函数
END_MESSAGE_MAP()
- 结尾符号
DECLARE_MESSAGE_MAP
AFX_MSGMAP_ENTRY
- 包含了一个消息的所有相关信息
AFX_MSGMAP
- 用来得到基类的消息映射入口地址
- 得到本身的消息映射入口地址
实际上,MFC 把所有的消息一条条填入到
AFX_MSGMAP_ENTRY
结构中去,形成一个数组,该数组存放了所有的消息和与它们相关的参数。同时通过
AFX_MSGMAP
能得到该数组的首地址,同时得到基类的消息映射入口地址。当本身对该消息不响应的时候,就可以上溯到基类的消息映射表寻找对应的消息响应。
MFC 通过钩子函数_AfxCbtFilterHook()截获消息,并在此函数中把窗口过程函数设置为AfxWindProc,而原来的窗口过程函数被保存在成员变量m_pfnSuper中。
MFC消息映射的步骤
- 函数
AfxWndProc
接收Windows操作系统发送的消息。 - 函数
AfxWndProc
调用函数AfxCallWndProc
进行消息处理,这里一个进步是把对句柄的操作转换成对CWnd 对象的操作。 - 函数
AfxCallWndProc
调用CWnd 类的方法WindowProc
进行消息处理 WindowProc
调用OnWndMsg
进行正式的消息处理,即把消息派送到相关的方法中去处理- 如果
OnWndMsg
方法没有对消息进行处理的话,就调用DefWindowProc
对消息进行处理
MFC消息分类
- 命令消息(
WM_COMMAND
)- 比如菜单项的选择,工具栏按钮点击等发出该消息。所有派生自
CCmdTarget
的类都有
能力接收WM_COMMAND
消息。
- 比如菜单项的选择,工具栏按钮点击等发出该消息。所有派生自
- 标准消息(
WM_XXX
)- 比如窗口创建,窗口销毁等。所有派生自CWnd的类才有资格接收标准消息。
- 通告消息(
WM_NOTIFY
)- 这是有控件向父窗口发送的消息,标示控件本身状态的变化。比如下拉列表框选项的改变
CBN_SELCHANGE
和树形控件的TVN_SELCHANGED
消息都是通告消息。 - Window 9x 版及以后的新控件通告消息不再通过
WM_COMMAND
传送,而是通过WM_NOTIFY
传送,但是老控件的通告消息,比如CBN_SELCHANGE
还是通过WM_COMMAND
消息发送。
- 这是有控件向父窗口发送的消息,标示控件本身状态的变化。比如下拉列表框选项的改变
- 自定义消息
- 利用MFC 编程,可以使用自定义消息。使用自定义消息需要遵循一定的步骤,并需要
自己编写消息响应函数
- 利用MFC 编程,可以使用自定义消息。使用自定义消息需要遵循一定的步骤,并需要
本文为原创文章,版权归Aet所有,欢迎分享本文,转载请保留出处!
你可能也喜欢
- ♥ 关于异常的捕获和dump文件的生成07/05
- ♥ Soui五05/30
- ♥ Windbg:命令实践详解一03/27
- ♥ Cef:沙盒、CefApp、CefClient02/29
- ♥ Windows 核心编程 _ 内核对象:线程同步一07/29
- ♥ 打包_7z生成自解压打包exe07/11