MFC 消息映射(一)

---恢复内容开始---

  最近在看《深入浅出MFC》一书。消息映射流程图如下:

在此附加上不实用MFC Wizard 编写的简单MFC窗口程序,纯粹为学习消息映射。下载地址:Hello.06

MFC 程序入口:

 1 int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,_In_ LPTSTR lpCmdLine, int nCmdShow)
 2 {
 3 
 4     CWinApp* pApp = AfxGetApp();
 5 
 6     // AFX internal initialization
 7     AfxWinInit(hInstance, hPrevInstance, lpCmdLine,nCmdShow)
 8     
 9         pApp->InitApplication());
10     
11     // Perform specific initializations
12     pThread->InitInstance();
13     
14     nReturnCode = pThread->Run();
15 
16     return nReturnCode;
17 }    

pThread->InitInstance() 即调用 :(Note:CMyWinAPP 改下了CWinApp 的InitInstance()函数,根据C++多态原则,调用)

 1 BOOL CMyWinApp::InitInstance()
 2 {
 3    m_pMainWnd = new CMyFrameWnd();
 4    m_pMainWnd->ShowWindow(m_nCmdShow);
 5    m_pMainWnd->UpdateWindow();
 6    return TRUE;
 7 }
 8 
 9 //--------------------------------------------------------------------
10 // CMyFrameWnd's member
11 //--------------------------------------------------------------------
12 CMyFrameWnd::CMyFrameWnd()
13 {
14    Create(NULL, "Hello MFC", WS_OVERLAPPEDWINDOW, rectDefault,
15           NULL, "MainMenu");     // "MainMenu" 定義於 RC 檔
16 }

上述代码的CWnd::Create()完成了窗口类的注册(Register)并绑定窗口函数:

LRESULT CALLBACK
AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam),

然后创建窗口(CreateWindow) 。

AfxWndProc函数即对应文首图中函数。

 

利用hook 技术,任何默认创建的窗口都以AfxWndProc作为窗口函数。

下图介绍了CFrameWnd,CDocument,CView之间的消息路由顺序:

