一、消息映射机制的使用

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,表示确实没有,那么再调用缺省的窗口处理函数。