牧光小院

被约束的日日夜夜,停不下来的时间。

MFC漫谈(三)——消息映射

MFC中的消息循环呢?我们熟悉的switch……case……到哪里去了?



在MFC中,消息的循环并不是用switch……case……实现的,它依赖于一张由程序自身定义的消息网。

首先,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_PTR 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

struct  AFX_MSGMAP  {
  
const  AFX_MSGMAP *
 pBaseMap;
  
const  AFX_MSGMAP_ENTRY *
 lpEntries;
}
;

这样一个AFX_MSGMAP对象就成了构建消息映射表的关键人物,它一只手拉着基类的AFX_MSGMAP对象,另一只手拉着类本身的消息映射表,这样只要正确地在每一个类中都安插一个AFX_MSGMAP对象,那么整个消息映射表就建立起来了。那么,何为正确呢?含义有2:一是正确的设置pBaseMap,令它指向基类,二是正确的建立类自身的消息映射表。这两个工作是由4个宏完成的,

它们是:DECLARE_MEMSSAGE_MAP() / BEGIN_MESSAGE_MAP() / ON_COMMAND()(注:ON_COMMAND宏只是为了处理命令消息,对于其它的消息还有对应的宏,但是原理是相同的) / END_MESSAGE_MAP()。
让我们一个个的看看:

#define  DECLARE_MESSAGE_MAP() \
private : \
  
static   const
 AFX_MSGMAP_ENTRY _messageEntries[]; \
protected
: \
  
static   const
 AFX_MSGMAP messageMap; \
  
virtual   const  AFX_MSGMAP *  GetMessageMap()  const ; \

这个宏的作用有3:
 1. 在类中插入一个静态成员_messageEntries,这是用来存放类要处理的消息的数组(即类本身的消息映射表)
 2.  另一个静态成员massageMap用来指向基类的消息映射表
 3. 安插一个虚函数,其内容有待实现

接下来,_messageEntries的初始化,messageMap的正确指向,GetMessageMap函数的实现这些工作还都没做,那正是后三个宏的责任,它们要顺序使用,方能工作正常。

#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[]  =
 \
 
{ \

这个宏的作用有3:
1. 定义了安插在类中的虚函数GetMessageMap(),只是简单的返回messageMap对象的地址
2. 初始化messageMap,把派生类和基类联系起来构成一个大的消息映射表
3. 为类本身的消息映射表的初始化做语法准备

ON_COMMAND这个宏的作用就是向_messageEntries数组中添加类本身要处理的命令消息,其实在MFC中还有很多更方便的宏可以向类中添加消息,例如OM_WM_PAINT等,这里,我们主要讨论ON_COMMAND,毕竟原理都是相同的。

#define  ON_COMMAND(id, memberFxn) \
{ WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, \
  static_cast
< AFX_PMSG >  (memberFxn) }
,

无非是对AFX_MSG_ENTRY结构的初始化,这样在类中为每一个想要处理的消息都是用一个ON_COMMAND宏,就自动的初始化了类本身的消息映射表。

最后,当全部的信息添加完毕后,使用END_MESSAGE_MAP()宏通知MFC一个类消息映射表结束了。

#define  END_MESSAGE_MAP() \
  
{ 0 0 0 0 , AfxSig_end, (AFX_PMSG) 0  }  \
 }; \

实现手法单纯得很,无非是一个全0的AFX_MESSAGE_MAP对象。

结论
想要让你的类处理某个消息,使用下面的组合:

BEGIN_MESSAGE_MAP(theClass, the  base  Class)
// 消息处理宏

END_MESSAGE_MAP()

(待续……)

posted on 2006-05-18 16:31 nacci 阅读(3729) 评论(4)  编辑 收藏 引用 所属分类: C++漫谈

评论

# re: MFC漫谈(三)——消息映射 2007-09-27 10:27 nono

Very good!看了好几篇消息映射的文章,到这里终于能看明白了。  回复  更多评论   

# re: MFC漫谈(三)——消息映射 2007-09-30 17:55 shirb

Very good!看了好几篇消息映射的文章,到这里终于能看明白了。  回复  更多评论   

# re: MFC漫谈(三)——消息映射 2007-10-11 15:34 seamonst

明了。  回复  更多评论   

# re: MFC漫谈(三)——消息映射 2010-12-05 22:44 NNF

剖析地很好  回复  更多评论   


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理


<2010年12月>
2829301234
567891011
12131415161718
19202122232425
2627282930311
2345678

导航

统计

常用链接

留言簿(2)

随笔分类

收藏夹

大家的声音

积分与排名

最新评论

阅读排行榜

评论排行榜