相关原始代码如下:

  1 BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra,
  2     AFX_CMDHANDLERINFO* pHandlerInfo)
  3 {
  4     CPushRoutingFrame push(this);
  5 
  6     // pump through current view FIRST
  7     CView* pView = GetActiveView();
  8     if (pView != NULL && pView->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
  9         return TRUE;
 10 
 11     // then pump through frame
 12     if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
 13         return TRUE;
 14 
 15     // last but not least, pump through app
 16     CWinApp* pApp = AfxGetApp();
 17     if (pApp != NULL && pApp->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
 18         return TRUE;
 19 
 20     return FALSE;
 21 }
 22 
 23 /////////////////////////////////////////////////////////////////////////////
 24 // Command routing
 25 
 26 BOOL CView::OnCmdMsg(UINT nID, int nCode, void* pExtra,
 27     AFX_CMDHANDLERINFO* pHandlerInfo)
 28 {
 29     // first pump through pane
 30     if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
 31         return TRUE;
 32 
 33     // then pump through document
 34     if (m_pDocument != NULL)
 35     {
 36         // special state for saving view before routing to document
 37         CPushRoutingView push(this);
 38         return m_pDocument->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
 39     }
 40 
 41     return FALSE;
 42 }
 43 CMDIChildWnd::OnCmdMsg
 44 BOOL CMDIFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra,
 45     AFX_CMDHANDLERINFO* pHandlerInfo)
 46 {
 47     CMDIChildWnd* pActiveChild = MDIGetActive();
 48     // pump through active child FIRST
 49     if (pActiveChild != NULL)
 50     {
 51         CPushRoutingFrame push(this);
 52         if (pActiveChild->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
 53             return TRUE;
 54     }
 55 
 56     // then pump through normal frame
 57     return CFrameWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
 58 }
 59 
 60 
 61 
 62 /////////////////////////////////////////////////////////////////////////////
 63 // command routing
 64 
 65 BOOL CDocument::OnCmdMsg(UINT nID, int nCode, void* pExtra,
 66     AFX_CMDHANDLERINFO* pHandlerInfo)
 67 {
 68     if (CCmdTarget::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
 69         return TRUE;
 70 
 71     // otherwise check template
 72     if (m_pDocTemplate != NULL &&
 73       m_pDocTemplate->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
 74         return TRUE;
 75 
 76     return FALSE;
 77 }
 78 /////////////////////////////////////////////////////////////////////////////
 79 
 80 
 81 
 82 BOOL CDocTemplate::OnCmdMsg(UINT nID, int nCode, void* pExtra,
 83     AFX_CMDHANDLERINFO* pHandlerInfo)
 84 {
 85     BOOL bReturn;
 86     CCmdTarget* pFactory = DYNAMIC_DOWNCAST(CCmdTarget, m_pAttachedFactory);
 87 
 88     if (nCode == CN_OLE_UNREGISTER && pFactory != NULL)
 89         bReturn = pFactory->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
 90     else
 91         bReturn = CCmdTarget::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);
 92 
 93     return bReturn;
 94 }
 95 
 96 BOOL CDialog::OnCmdMsg(UINT nID, int nCode, void* pExtra,
 97     AFX_CMDHANDLERINFO* pHandlerInfo)
 98 {
 99     if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
100         return TRUE;
101 
102     if ((nCode != CN_COMMAND && nCode != CN_UPDATE_COMMAND_UI) ||
103             !IS_COMMAND_ID(nID) || nID >= 0xf000)
104     {
105         // control notification or non-command button or system command
106         return FALSE;       // not routed any further
107     }
108 
109     // if we have an owner window, give it second crack
110     CWnd* pOwner = GetParent();
111     if (pOwner != NULL)
112     {
113         TRACE(traceCmdRouting, 1, "Routing command id 0x%04X to owner window.\n", nID);
114 
115         ASSERT(pOwner != this);
116         if (pOwner->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
117             return TRUE;
118     }
119 
120     // last crack goes to the current CWinThread object
121     CWinThread* pThread = AfxGetThread();
122     if (pThread != NULL)
123     {
124         TRACE(traceCmdRouting, 1, "Routing command id 0x%04X to app.\n", nID);
125 
126         if (pThread->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
127             return TRUE;
128     }
129 
130     TRACE(traceCmdRouting, 1, "IGNORING command id 0x%04X sent to %hs dialog.\n", nID,
131             GetRuntimeClass()->m_lpszClassName);
132 
133     return FALSE;
134 }
View Code

在 OnCmdMsg过程中,消息对应的函数有_AfxDispatchCmdMsg调用。

CCmdTarget::OnCmdMsg函数很关键,原始代码摘抄下来:

 1 BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra,
 2     AFX_CMDHANDLERINFO* pHandlerInfo)
 3 {
 4 //在【消息:On函数】表中查找,找不到就到父类查找
 5     // for backward compatibility HIWORD(nCode)==0 is WM_COMMAND
 6     if (nMsg == 0)
 7         nMsg = WM_COMMAND;
 8 
 9     // look through message map to see if it applies to us
10 
11     for (pMessageMap = GetMessageMap(); pMessageMap->pfnGetBaseMap != NULL;
12       pMessageMap = (*pMessageMap->pfnGetBaseMap)())
13     {
14         // Note: catches BEGIN_MESSAGE_MAP(CMyClass, CMyClass)!
15         ASSERT(pMessageMap != (*pMessageMap->pfnGetBaseMap)());
16         lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, nMsg, nCode, nID);
17         if (lpEntry != NULL)
18         {
19             // found it
20 #ifdef _DEBUG
21             if (nCode == CN_COMMAND)
22                 TRACE(traceCmdRouting, 1, "SENDING command id 0x%04X to %hs target.\n", nID,
23                     GetRuntimeClass()->m_lpszClassName);
24             else if (nCode > CN_COMMAND)
25                 TRACE(traceCmdRouting, 1, "SENDING control notification %d from control id 0x%04X to %hs window.\n",
26                     nCode, nID, GetRuntimeClass()->m_lpszClassName);
27 #endif //_DEBUG
28             return _AfxDispatchCmdMsg(this, nID, nCode,
29                 lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo);
30         }
31     }
32     return FALSE;   // not handled
33 }

下图是CView类的 WM_PAINT消息路由过程:

上图也说明了MFC  文档/视结构的继承关系。

总结:C++多态可以从数据与函数的角度理解,数据属于具体的类实例,一个实例一份;成员函数代码确为多个实例共享。虚函数表由

项【实例指针:重载函数】组成,其实现了“子类没有才调用父类虚函数”功能。

另外对于形如的代码:

 1 BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra,
 2     AFX_CMDHANDLERINFO* pHandlerInfo)
 3 {
 4     CPushRoutingFrame push(this);
 5 
 6     // pump through current view FIRST
 7     CView* pView = GetActiveView();
 8     if (pView != NULL && pView->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
 9         return TRUE;
10 
11     // then pump through frame
12     if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
13         return TRUE;
14 
15     // last but not least, pump through app
16     CWinApp* pApp = AfxGetApp();
17     if (pApp != NULL && pApp->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))
18         return TRUE;
19 
20     return FALSE;
21 }

在子类的重载函数中使用Parent::Fun方式调用父类函数可以简单理解为从父类复制相关代码。还要注意的是子类继承父类函数也有类似特征(复制)。

boyang987

 

 

---恢复内容结束---

posted @ 2015-01-23 18:06  boyang987  阅读(315)  评论(0编辑  收藏  举报