苏林

一程序, 一世界

苏林ID:Slin000
72977次访问,排名1327好友50人,关注者61
仔细思考,踏实做事
MSN:hotsoo@hotmail.com
Slin000的文章
原创 28 篇
翻译 0 篇
转载 0 篇
评论 388 篇
苏林的公告
本博客所有文章均为原创,
转载请注明作者和原文地址
最近评论
zidane1983:中国经济确实在经理冬天,我一个做model的朋友,现在失业在家,他们那个行业许多娱乐公司都倒闭了。说明以前有闲钱消费娱乐的人现在都勒紧腰带了。
lzqlgq:严重同意
vvvvip:从奥运赛场到IT,怎么老是中国人在打中国人呢?
vvvvip:到底谁才是真正得流氓我们试了才知道,不过他们这些冤冤相报何时了啊
测试:流氓改不来本性,周利用了法律的漏洞。
文章分类
    收藏
      相册
      友情链接
      爱生活,爱读书--杨福川 图灵
      存档
      订阅我的博客
      XML聚合  FeedSky
      订阅到鲜果
      订阅到Google
      订阅到抓虾
      订阅到BlogLines
      订阅到Yahoo
      订阅到GouGou
      订阅到飞鸽
      订阅到Rojo
      订阅到newsgator
      订阅到netvibes

      原创 透过宏定义了解MFC的消息响应机制收藏

      新一篇: Bug分析之0减1到底等于几 | 旧一篇: MFC框架中WM_COMMAND消息响应顺序

       

      消息系统是MFC的重要组成部分。MFC的消息响应机制并不复杂,而且MFC的开发环境 Visual Studio来供了非常好的自动化工具,自动生成代码。MFC也定义了丰富的宏来简化消息响应的代码。这使得很多初学者都能快速开发出基于消息响应机制的应用程序。然后也正是这些带来方便的宏,使很多人摸不着头脑。当不小心的代码操作使自动化工具不好用的时候,看着那一组更像是一堆的宏,很多人只好一头雾水。
      要想真正了解MFC的消息机制,必需弄清楚这些宏。好在源码面前无秘密,我们将从这些宏的源码着手,逐步分析、了解、并学习MFC的消息响应及映射机制。
      第一个宏:DECLARE_MESSAGE_MAP()
      作用:为一个消息响应类声明必需的成员变量和成员函数。
      我们在窗口类、应用程序类、文档类、视图类、以及这些类的子类的定义中,都能看到DECLARE_MESSAGE_MAP()宏,通常被自动化工具声明在类的最后部分,如:
      // 生成的消息映射函数
      protected:
           DECLARE_MESSAGE_MAP()
      };
      DECLARE_MESSAGE_MAP()
      宏定义如下(在DLL类型和WINDOWS程序类型下,定义会有不同,本文只分析非DLL类型,下同):
      #define DECLARE_MESSAGE_MAP() 
      private
           
      static const AFX_MSGMAP_ENTRY _messageEntries[]; 
      protected
           
      static const AFX_MSGMAP messageMap; 
           
      virtual const AFX_MSGMAP* GetMessageMap() const
      可以看到,宏DECLARE_MESSAGE_MAP()定义了两个静态成员变量,并重载了一个虚函数。下面分析一下这三个成员:
      _messageEntries被定义为一个AFX_MSGMAP_ENTRY类型的数组。结构体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_PTR nSig;       // signature type (action) or pointer to message #
           AFX_PMSG pfn;    // routine to call (or special value)
      };
      通过查看源代码中的注释,可以看出AFX_MSGMAP_ENTRY定义了一个消息入口,或者说定义了一个消息到函数的映射关系。nMessage和nCode确定一条消息的内容,nID和nLastID确定了一条消息的来源,而nSig和pfn确定了消息的响应函数和调用方式。
      通过对消息响应过程的源码分析可知,nSig事实上是一系列编码,每一种编码代表一种响应函数的类型,包括返回值、参数信息等。在响应消息的时候,将把pfn指向的函数指针强制类型转换为nSig代表的函数类型,然后再调用。
      pfn的类型AFX_PMSG定义如下,意为CCmdTarget的成员函数:
      typedef void (AFX_MSG_CALL CCmdTarget::*AFX_PMSG)(void);
      由此我们可以得出:静态成员_messageEntries为是一个消息到函数的映射表,或叫消息入口表。通过查找此表,可以找到消息的响应函数。
      DECLARE_MESSAGE_MAP()宏声明的另一个静态成员变量messageMap被定义为AFX_MSGMAP类型。AFX_MSGMAP定义如下:
      struct AFX_MSGMAP
      {
      #ifdef _AFXDLL
           
      const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
      #else
           
      const AFX_MSGMAP* pBaseMap;
      #endif
           
      const AFX_MSGMAP_ENTRY* lpEntries;
      };
      过滤掉_AFXDLL(当MFC工程是以动态链接库为目标代码编译时,使用_AFXDLL宏)的影响,可以简化为如下:
      struct AFX_MSGMAP
      {
           
      const AFX_MSGMAP* pBaseMap;
           
      const AFX_MSGMAP_ENTRY* lpEntries;
      };
      可见结构体AFX_MSGMAP中定义了两个指针,pBaseMap指向另一个AFX_MSGMAP,lpEntries指向一个消息入口表。可以推想,在响应消息时,一定是在lpEntries指向的消息入口表中寻找响应函数,也可能会在pBaseMap指向的结构体中做同样的响应函数寻找操作。
      至于DECLARE_MESSAGE_MAP()宏重载的虚函数GetMessageMap,可以猜测只是用来返回成员messageMap的地址而已。因为GetMessageMap是虚函数,所以系统只要通过调用消息响应类的基类CCmdTarget类的GetMessageMap函数,便可以找到最后一级子类的消息映射信息。我们将在接下来对其它几个宏的分析中得到相同的结论。
      第二个重要的宏:BEGIN_MESSAGE_MAP
      作用:定义DECLARE_MESSAGE_MAP宏声明的静态变量。
      BEGIN_MESSAGE_MAP定义的源代码如下:
      #define BEGIN_MESSAGE_MAP(theClass, baseClass) 
           
      const AFX_MSGMAP* theClass::GetMessageMap() const 
               { 
      return &theClass::messageMap; } 
           AFX_COMDAT 
      const AFX_MSGMAP theClass::messageMap = 
           { 
      &baseClass::messageMap, &theClass::_messageEntries[0] }; 
           AFX_COMDAT 
      const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = 
           { 
      BEGIN_MESSAGE_MAP宏有两个参数,theClass表示为当前类,bassClass为当前类的父类。
      BEGIN_MESSAGE_MAP宏首先定义了函数GetMessageMap的函数体,如前文所述,直接返回当前类的成员变量messageMap的地址。
      const AFX_MSGMAP* theClass::GetMessageMap() const 
               { 
      return &theClass::messageMap; } 
      然后初始化了当前类的成员变量messageMap。messageMap的pBaseMap指针指向其父类的messageMap成员,lpEntries指针指向当前类的_messageEntries数组的首地址。
      AFX_COMDAT const AFX_MSGMAP theClass::messageMap = 
           { 
      &baseClass::messageMap, &theClass::_messageEntries[0] };
      最后,定义了_messageEntries数组初始化代码的开始部分。
      AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] = 
           { 
      第三个宏END_MESSAGE_MAP()
      作用:定义_messageEntries数组初始化代码的结束部分。
      #define END_MESSAGE_MAP() 
               {
      0000, AfxSig_end, (AFX_PMSG)0 } 
           }; 
      DECLARE_MESSAGE_MAP和END_MESSAGE_MAP之间还有一些宏,如ON_COMMAND、ON_WM_CREATE等,这些宏最终都会被生成一条AFX_MSGMAP_ENTRY结构体数据,并成为_messageEntries消息映射表数据的一个元素。我们以常见的ON_COMMAND宏为例。ON_COMMAND宏的源代码为:
      #define ON_COMMAND(id, memberFxn) 
           { WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, 
               static_cast
      <AFX_PMSG> (memberFxn) },
      通过以上分析,我们可以得到一个链表式的数据结构,子类的messageMap成员为链表的头节点。链表的每个节点都包含一个消息入口表。MFC的消息系统的标准备消息处理函数CCmdTarget::OnCmdMsg正是通过这样一个链表查找到消息的响应函数,并调用该函数来响应消息。

      作者:苏林 

      发表于 @ 2008年01月15日 17:57:00|评论(loading...)|编辑

      新一篇: Bug分析之0减1到底等于几 | 旧一篇: MFC框架中WM_COMMAND消息响应顺序

      评论

      #glacierLan 发表于2008-01-16 09:57:23  IP: 124.42.78.*
      最后那张图片是个叉叉,看不到啊。能再上传一下么
      #Slin000 发表于2008-01-16 10:21:56  IP: 210.13.74.*
      好了,已经改好了
      CSDN程序使用的是相对地址, 改为绝对地址就可以了
      实际地址是 writeblog.csdn.net/images/slin000/....
      系统使用的是相对地址/images/slin000/...,
      在blog.csdn.net/slin000下看文章就看不到图片了
      谢谢
      #carr123 发表于2008-01-16 18:33:23  IP: 218.94.57.*
      这么复杂的宏定义看着都累,何况用呢.
      #060 发表于2008-01-16 20:32:05  IP: 218.56.58.*
      一般情况下不会用到这些宏, 这些后都有向导自动生成。知道它们的作用就行了。
      #test 发表于2008-01-16 22:34:15  IP: 58.217.179.*
      人家说了几百遍的东西,你在说一遍又有什么意义呢。
      2008-01-17 09:13:31作者回复
      又有什么东西是人家没说过的呢?<br />去社区里看看,又有几个人在问没人说过的问题呢?<br />先知道的人应该努力把自己知道的告诉还没有知道的人。<br />每天都有很多新手出现,相信写这些东西会给他们带来好处。
      #yueguangbuxiu 发表于2008-07-30 16:44:20  IP: 192.168.0.*
      一个只会windows,以及一些Mfc的人,能有多大出息,看你的ID也快6-7年了,你也就这样的水平?
      发表评论  


      登录
      Csdn Blog version 3.1a
      Copyright © 苏林