MFC 消息映射、分派和传递
几个重要的结构体:
struct AFX_MSGMAP { AFX_MSGMAP* pBaseMessageMap; AFX_MSGMAP_ENTRY* lpEntries; }
struct AFX_MSGMAP_ENTRY { UINT nMessage;// Windows消息 UINT nCode;// 控制消息的通知码 UINT nID;// 其控制组件的ID UINT nLastID;// 如果是一定范围的消息映射,此值表示区间的最大值 UINT nSig; // 消息的动作标识 AFX_MSG pfn;// 消息响应函数 }
此结构体的数据由任何一个ON_宏都会把这六个数据初始化,例如 此处的ON_WM_CREATE 和 下面提到的ON_COMMAND:
#define ON_WM_CREATE() \ { WM_CREATE, 0, 0, 0, AfxSig_is, (AFX_MSG)(AFX_PMSGW)(int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT)OnCreate) },
AFX_PMSG 是一个函数指针:
typedef void (CCmdTarget::* AFX_PMSG)(void);
定义一个DECLARE_MESSAGE_MAP()宏
#define DECLARE_MESSAGE_MAP() \ static AFX_MSGMAP_ENTRY _messageEntries[] ;\ static AFX_MSGMAP messageMap; \ virtual AFX_MSGMAP* GetMessageMap() const;
于是DECLARE_MESSAGE_MAP宏就相当于声明了下面的数据结构:
这个数据结构的内容填充工作由下面三个宏完成:
#define BEGIN_MESSAGE_MAP (theClass,baseClass) \ AFX_MSGMAP* theClass::GetMessageMap() const \ { return &theClass::messageMap; }\ AFX_MSGMAP theClass::messageMap = \ { &(baseClass::messageMap), \ (AFX_MSGMAP_ENTRY*) &(theClass::_messageEntries) ; \ } \ AFX_MSGMAP_ENTRY theClass::_messageEntries[] = \ {
#define ON_COMMAND(id,memberFxn) \ { WM_COMMAND, 0, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)memberFxn },
#define END_MESSAGE_MAP() \ { 0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0} \ };
这个AfxSig枚举的具体作用是什么呢?
上面的宏ON_WM_CREATE和ON_COMMAND也有出现类似的AfxSig_,那么到底作何用处?
从上面两个宏的最后一个参数可以看出,表示的是具体的执行函数,且都进行了类型转换,最终转换为AFX_PMSG.
可是AFX_PMSG的定义呢?我们再写一遍。
typedef void (CCmdTarget::* AFX_PMSG)(void);
这是一个什么鬼?无参,无返回值,不可能所有的函数都如此吧?
这正是AfxSig_的作用所在,当需要调用AFX_MSGMAP_ENTRY中的消息响应函数pfn的时候,具体的动作是这样的(出现在wincore.cpp CWnd::OnWndMsg 和 DispatchCmdMsg中):
union MessageMapFunctions mmf; mmf.pfn = lpEntry->pfn; // lpEntry 是AFX_MSGMAP_ENTRY的对象 switch(lpEntry->nSig) { case AfxSig_is: lResult = (this->*mmf.pfn_is)((LPTSTR)lParam); break; case AfxSig_lwl: lResult = (this->*mmf.pfn_lwl)(wParam, lParam); break; case AfxSig_vv: lResult = (this->*mmf.pfn_vv)(); break; ..... }
重点注意: MessageMapFunctions 和AfxSig_. AfxSig_ 定义位于AFXMSG_.H档。
enum AfxSig { AfxSig_end = 0, // [marks end of message map] AfxSig_b_D_v, // BOOL (CDC*) AfxSig_b_b_v, // BOOL (BOOL) AfxSig_b_u_v, // BOOL (UINT) AfxSig_b_h_v, // BOOL (HANDLE) AfxSig_b_W_uu, // BOOL (CWnd*, UINT, UINT) AfxSig_b_W_COPYDATASTRUCT, // BOOL (CWnd*, COPYDATASTRUCT*) AfxSig_b_v_HELPINFO, // BOOL (LPHELPINFO); AfxSig_CTLCOLOR, // HBRUSH (CDC*, CWnd*, UINT) AfxSig_CTLCOLOR_REFLECT, // HBRUSH (CDC*, UINT) AfxSig_i_u_W_u, // int (UINT, CWnd*, UINT) // ?TOITEM AfxSig_i_uu_v, // int (UINT, UINT) AfxSig_i_W_uu, // int (CWnd*, UINT, UINT) AfxSig_i_v_s, // int (LPTSTR) AfxSig_l_w_l, // LRESULT (WPARAM, LPARAM) AfxSig_l_uu_M, // LRESULT (UINT, UINT, CMenu*) .... // 后面很多不列举 };
MessageMapFunctions 定义于WINCORE.cpp 中:
union MessageMapFunctions { AFX_PMSG pfn; // generic member function pointer BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_D)(CDC*); BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_b)(BOOL); BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_u)(UINT); BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_h)(HANDLE); BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_W_u_u)(CWnd*, UINT, UINT); BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_W_COPYDATASTRUCT)(CWnd*, COPYDATASTRUCT*); BOOL (AFX_MSG_CALL CCmdTarget::*pfn_b_HELPINFO)(LPHELPINFO); HBRUSH (AFX_MSG_CALL CCmdTarget::*pfn_B_D_W_u)(CDC*, CWnd*, UINT); HBRUSH (AFX_MSG_CALL CCmdTarget::*pfn_B_D_u)(CDC*, UINT); int (AFX_MSG_CALL CCmdTarget::*pfn_i_u_W_u)(UINT, CWnd*, UINT); int (AFX_MSG_CALL CCmdTarget::*pfn_i_u_u)(UINT, UINT); int (AFX_MSG_CALL CCmdTarget::*pfn_i_W_u_u)(CWnd*, UINT, UINT); int (AFX_MSG_CALL CWnd::*pfn_i_s)(LPTSTR); int (AFX_MSG_CALL CWnd::*pfn_i_S)(LPCTSTR); LRESULT (AFX_MSG_CALL CWnd::*pfn_l_w_l)(WPARAM, LPARAM); LRESULT (AFX_MSG_CALL CWnd::*pfn_l_u_u_M)(UINT, UINT, CMenu*);
.... // 后面很多不列举
};
其实真正的函数只有pfn一个,但通过union后,它就有了许多类型不同的形象。这里可以看出union自动转型的作用。
//***********************************************************************************************
上面讲基本内容解释完毕,下面以实例来演示宏展开后的结果:
//in header file class CView : public CWnd { public: //... DECLARE_MESSAGE_MAP() }; //in implementation file #define CViewid 122 //... BEGIN_MESSAGE_MAP(CView, CWnd) ON_COMMAND(CViewid, 0) END_MESSAGE_MAP //被展开之后为 //in header file class CView : public CWnd { public: //... static AFX_MSGMAP_ENTRY _messageEntries[]; static AFX_MSGMAP messageMap; virtual AFX_MSGMAP* GetMessageMap() const; }; //in implementation file AFX_MSGMAP* CView::GetMessageMap() const { return &CView::messageMap; } AFX_MSGMAP CView::messageMap = { &(CWnd::messageMap), (AFX_MSGMAP_ENTRY*)&(CView::_messageEntries) }; AFX_MSGMAP_ENTRY CView::_messageEntries[] = { {WM_COMMAND, 0, (WORD)122, (WORD)122, 1, (AFX_MSGMAP)0}, {0, 0, 0, 0, 0, (AFX_MSGMAP)0} };
以图表示则为:
MFC中定义了各种类似ON_COMMAND这样的宏,把各式各样的消息与特定的消息处理函数关联起来。
下面的代码中每一个CCmdTarget的衍生类都产生类似上图的消息映射表:
//in head file class CObject { //.... //注意:CObject并不属于消息流动网的一份子 }; class CCmdTarget :public CObject { //... DECLARE_MESSAGE_MAP() }; class CWinThread :public CCmdTarget { //... //注意:CWinThread并不属于消息流动网的一份子 }; class CWinApp :public CWinThread { //... DECLARE_MESSAGE_MAP() }; class CDocument :public CCmdTarget { //... DECLARE_MESSAGE_MAP() }; class CWnd :public CCmdTarget { //... DECLARE_MESSAGE_MAP() }; class CFrameWnd :public CWnd { //... DECLARE_MESSAGE_MAP() }; class CView :public CWnd { //... DECLARE_MESSAGE_MAP() }; class CView :public CWnd { //... DECLARE_MESSAGE_MAP() }; class CMyWinApp :public CWinApp { //... DECLARE_MESSAGE_MAP() }; class CMyFrameWnd :public CFrameWnd { //... DECLARE_MESSAGE_MAP() }; class CMyDoc :public CDocument { //... DECLARE_MESSAGE_MAP() }; class CMyView :public CView { //... DECLARE_MESSAGE_MAP() };
//in implementation file BEGIN_MESSAGE_MAP(CWnd, CCmdTarget) ON_COMMAND(CWndid, 0) END_MESSAGE_MAP BEGIN_MESSAGE_MAP(CFrameWnd, CWnd) ON_COMMAND(CFrameWndid, 0) END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CDocument, CCmdTarget) ON_COMMAND(CDocumentid, 0) END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CView, CWnd) ON_COMMAND(CViewid, 0) END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CWinApp, CCmdTarget) ON_COMMAND(CWinAppid, 0) END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CMyWinApp, CWinApp) ON_COMMAND(CMyWinAppid, 0) END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd) ON_COMMAND(CMyFrameWndid, 0) END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CMyDoc, CDocument) ON_COMMAND(CMyDocid, 0) END_MESSAGE_MAP() BEGIN_MESSAGE_MAP(CMyView, CView) ON_COMMAND(CMyViewid, 0) END_MESSAGE_MAP() //同时也设定了消息的终极标靶CCmdTarget的映射表内容: AFX_MSGMAP CCmdTarget::messageMap = { NULL, &CCmdTarget::_messageEntries[0] }; AFX_MSGMAP_ENTRY CCmdTarget::_messageEntries = { {0, 0, CCmdTargetid, 0, AfxSig_end, 0 } }
以上构成了完整的消息流动网,如下图: