MFC六大机制(三)消息映射机制
一、消息映射机制的使用
1、必须由CCmdTarget类直接派生或者间接派生
2、在类内写声明宏DECLARE_MESSAGE_MAP()
3、在类外写实现宏BEGIN_MESSAGE_MPA(CMainFrameWnd, CFrameWnd)
二、写一个简单的窗口应用程序
1 //窗口框架类 2 class CMainFrameWnd : public CFrameWnd 3 { 4 //DECLARE_MESSAGE_MAP() 5 private: 6 static const AFX_MSGMAP_ENTRY _messageEntries[]; 7 /*struct AFX_MSGMAP_ENTRY 8 { 9 UINT nMessage; // 消息ID 10 UINT nCode; // 通知码 11 UINT nID; // 命令ID/控件ID 12 UINT nLastID; // 最后一个控件ID 13 UINT nSig; // 处理消息的函数的类型 14 AFX_PMSG pfn; // 处理消息的函数的地址 15 };*/ 16 protected: 17 static AFX_DATA const AFX_MSGMAP messageMap; 18 /*struct AFX_MSGMAP 19 { 20 21 const AFX_MSGMAP* pBaseMap; //获取父类静态变量地址 22 const AFX_MSGMAP_ENTRY* lpEntries; //获取相应类的静态数组首地址 23 };*/ 24 virtual const AFX_MSGMAP* GetMessageMap() const; 25 protected: 26 afx_msg int OnCreate( LPCREATESTRUCT lpCreateStruct ); 27 }; 28 //BEGIN_MESSAGE_MAP(CMainFrameWnd, CFrameWnd) 29 30 //获取本类的静态变量地址(获取链表的头结点) 31 const AFX_MSGMAP* CMainFrameWnd::GetMessageMap() const 32 { 33 return &CMainFrameWnd::messageMap; 34 } 35 36 AFX_COMDAT AFX_DATADEF const AFX_MSGMAP CMainFrameWnd::messageMap = 37 { 38 &CFrameWnd::messageMap, 39 &CMainFrameWnd::_messageEntries[0] 40 }; 41 42 AFX_COMDAT const AFX_MSGMAP_ENTRY CMainFrameWnd::_messageEntries[] = 43 { 44 //ON_WM_CREATE() 45 { WM_CREATE, 0, 0, 0, AfxSig_is, 46 (AFX_PMSG)(AFX_PMSGW)(int (AFX_MSG_CALL CWnd::*)(LPCREATESTRUCT))&OnCreate }, 47 //END_MESSAGE_MAP() 48 {0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 } 49 }; 50 51 int CMainFrameWnd::OnCreate( LPCREATESTRUCT lpCreateStruct ) 52 { 53 AfxMessageBox("创建窗口消息"); 54 return CFrameWnd::OnCreate(lpCreateStruct); 55 } 56 57 //应用程序类 58 class CMyWinApp : public CWinApp 59 { 60 public: 61 CMyWinApp(void); 62 virtual BOOL InitInstance(void); 63 }; 64 65 CMyWinApp::CMyWinApp(void) 66 { 67 68 } 69 70 CMyWinApp theApp;//唯一的全局应用程序对象 71 72 BOOL CMyWinApp::InitInstance(void) 73 { 74 CMainFrameWnd *pFrame = new CMainFrameWnd; 75 pFrame->Create(NULL, "MFCSummaryProcess"); 76 m_pMainWnd = pFrame; 77 m_pMainWnd->ShowWindow(SW_SHOW); 78 m_pMainWnd->UpdateWindow(); 79 return TRUE; 80 }
从上面的两个宏展开可以看出,在类中添加了一个静态数组,一个静态变量,和一个虚函数。
1) 静态数组:其元素类型为一个结构体,结构体里面主要是保存了消息和消息所对应的处理函数。
2)静态变量:类型为结构体,有两个成员,第一个成员是用来保存父类的静态变量地址,这样,就会形成一个链表,
第二个成员就是用来保存静态数组的首地址。
3)虚函数:获取当前类的静态变量的地址。
三、通过看源代码写伪代码来体会消息映射机制的过程
1 AfxWndProc(...) 2 { 3 return AfxCallWndProc(...); 4 { 5 lResult = pWnd->WindowProc(nMsg, wParam, lParam); 6 { 7 OnWndMsg(message, wParam, lParam, &lResult) 8 { 9 //获取自己创建的框架窗口类的静态变量地址(头结点) 10 const AFX_MSGMAP* pMessageMap = GetMessageMap(); 11 { 12 return &CMainFrameWnd::messageMap; 13 } 14 //循环遍历 15 for (; pMessageMap != NULL; pMessageMap = pMessageMap->pBaseMap) 16 { 17 //查找数组中是否有与message相等的消息ID,如果有返回数组元素地址,否则继续遍历 18 if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, message, 0, 0)) != NULL) 19 { 20 pMsgCache->lpEntry = lpEntry; 21 AfxUnlockGlobals(CRIT_WINMSGCACHE); 22 goto LDispatch; 23 } 24 } 25 LDispatch: 26 union MessageMapFunctions mmf; 27 //找到之后,把消息ID对应的消息处理函数地址存放到一个联合变量里 28 mmf.pfn = lpEntry->pfn; 29 nSig = lpEntry->nSig; 30 switch (nSig) 31 {... 32 case AfxSig_is: 33 lResult = (this->*mmf.pfn_is)((LPTSTR)lParam); 34 { 35 AfxMessageBox("创建窗口消息"); 36 return CFrameWnd::OnCreate(lpCreateStruct); 37 } 38 break;... 39 } 40 } 41 } 42 } 43 }
通过伪代码我们可以总结出消息映射的过程:首先,通过自己创建的框架窗口类对象调用GetMessageMap()获取本类的静态变量的首地址,
也就是链表的头结点,然后进行遍历这个链表,先从头结点开始,通过头结点的第二个成员,访问静态数组,查找这个数组中是否有跟当前
消息一致的数组元素,如果有,就把这个元素的首地址保存起来,结束循环,然后把这个元素里的一个成员处理函数指针存放到一个联合里,
最后就调用这个指针所指向的函数;如果没有的话,继续遍历这个链表,重复之前的过程,如果找到,调用基类中消息所对应的处理函数,
如果pMessageMap为NULL,表示确实没有,那么再调用缺省的窗口处理函数。