该文主要介绍MFC中各对象之间继承体系建立的过程中随之建立的消息网络整体布局,会介绍如下问题:
1. 单个对象中的消息MAP如何形成
2. 具有继承关系的类的消息MAP如何建立关系
DECLARE_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(CMsgMapView, CView) ...... ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint) ...... END_MESSAGE_MAP()
上文中是一对宏定义,看看这些宏背后究竟是什么东西?转到相关定义可以看到
#define DECLARE_MESSAGE_MAP() \ protected: \ static const AFX_MSGMAP* PASCAL GetThisMessageMap(); \ virtual const AFX_MSGMAP* GetMessageMap() const; \
BEGIN_MESSAGE_MAP的宏定义
#define BEGIN_MESSAGE_MAP(theClass, baseClass) \ PTM_WARNING_DISABLE \ const AFX_MSGMAP* theClass::GetMessageMap() const \ { return GetThisMessageMap(); } \ const AFX_MSGMAP* PASCAL theClass::GetThisMessageMap() \ { \ typedef theClass ThisClass; \ typedef baseClass TheBaseClass; \ static const AFX_MSGMAP_ENTRY _messageEntries[] = \ {
END_MESSAGE_MAP的宏定义
#define END_MESSAGE_MAP() \ {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } \ }; \ static const AFX_MSGMAP messageMap = \ { &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }; \ return &messageMap; \ } \ PTM_WARNING_RESTORE
先看宏定义中涉及到的几个结构体:
消息MAP表的定义:有两个指针,一个指针指向父类的MSGMAP,一个指向自己的MSGMAP_ENTRY数组
struct AFX_MSGMAP { const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)(); const AFX_MSGMAP_ENTRY* lpEntries; };
表中的单项是如何定义:其中定义了消息的信息以及对应的处理函数(AFX_PMSG 函数指针)
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以及AFX_MSGMAP可以知道,在单个类中,消息MAP的结构如下:
BEGIN_MESSAGE_MAP(CMsgMapView, CView) // 标准打印命令 ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint) ...... END_MESSAGE_MAP()
#define ON_COMMAND(id, memberFxn) \ { WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, \ static_cast<AFX_PMSG> (memberFxn) },
我们用代码的形式,展现出来,其实在BEGIN_MSESSAGE_MAP和END_MESSAGE_MAP之间,那些ON_COMMAND,ON_MESSAGE,就把相关信息及其处理填充到映射表中了。
static const AFX_MSGMAP_ENTRY _messageEntries[] = { {WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, static_cast<AFX_PMSG> (memberFxn)}, {WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSigCmd_v, static_cast<AFX_PMSG> (memberFxn)},{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } }
static const AFX_MSGMAP messageMap = { &TheBaseClass::GetThisMessageMap, &_messageEntries[0] }
在该定义中可以看出,消息映射表是一个静态变量,所有该类的对象共享该消息映射表。此处定义并初始化了消息映射表。
&TheBaseClass::GetThisMessageMap 获取父类消息映射表的地址,这样子类就和基类的消息映射表之间建立起关系了!
&messageEntries[0]就是子类自己消息映射表的首地址! 而其他的方法,getMessageMap,就不解释了哈,根据字面意思就能看出来了。
在CWinThread中,没有DECLARE_MESSAGE_MAP(),那就意味着CWinThread中没有消息映射表,但是CWinApp的父类是 CWinThread,这样的话消息映射表不是断掉了吗? 从BEGIN_MESSAGE_MAP(theClass, baseClass)可以看到消息MAP的关系, 但是没翻到相关代码,于是自己写了个程序,跟踪了下:
void CMsgMapApp::OnTestShowMsgMapAction() { // TODO: 在此添加命令处理程序代码 const AFX_MSGMAP* pMsgMap = GetMessageMap(); for (; pMsgMap != NULL; pMsgMap = pMsgMap->pfnGetBaseMap()) { const AFX_MSGMAP_ENTRY* pMsgEntry = pMsgMap->lpEntries; } }
那么消息网络的整体结构就看以看出来了
注:在下文中,会解释消息是如何推进消息网络,并寻找到自己的处理函数参考:《深入浅出MFC》