首页 > 代码库 > 【转】Windows消息投递流程:WM_COMMAND消息流程

【转】Windows消息投递流程:WM_COMMAND消息流程

原文网址:http://blog.csdn.net/hyhnoproblem/article/details/6182585

该示例通过研究基本的单文档程序的“文件”--“打开”命令,分析WM_COMMAND消息投递流程。基于VS 2005 代码

 

AfxWndProc最终调用的是OnWndMsg,这个函数负责消息的分发处理。当消息是WM_COMMAND时,将消息投递给OnCommand函数。

 

[cpp] view plaincopy
 
  1. // wincore.cpp 1746  
  2. BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)  
  3. {  
  4.     LRESULT lResult = 0;  
  5.     union MessageMapFunctions mmf;  
  6.     mmf.pfn = 0;  
  7.     CInternalGlobalLock winMsgLock;  
  8.     // special case for commands  
  9.     if (message == WM_COMMAND)  
  10.     {  
  11.         if (OnCommand(wParam, lParam))  
  12.         {  
  13.             lResult = 1;  
  14.             goto LReturnTrue;  
  15.         }  
  16.         return FALSE;  
  17.     }  
  18.     //...  
  19. }  

 

 

 

OnCommand是个虚函数,因为消息是主窗口产生的,所以调用的是CFrameWnd::OnCommand函数该函数先检查该消息是不是在线请求帮助,如果是,则程序给框架窗口发送一个WM_COMMANDHELP消息,否则交由基类CWnd::OnCommand()处理。

 

[cpp] view plaincopy
 
  1. // winfrm.cpp 299  
  2. BOOL CFrameWnd::OnCommand(WPARAM wParam, LPARAM lParam)  
  3. // return TRUE if command invocation was attempted  
  4. {  
  5.     HWND hWndCtrl = (HWND)lParam;  
  6.     UINT nID = LOWORD(wParam);  
  7.     CFrameWnd* pFrameWnd = GetTopLevelFrame();  
  8.     ENSURE_VALID(pFrameWnd);  
  9.     if (pFrameWnd->m_bHelpMode && hWndCtrl == NULL &&  
  10.         nID != ID_HELP && nID != ID_DEFAULT_HELP && nID != ID_CONTEXT_HELP)  
  11.     {  
  12.         // route as help  
  13.         if (!SendMessage(WM_COMMANDHELP, 0, HID_BASE_COMMAND+nID))  
  14.             SendMessage(WM_COMMAND, ID_DEFAULT_HELP);  
  15.         return TRUE;  
  16.     }  
  17.     // route as normal command  
  18.     return CWnd::OnCommand(wParam, lParam);  
  19. }  

 

 

 

CWnd::OnCommand首先检查表示控件的LPARAM。如果消息是由控件产生的,LPARAM就会包含控件窗口句柄。如果消息是控件通知,框架就会执行特定的处理过程。如果消息是为某个控件产生的,OnCommand会将消息直接发送给控件,然后OnCommand返回。否则,CWnd::OnCommand()要确保产生命令的界面元素没有被禁用,然后将消息传递给OnCmdMsg函数,调用的是CFrameWnd::OnCmdMsg函数

 

[cpp] view plaincopy
 
  1. // wParam  
  2. //  The high-order word specifies the notification code if the message is from a control.   
  3. //  If the message is from an accelerator, this value is 1. If the message is from a menu,   
  4. //  this value is zero.   
  5. //  The low-order word specifies the identifier of the menu item, control, or accelerator.   
  6. // lParam  
  7. //  Handle to the control sending the message if the message is from a control.   
  8. //  Otherwise, this parameter is NULL.   
  9. BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam)  
  10. // return TRUE if command invocation was attempted  
  11. {  
  12.     UINT nID = LOWORD(wParam);  
  13.     HWND hWndCtrl = (HWND)lParam;  
  14.     int nCode = HIWORD(wParam);  
  15.     // default routing for command messages (through closure table)  
  16.     if (hWndCtrl == NULL)  
  17.     {   // 菜单命令先走这里  
  18.         // zero IDs for normal commands are not allowed  
  19.         if (nID == 0)  
  20.             return FALSE;  
  21.         // make sure command has not become disabled before routing  
  22.         CTestCmdUI state;  
  23.         state.m_nID = nID;  
  24.         OnCmdMsg(nID, CN_UPDATE_COMMAND_UI, &state, NULL);      // CFrameWnd::OnCmdMsg  
  25.         if (!state.m_bEnabled)  
  26.         {  
  27.             TRACE(traceAppMsg, 0, "Warning: not executing disabled command %d/n", nID);  
  28.             return TRUE;  
  29.         }  
  30.         // menu or accelerator  
  31.         nCode = CN_COMMAND;  
  32.     }  
  33.     else  
  34.     {  
  35.         // ToolBar命令走这里,或控件通知  
  36.         // control notification  
  37.         ASSERT(nID == 0 || ::IsWindow(hWndCtrl));  
  38.         if (_afxThreadState->m_hLockoutNotifyWindow == m_hWnd)  
  39.             return TRUE;        // locked out - ignore control notification  
  40.         // reflect notification to child window control  
  41.         if (ReflectLastMsg(hWndCtrl))  
  42.             return TRUE;    // eaten by child  
  43.         // zero IDs for normal commands are not allowed  
  44.         if (nID == 0)  
  45.             return FALSE;  
  46.     }  
  47.     // 调用CFrameWnd::OnCmdMsg  
  48.     return OnCmdMsg(nID, nCode, NULL, NULL);  
  49. }  

 

 

 

