WINDOWS消息机制(四)--MFC消息网络整体布局

该文主要介绍MFC中各对象之间继承体系建立的过程中随之建立的消息网络整体布局,会介绍如下问题:

1. 单个对象中的消息MAP如何形成
2. 具有继承关系的类的消息MAP如何建立关系

1. 单个MFC类中的消息MAP如何形成

是不是每个类中都有消息MAP呢,不是的,看有没有如下宏定义:

DECLARE_MESSAGE_MAP()
BEGIN_MESSAGE_MAP(CMsgMapView, CView)
        ......
	ON_COMMAND(ID_FILE_PRINT, &CView::OnFilePrint)
	......
END_MESSAGE_MAP()

上文中是一对宏定义,看看这些宏背后究竟是什么东西?转到相关定义可以看到
DECLARE_MSG_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的结构如下:

WINDOWS消息机制(四)--MFC消息网络整体布局_第1张图片
那消息和处理函数是怎么填充到消息映射表中的呢?是由如下宏定义实现的。

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


2. MFC中各对象之间消息网络的构建

我们在MAP中看到有pBaseMsgMap,意味指向基类的消息MAP,那如何让该指针指向父类的消息MAP呢?
在END_MESSAGE_MAP宏定义中,有如下定义:
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;
	}
}

然后单步调试,可以看到消息之间的关系:WINDOWS消息机制(四)--MFC消息网络整体布局_第2张图片


从上图可以看到,CMyApp消息MAP的BASE指向CWinApp的消息MAP;
而CWinApp的消息MAP的BASE是CCmdTarget的消息MAP.跳过了CWinThread 所以MFC的单文档视图的消息网络结构如下:

WINDOWS消息机制(四)--MFC消息网络整体布局_第3张图片

那么消息网络的整体结构就看以看出来了

注:在下文中,会解释消息是如何推进消息网络,并寻找到自己的处理函数

参考:《深入浅出MFC》

你可能感兴趣的:(WINDOWS消息机制,MFC消息网络的构建)