一.MFC消息响应机制分析
---- MFC是Windows下程序设计的最流行的一个类库,但是该类库比较庞杂,尤其是它的消息映射机制,更是涉及到很多低层的东西,我们在这里,对它的整个消息映射机制进行了系统的分析,可以帮助程序开发人员对MFC的消息映射机制有一个比较透彻的了解。
---- 关键词:面向对象 消息映射 MFC 程序设计
1.引言
---- VC++的MFC类库实际上是Windows下C++编程的一套最为流行的类库。MFC的框架结构大大方便了程序员的编程工作,但是为了更加有效、灵活的使用MFC编程,了解MFC的体系结构往往可以使编程工作事半功倍。它合理的封装了WIN32 API函数,并设计了一套方便的消息映射机制。但这套机制本身比较庞大和复杂,对它的分析和了解无疑有助于我们写出更为合理的高效的程序。这里我们简单的分析MFC的消息响应机制,以了解MFC是如何对Windows的消息加以封装,方便用户的开发。
2. SDK下的消息机制实现
---- 这里简单的回顾一下SDK下我们是如何进行Windows的程序开发的。一般来说,Windows的消息都是和线程相对应的。即Windows会把消息发送给和该消息相对应的线程。在SDK的模式下,程序是通过GetMessage函数从和某个线程相对应的消息队列里面把消息取出来并放到一个特殊的结构里面,一个消息的结构是一个如下的STRUCTURE。
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
}MSG;
---- 其中hwnd表示和窗口过程相关的窗口的句柄,message表示消息的ID号,wParam和lParam表示和消息相关的参数,time表示消息发送的时间,pt表示消息发送时的鼠标的位置。
---- 然后TranslateMessage函数用来把虚键消息翻译成字符消息并放到响应的消息队列里面,最后DispatchMessage函数把消息分发到相关的窗口过程。然后窗口过程根据消息的类型对不同的消息进行相关的处理。在SDK编程过程中,用户需要在窗口过程中分析消息的类型和跟消息一起的参数的含义,做不同的处理,相对比较麻烦,而MFC把消息调用的过程给封装起来,使用户能够通过ClassWizard方便的使用和处理Windows的各种消息。
3.MFC的消息实现机制
---- 我们可以看到,在MFC的框架结构下,可以进行消息处理的类的头文件里面都会含有DECLARE_MESSAGE_MAP()宏,这里主要进行消息映射和消息处理函数的声明。可以进行消息处理的类的实现文件里一般都含有如下的结构。
BEGIN_MESSAGE_MAP(CInheritClass, CBaseClass)
//{{AFX_MSG_MAP(CInheritClass)
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
---- 这里主要进行消息映射的实现和消息处理函数的实现。
---- 所有能够进行消息处理的类都是基于CCmdTarget类的,也就是说CCmdTarget类是所有可以进行消息处理类的父类。CCmdTarget类是MFC处理命令消息的基础和核心。
---- 同时MFC定义了下面的两个主要结构:
AFX_MSGMAP_ENTRY
struct AFX_MSGMAP_ENTRY
{
UINT nMessage; // windows message
UINT nCode; // control code or WM_NOTIFY code
UINT nID;
// control ID (or 0 for windows messages)
UINT nLastID;
// used for entries specifying a range of control id's
UINT nSig;
// signature type (action) or pointer to message #
AFX_PMSG pfn; // routine to call (or special value)
};
和AFX_MSGMAP
struct AFX_MSGMAP
{
#ifdef _AFXDLL
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
#else
const AFX_MSGMAP* pBaseMap;
#endif
const AFX_MSGMAP_ENTRY* lpEntries;
};
其中AFX_MSGMAP_ENTRY结构包含了
一个消息的所有相关信息,其中
nMessage为Windows消息的ID号
nCode为控制消息的通知码
nID为Windows控制消息的ID
nLastID表示如果是一个指定范围的消息被映射的话,
nLastID用来表示它的范围。
nSig表示消息的动作标识
AFX_PMSG pfn 它实际上是一个指向
和该消息相应的执行函数的指针。
---- 而AFX_MSGMAP主要作用是两个,一:用来得到基类的消息映射入口地址。二:得到本身的消息映射入口地址。
---- 实际上,MFC把所有的消息一条条填入到AFX_MSGMAP_ENTRY结构中去,形成一个数组,该数组存放了所有的消息和与它们相关的参数。同时通过AFX_MSGMAP能得到该数组的首地址,同时得到基类的消息映射入口地址,这是为了当本身对该消息不响应的时候,就调用其基类的消息响应。
---- 现在我们来分析MFC是如何让窗口过程来处理消息的,实际上所有MFC的窗口类都通过钩子函数_AfxCbtFilterHook截获消息,并且在钩子函数_AfxCbtFilterHook中把窗口过程设定为AfxWndProc。原来的窗口过程保存在成员变量m_pfnSuper中。
---- 所以在MFC框架下,一般一个消息的处理过程是这样的。
函数AfxWndProc接收Windows操作系统发送的消息。
函数AfxWndProc调用函数AfxCallWndProc进行消息处理,这里一个进步是把对句柄的操作转换成对CWnd对象的操作。
函数AfxCallWndProc调用CWnd类的方法WindowProc进行消息处理。注意AfxWndProc和AfxCallWndProc都是AFX的API函数。而WindowProc已经是CWnd的一个方法。所以可以注意到在WindowProc中已经没有关于句柄或者是CWnd的参数了。
方法WindowProc调用方法OnWndMsg进行正式的消息处理,即把消息派送到相关的方法中去处理。消息是如何派送的呢?实际上在CWnd类中都保存了一个AFX_MSGMAP的结构,而在AFX_MSGMAP结构中保存有所有我们用ClassWizard生成的消息的数组的入口,我们把传给OnWndMsg的message和数组中的所有的message进行比较,找到匹配的那一个消息。实际上系统是通过函数AfxFindMessageEntry来实现的。找到了那个message,实际上我们就得到一个AFX_MSGMAP_ENTRY结构,而我们在上面已经提到AFX_MSGMAP_ENTRY保存了和该消息相关的所有信息,其中主要的是消息的动作标识和跟消息相关的执行函数。然后我们就可以根据消息的动作标识调用相关的执行函数,而这个执行函数实际上就是通过ClassWizard在类实现中定义的一个方法。这样就把消息的处理转化到类中的一个方法的实现上。举一个简单的例子,比如在View中对WM_LButtonDown消息的处理就转化成对如下一个方法的操作。
void CInheritView::OnLButtonDown
(UINT nFlags, CPoint point)
{
// TODO: Add your message
handler code here and/or call default
CView::OnLButtonDown(nFlags, point);
}
注意这里CView::OnLButtonDown(nFlags, point)实际上就是调用CWnd的Default()方法。 而Default()方法所做的工作就是调用DefWindowProc对消息进行处理。这实际上是调用原来的窗口过程进行缺省的消息处理。
如果OnWndMsg方法没有对消息进行处理的话,就调用DefWindowProc对消息进行处理。这是实际上是调用原来的窗口过程进行缺省的消息处理。
---- 所以如果正常的消息处理的话,MFC窗口类是完全脱离了原来的窗口过程,用自己的一套体系结构实现消息的映射和处理。即先调用MFC窗口类挂上去的窗口过程,再调用原先的窗口过程。并且用户面对和消息相关的参数不再是死板的wParam和lParam,而是和消息类型具体相关的参数。比如和消息WM_LbuttonDown相对应的方法OnLButtonDown的两个参数是nFlags和point。nFlags表示在按下鼠标左键的时候是否有其他虚键按下,point更简单,就是表示鼠标的位置。
---- 同时MFC窗口类消息传递中还提供了两个函数,分别为WalkPreTranslateTree和PreTranslateMessage。我们知道利用MFC框架生成的程序,都是从CWinApp开始执行的,而CWinapp实际继承了CWinThread类。在CWinThread的运行过程中会调用窗口类中的WalkPreTranslateTree方法。而WalkPreTranslateTree方法实际上就是从当前窗口开始查找愿意进行消息翻译的类,直到找到窗口没有父类为止。在WalkPreTranslateTree方法中调用了PreTranslateMessage方法。实际上PreTranslateMessage最大的好处是我们在消息处理前可以在这个方法里面先做一些事情。举一个简单的例子,比如我们希望在一个CEdit对象里,把所有的输入的字母都以大写的形式出现。我们只需要在PreTranslateMessage方法中判断message是否为WM_CHAR,如果是的话,把wParam(表示键值)由小写字母的值该为大写字母的值就实现了这个功能。
---- 继续上面的例子,根据我们对MFC消息机制的分析,我们很容易得到除了上面的方法,我们至少还可以在另外两个地方进行操作。
---- 一:在消息的处理方法里面即OnChar中,当然最后我们不再调用CEdit::OnChar(nChar, nRepCnt, nFlags),而是直接调用DefWindowProc(WM_CHAR,nChar,MAKELPARAM (nRepCnt,nFlags))。因为从我们上面的分析可以知道CEdit::OnChar(nChar, nRepCnt, nFlags)实际上也就是对DefWindowProc方法的调用。
---- 二:我们可以直接重载DefWindowProc方法,对message类型等于WM_CHAR的,直接修改nChar的值即可。
4.小结
---- 通过对MFC类库的分析和了解,不仅能够使我们更好的使用MFC类库,同时,对于我们自己设计和实现框架和类,无疑也有相当大的帮助。
二.MFC的消息映射机制
MFC的设计者们在设计MFC时,紧紧把握一个目标,那就是尽可能使得MFC的代码要小,速度尽可能快。为了这个目标,他们使用了许多技巧,其中很多技巧体现在宏的运用上,实现MFC的消息映射的机制就是其中之一。
同MFC消息映射机制有关的宏有下面几个:
DECLARE_MESSAGE_MAP()宏
BEGIN_MESSAGE_MAP(theClass, baseClass)和END_MESSAGE_MAP()宏
弄懂MFC消息映射机制的最好办法是将找出一个具体的实例,将这些宏展开,并找出相关的数据结构。
DECLARE_MESSAGE_MAP()
DECLARE_MESSAGE_MAP()宏的定义如下:
#define DECLARE_MESSAGE_MAP() \
private: \
static const AFX_MSGMAP_ENTRY _messageEntries[]; \
protected: \
static AFX_DATA const AFX_MSGMAP messageMap; \
virtual const AFX_MSGMAP* GetMessageMap() const; \
从上面的定义可以看出,DECLARE_MESSAGE_MAP()作下面三件事:
定义一个长度不定的静态数组变量_messageEntries[];
定义一个静态变量messageMap;
定义一个虚拟函数GetMessageMap();
在DECLARE_MESSAGE_MAP()宏中,涉及到MFC中两个对外不公开的数据结构
AFX_MSGMAP_ENTRY和AFX_MSGMAP。为了弄清楚消息映射,有必要考察一下这两个数据结构的定义。
AFX_MSGMAP_ENTRY的定义
struct AFX_MSGMAP_ENTRY
{
UINT nMessage; // windows message
UINT nCode; // control code or WM_NOTIFY code
UINT nID; // control ID (or 0 for windows messages)
UINT nLastID; // used for entries specifying a range of control id's
UINT nSig; // signature type (action) or pointer to message #
AFX_PMSG pfn; // routine to call (or special value)
};
结构中各项的含义注释已经说明得很清楚了,这里不再多述,从上面的定义你是否看出,AFX_MSGMAP_ENTRY结构实际上定义了消息和处理此消息的动作之间的映射关系。因此静态数组变量_messageEntries[]实际上定义了一张表,表中的每一项指定了相应的对象所要处理的消息和处理此消息的函数的对应关系,因而这张表也称为消息映射表。再看看AFX_MSGMAP的定义。
(2)AFX_MSGMAP的定义
line-height: 21px; font-size: 1
分享到:
相关推荐
在MFC中,消息响应机制是实现用户界面与应用程序逻辑交互的关键部分。它基于Windows的消息系统,但提供了一个更加高级的抽象层次,让开发者可以更方便地处理和响应用户操作。 **面向对象的消息映射** 面向对象的...
总的来说,MFC的消息响应机制是通过类的继承和消息映射,将Windows消息转化为面向对象的方法调用,使得开发者能更专注于业务逻辑,而不是底层的消息处理细节。通过深入理解MFC的消息响应机制,开发者可以更好地利用...
本压缩包文件“mfc_ClassWizard.rar”着重介绍了MFC的消息响应机制以及如何利用ClassWizard来处理这些消息。 MFC消息响应机制是基于Windows的消息驱动模型构建的,它允许程序对来自用户或系统的各种事件做出反应。...
MFC框架会根据消息映射机制将这些命令消息分发给不同的对象进行处理,如文档、窗口、应用程序或文档模板等。 #### 小结 通过上述分析,我们可以清楚地了解到MFC消息机制的实现原理及消息处理过程。MFC通过消息映射...
首先,理解MFC消息映射机制的关键在于了解Windows的消息系统。在Windows编程中,所有的用户交互,如点击按钮或拖动窗口,都会生成特定的消息。这些消息会被放入消息队列,然后由消息循环进行处理。MFC通过消息映射...
通过调试和查看消息处理函数的执行过程,可以进一步加深对MFC消息响应机制的理解。 总之,MFC消息响应是Windows编程中的关键部分,它使得程序能够正确响应用户的操作。而纯代码设计对话框则提供了一种灵活的方式来...
在MFC(Microsoft Foundation Classes)框架中,消息处理机制是应用程序的核心组成部分,它定义了如何响应用户交互和其他系统事件。本文将深入探讨MFC中消息响应的顺序、应用程序创建窗口的过程以及窗口关闭和对话框...
MFC 的消息响应机制是 Windows 程序设计中最重要的一部分,它封装了 WIN32 API 函数,并设计了一套方便的消息映射机制。MFC 的消息响应机制使程序员可以更方便地处理 Windows 消息,从而开发出更加高效的程序。 二...
通过观看这些文件,你将能够更好地理解MFC消息映射机制,并掌握如何使用ClassWizard进行实际开发。 总的来说,掌握MFC消息映射机制和ClassWizard的使用,对于提升Windows应用程序的开发效率至关重要。通过深入学习...
理解并熟练掌握MFC的消息映射机制和消息分类对于开发高效、响应迅速的Windows应用程序至关重要。通过合理设计和组织消息映射,开发者可以清晰地知道每个事件如何触发,以及如何执行相应的操作,从而使代码结构更加...
在Microsoft Foundation Classes (MFC)库中,消息映射是一个核心机制,用于处理Windows应用程序中的用户交互和其他系统事件。这个小例子将深入讲解MFC消息映射的概念,并通过创建简单的菜单选项来阐述其工作原理。 ...
在Microsoft Foundation Class (MFC)库中,消息响应机制是应用程序与用户交互的核心部分。MFC消息响应顺序涉及从消息的产生、传递到最终处理的整个流程。以下将详细阐述这个过程。 首先,当Windows系统接收到用户...
在MFC中,消息映射机制是实现用户界面与应用程序逻辑之间交互的关键部分。本课程将深入剖析这一机制,并介绍如何利用ClassWizard工具来高效地处理消息映射。 首先,我们来理解MFC消息映射的概念。在Windows编程中,...
通过阅读压缩包中的“MFC消息响应机制.doc”文档,你可以获得更详细的实践指导,而“深入浅出MFC.pdf”和“VC++深入详解.pdf”则提供了全面的理论知识和案例分析,帮助你从底层到应用层全面理解VC++和MFC的使用。...
在Windows应用程序开发中,特别是使用MFC(Microsoft Foundation Classes)框架时,理解消息映射机制至关重要。 **Windows消息**是一种用于在应用程序与操作系统之间传递信息的数据结构。每当发生一个特定事件,如...
学习这部分源代码,你可以深入理解MFC如何封装和处理Windows消息,以及如何通过消息映射机制来组织代码。同时,对于Windows GUI编程的基础知识,如窗口生命周期、绘图原理、控件交互等,也会有更深入的理解。此外,...
总之,MFC框架下的消息映射机制是Windows应用程序的核心组成部分,它不仅涉及消息的生成、传递和处理,还涉及到应用程序如何组织和响应这些消息,以实现用户界面的实时更新和互动。理解和掌握这一机制对于开发高效...
在MFC中,每个窗口对象(`CWnd`派生类)都有自己的消息映射表(message map),用于定义窗口如何响应各种消息。消息映射表使用`ON_MESSAGE`、`ON_COMMAND`、`ON_BN_CLICKED`等宏定义,将消息与处理函数关联起来。...
首先,理解MFC的消息传递机制,我们需要知道Windows应用程序的基本运行流程。在Windows环境中,用户与应用程序交互主要是通过鼠标和键盘输入,这些输入被转换为消息并放入一个称为消息队列的消息队列中。应用程序的...