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) 
  };

  typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);

结构中各项的含义注释已经说明得很清楚了,这里不再多述,从上面的定义你是否看出,AFX_MSGMAP_ENTRY结构实际上定义了消息和处理此消息的动作之间的映射关系。因此静态数组变量_messageEntries[]实际上定义了一张表,表中的每一项指定了相应的对象所要处理的消息和处理此消息的函数的对应关系,因而这张表也称为消息映射表。再看看AFX_MSGMAP的定义。 
  (2)AFX_MSGMAP的定义 
  struct AFX_MSGMAP 
  { 
   const AFX_MSGMAP* pBaseMap; 
   const AFX_MSGMAP_ENTRY* lpEntries; 
   }; 

不难看出,AFX_MSGMAP定义了一单向链表,链表中每一项的值是一指向消息映射表的指针(实际上就是_messageEntries的值)。通过这个链表,使得在某个类中调用基类的的消息处理函数很容易,因此,“父类的消息处理函数是子类的缺省消息处理函数”就“顺理成章”了。在后面的“MFC窗口的消息处理”一节中会对此作详细的讲解。 

  BEGIN_MESSAGE_MAP()和END_MESSAGE_MAP() 

  它们的定义如下: 
  #define BEGIN_MESSAGE_MAP(theClass, baseClass) / 
  const AFX_MSGMAP* theClass::GetMessageMap() const / 
  { return &theClass::messageMap; } / 
  AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap = / 
  { &baseClass::messageMap, &theClass::_messageEntries[0] }; / 
  AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = / 
  { / 
   #define END_MESSAGE_MAP() / 
   {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } / 
   }; /
对应BEGIN_MESSAGE_MAP()的定义可能不是一下子就看得明白,不过不要紧,举一例子就很清楚了。对于BEGIN_MESSAGE_MAP(CView, CWnd),VC预编译器将其展开成下面的形式: 
  const AFX_MSGMAP* CView::GetMessageMap() const 
  {  
   return &CView::messageMap;  
   } 
  AFX_COMDAT AFX_DATADEF const AFX_MSGMAP CView::messageMap = 
  {  
   &CWnd::messageMap,  
   &CView::_messageEntries[0]  
  }; 
  AFX_COMDAT const AFX_MSGMAP_ENTRY CView::_messageEntries[] = 
  { 
  至于END_MESSAGE_MAP()则不过定义了一个表示映射表结束的标志项,我想大家对于这种简单的技巧应该是很熟悉的,无需多述。 
到此为止,我想大家也已经想到了象ON_COMMAND这样的宏的具体作用了,不错它们只不过定义了一种类型的消息映射项,看看ON_COMMAND的定义: 
  #define ON_COMMAND(id, memberFxn) / 
  { WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)&memberFxn }, 
   根据上面的定义,ON_COMMAND(ID_FILE_NEW, OnFileNew)将被VC预编译器展开 
   如下: 
  {WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv,  
  (AFX_PMSG)&OnFileNew}, 
到此,MFC的消息映射机制已经清楚了,现在提出并解答两个问题以作为对这一节的小结。 

  为什么不直接使用虚拟函数实现消息处理函数呢?这是一个GOOD QUESTION。前面已经说过,MFC的设计者们在设计MFC时有一个很明确的目标,就是使得“MFC的代码尽可能小,速度尽可能快”,如果采用虚拟函数,那么对于所有的窗口消息,都必须有一个与之对应的虚拟函数,因而对每一个从CWnd派生的类而言,都会有一张很大的虚拟函数表vtbl。但是在实际应用中,一般只对少数的消息进行处理,大部分都交给系统缺省处理,所以表中的大部分项都是无用项,这样做就浪费了很多内存资源,这同MFC设计者们的设计目标是相违背的。当然,MFC所使用的方法只是解决这类问题的方式之一,不排除还有其他的解决方式,但就我个人观点而言,这是一种最好的解决方式,体现了很高的技巧性,值得我们学习。

时间: 2025-01-13 16:14:02

MFC的消息映射机制揭秘的相关文章

C++MFC编程笔记day02 MFC消息映射机制、菜单资源使用

机制3:MFC消息映射机制: 类内声明,类外定义宏,绑定消息处理函数派生自CCmdTarget类内声明宏:DECLARE_MESSAGE_MAP()类外添加实现宏:BEGIN_MESSAGE_MAP(类名,父类名)END_MESSAGE_MAP() //数据结构 struct AFX_MSGMAP_ENTRY {UINT nMessage;   // 消息IDUINT nCode;      // 通知码UINT nID;        // 控件ID或消息UINT nLastID;    //