CFrameWnd::OnCmdMsg函数调用时,pExtra和pHandlerInfo是NULL,因为处理命令不需要这一信息。取出的消息按照以下顺序经过应用程序的各个部分:活动视图、活动视图的文档、文档模板、主窗口、应用程序。

 

[cpp] view plaincopy
 
  1. // winfrm.cpp 880  
  2. BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra,  
  3.                          AFX_CMDHANDLERINFO* pHandlerInfo)  
  4. {  
  5.     CPushRoutingFrame push(this);  
  6.     // 将消息传递给活动视图,调用CView::OnCmdMsg  
  7.     CView* pView = GetActiveView();  
  8.     if (pView != NULL && pView->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))  
  9.         return TRUE;  
  10.     // 由框架处理该消息,CWnd没有覆盖OnCmdMsg,调用CCmdTarget::OnCmdMsg  
  11.     if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))  
  12.         return TRUE;  
  13.     // 将消息传递给应用程序,CWinApp没有覆盖OnCmdMsg,所以调用CCmdTarget::OnCmdTarget  
  14.     CWinApp* pApp = AfxGetApp();  
  15.     if (pApp != NULL && pApp->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))  
  16.         return TRUE;  
  17.     return FALSE;  
  18. }  
  19. // viewcore.cpp 154  
  20. BOOL CView::OnCmdMsg(UINT nID, int nCode, void* pExtra,  
  21.                      AFX_CMDHANDLERINFO* pHandlerInfo)  
  22. {  
  23.     // 首先由活动视图处理该消息,调用CCmdTarget::OnCmdMsg  
  24.     if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))  
  25.         return TRUE;  
  26.     // then pump through document  
  27.     if (m_pDocument != NULL)  
  28.     {  
  29.         // 将该消息传递给视图对应的文档,调用CDocument::OnCmdMsg  
  30.         CPushRoutingView push(this);  
  31.         return m_pDocument->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);  
  32.     }  
  33.     return FALSE;  
  34. }  
  35. // doccore.cpp 834  
  36. BOOL CDocument::OnCmdMsg(UINT nID, int nCode, void* pExtra,  
  37.                          AFX_CMDHANDLERINFO* pHandlerInfo)  
  38. {  
  39.     if (CCmdTarget::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))  
  40.         return TRUE;  
  41.     // otherwise check template,调用CDocTemplate::OnCmdMsg  
  42.     if (m_pDocTemplate != NULL &&  
  43.         m_pDocTemplate->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))  
  44.         return TRUE;  
  45.     return FALSE;  
  46. }  
  47. // doctempl.cpp 370  
  48. BOOL CDocTemplate::OnCmdMsg(UINT nID, int nCode, void* pExtra,  
  49.                             AFX_CMDHANDLERINFO* pHandlerInfo)  
  50. {  
  51.     BOOL bReturn;  
  52.     CCmdTarget* pFactory = DYNAMIC_DOWNCAST(CCmdTarget, m_pAttachedFactory);  
  53.     if (nCode == CN_OLE_UNREGISTER && pFactory != NULL)  
  54.         bReturn = pFactory->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);  
  55.     else  
  56.         bReturn = CCmdTarget::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo);  
  57.     return bReturn;  
  58. }  

 

 

 

上面的一些函数确定了程序执行流程,最终会调用到CCmdTarget::OnCmdMsg,该函数通过查找消息映射表,进而调用到消息处理函数。

 

[cpp] view plaincopy
 
  1. // cmdtarg.cpp 297  
  2. BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra,  
  3.                           AFX_CMDHANDLERINFO* pHandlerInfo)  
  4. {  
  5.     // ...  
  6.     // determine the message number and code (packed into nCode)  
  7.     const AFX_MSGMAP* pMessageMap;  
  8.     const AFX_MSGMAP_ENTRY* lpEntry;  
  9.     UINT nMsg = 0;  
  10.     //...  
  11.     if (nCode != CN_UPDATE_COMMAND_UI)  
  12.     {  
  13.         nMsg = HIWORD(nCode);  
  14.         nCode = LOWORD(nCode);  
  15.     }  
  16.     //...  
  17.     // for backward compatibility HIWORD(nCode)==0 is WM_COMMAND  
  18.     if (nMsg == 0)  
  19.         nMsg = WM_COMMAND;  
  20.     // look through message map to see if it applies to us  
  21.     for (pMessageMap = GetMessageMap(); pMessageMap->pfnGetBaseMap != NULL;  
  22.         pMessageMap = (*pMessageMap->pfnGetBaseMap)())  
  23.     {  
  24.         // Note: catches BEGIN_MESSAGE_MAP(CMyClass, CMyClass)!  
  25.         ASSERT(pMessageMap != (*pMessageMap->pfnGetBaseMap)());  
  26.         lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, nMsg, nCode, nID);  
  27.         if (lpEntry != NULL)  
  28.         {  
  29.             // found it  
  30.             return _AfxDispatchCmdMsg(this, nID, nCode,  
  31.                 lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo);  
  32.         }  
  33.     }  
  34.     return FALSE;   // not handled  
  35. }

 

【转】Windows消息投递流程:WM_COMMAND消息流程