WINDOWS消息機制(四)--MFC消息網絡總體佈局

該文主要介紹MFC中各對象之間繼承體系創建的過程當中隨之創建的消息網絡總體佈局,會介紹以下問題:windows

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的結構以下:spa

那消息和處理函數是怎麼填充到消息映射表中的呢?是由以下宏定義實現的。

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 獲取父類消息映射表的地址,這樣子類就和基類的消息映射表之間創建起關係了!code

&messageEntries[0]就是子類本身消息映射表的首地址! 而其餘的方法,getMessageMap,就不解釋了哈,根據字面意思就能看出來了。 orm

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

而後單步調試,能夠看到消息之間的關係:


從上圖能夠看到,CMyApp消息MAP的BASE指向CWinApp的消息MAP;
而CWinApp的消息MAP的BASE是CCmdTarget的消息MAP.跳過了CWinThread 因此MFC的單文檔視圖的消息網絡結構以下:

那麼消息網絡的總體結構就看以看出來了

注:在下文中,會解釋消息是如何推動消息網絡,並尋找到本身的處理函數

參考:《深刻淺出MFC》

相關文章
相關標籤/搜索