孙鑫VC++讲座笔记-(4)MFC消息映射机制的剖析

孙鑫VC++讲座笔记-(4)MFC消息映射机制的剖析 一,消息映射机制 1,消息响应函数:(例:在CDrawView类响应鼠标左键按下消息) 1)在头文件(DrawView.h)中声明消息响应函数原型. //{{AFX_MSG(CDrawView) //注释宏 afx_msg void OnLButtonDown(UINT nFlags, CPoint point); //}}AFX_MSG //注释宏 说明: 在注释宏之间的声明在VC中灰色显示.afx_msg宏表示声明的是一个消息响应函数.

MFC深入浅出-消息映射的实现

消息映射的实现   Windows消息概述   Windows 应用程序的输入由Windows系统以消息的形式发送给应用程序的窗口.这些窗口通过窗口过程来接收和处理消息,然后把控制返还给Windows.   消息的分类   队列消息和非队列消息   从消息的发送途径上看,消息分两种:队列消息和非队列消息.队列消息送到系统消息队列,然后到线程消息队列:非队列消息直接送给目的窗口过程. 这里,对消息队列阐述如下: Windows 维护一个系统消息队列(System message queue),每个

c++-C++ 6.0关于函数的消息映射机制的发问

问题描述 C++ 6.0关于函数的消息映射机制的发问 看了一些文章介绍,C++ 6.0的消息映射以后,函数接收到消息处理以后就返回系统控制权了,但是能得到当前消息的上一个消息么?我想知道这个消息是否执行过了 解决方案 http://blog.csdn.net/hxh129/article/details/9313897

mfc-visual c++中,MFC的消息映射宏背后的实现原理搞不明白?有谁能解释一下宏的知识。

问题描述 visual c++中,MFC的消息映射宏背后的实现原理搞不明白?有谁能解释一下宏的知识. BEGIN_MESSAGE_MAP() ...... ON_COMMAND() ........ END_MESSAGE_MAP() 这背后怎么执行,生成的,完全不知所云,只是想了解一下的原理,这样用是会用,但是不明不白 的,心里有些疑惑. 解决方案 MFC消息映射BEGIN_MESSAGE_MAP详解 解决方案二: MFC消息映射BEGIN_MESSAGE_MAP详解,我就是看的这个,http

MFC的消息反射机制

1.消息反射解释: 父窗口将子窗口发给它的通知消息,首先反射回子窗口进行处理(即给子窗口一个机会,让子窗口处理此消息),这样通知消息就有机会能被子窗口自身进行处理.   2.MFC中引入消息反射的原因: 在Windows的消息处理中,子窗口的发给其父窗口的通知消息只能由其父窗口进行处理,这使得子窗口的自身能动性大大降低(你想,它连改变自己的背景色,处理一个自身滚动问题都要其父窗口来完成),为了解决这个问题,在MFC中引入了 反射消息 "Reflect Message"的概念,进行消息反

mfc-MFC的消息映射采用机制问题

问题描述 MFC的消息映射采用机制问题 MFC的消息映射函数是通过消息映射宏实现的,这个消息映射宏的作用是什么?我觉得用了宏代码就复杂了,它的意义是什么? 解决方案 http://blog.csdn.net/liufei_learning/article/details/5903287 解决方案二: MFC消息映射机制MFC消息映射机制MFC消息映射机制

MFC消息映射的原理:笔记

多态的实现机制有两种,一是通过查找绝对位置表,二是查找名称表:两者各有优缺点,那么为什么mfc的消息映射采用了第二种方法,而不是c++使用的第一种呢?因为在mfc的gui类库是一个庞大的继承体系,而里面的每个类有很多成员函数(只说消息反映相关的成员函数啊),而且在派生类中,需要改写的也比较少(我用来做练习的程序就是那么一两个,呵呵).那么用c++的虚函数的实现机制会导致什么问题呢?就是大量虚表的建立使得空间浪费掉很多.   嗯-怎么办呢?于是各大c++名库(比如QT,MFC,VCL-)在消息映射

MFC技术内幕系列之(四)---MFC消息映射与消息传递内幕

 ////////////////////////////////////////////////////////////////////////////////////                     /********* 文章系列:MFC技术内幕系列***********/                     /************MFC技术内幕系列之(四)***********/                     /*****文章题目:MFC消息映射与消息传递内幕**