浅析Windows消息在mfc中的传递路线------(Command rounting)

来源:互联网 发布:直线职能制和矩阵值 编辑:程序博客网 时间:2024/06/06 17:47
     我写的每一遍文章都是有准备和有原因的,我绝不是凭个人的心情去做这件事。
   Windows消息的传递路线是MFC最神秘的地带之一,MFC的这种机制曾经困扰着无数的编程爱好者。长期以来,我也被它所困扰,而且让我很不爽。当然,很多前辈都对此有过比较深入的剖析,我也看过很多,但是我天生愚钝,看过各种版本任然不能彻底明白其中的机制到底是怎么样实现的。随着我对MFC的理解更加深入,我终于理清了它的头绪。当然,在这个时代还去分析MFC的内部机制是很愚蠢的,因为肯定有些人会站出来跟我说:你傻了,MFC是什么时候的东西啊,你现在还在纠结它,闲得蛋疼吧。但是我还是想搞清楚MFC的这种机制,我相信我的这篇文章在某个时候会帮到一些人,即使不能,我也不会感到遗憾,我尽力了。

        即使你刚开始学习MFC,那也没关系,我相信你仍能从下面的文字和代码中有所收获。其中,我会贴出关键的MFC底层代码,这样更有利于我们的理解。

        分析流程如下:

        1.分析Windows消息在MFC中的总体传递路线;

             2.分析Window命令消息WM_COMMAND的传递路线。

        一.Windows消息在MFC中的总体传递路线

       传递路线:AfxWndProc-----AfxCallWndProc-----WindowProc-----OnWndMsg-----DefWindowProc-----::DefWindowProc。AfxWndProc是消息的起点,为什么呢?请看MFC源代码:

 

BOOL CWnd::CreateEx(DWORD dwExStyle, LPCTSTR lpszClassName,LPCTSTR lpszWindowName, DWORD dwStyle,
                    int x, int y, int nWidth, int nHeight,                    HWND hWndParent, HMENU nIDorHMenu, LPVOID lpParam){
         ...... AfxHookWindowCreate(this); HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,   cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,   cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);

}

 

//安装hook的函数

void AFXAPI AfxHookWindowCreate(CWnd* pWnd){

.......

  pThreadState->m_hHookOldCbtFilter = ::SetWindowsHookEx(WH_CBT,_AfxCbtFilterHook, NULL, ::GetCurrentThreadId());

......}

 

//HookPorc钩子过程

LRESULT CALLBACK_AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam){

......

 

 WNDPROC afxWndProc = AfxGetAfxWndProc();    oldWndProc = (WNDPROC)SetWindowLong(hWnd, GWL_WNDPROC,(DWORD)afxWndProc);

    ......

 return lResult;

}

 

WNDPROC AFXAPI AfxGetAfxWndProc(){

......

return &AfxWndProc;

}

 

从上面的代码中我们可以知道,在创建窗口之前,MFC安装了一个WH_CBT类型的钩子。在每个窗口对象产生之际,将窗口所属类的窗口函数替换为AfxWndProc,::DispatchMessage就把消息都传送到AfxWndProc中去了。所以,消息的起点是AfxWndProc。继续看MFC中的源代码:

 

//消息的起点

LRESULT CALLBACKAfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam){......
CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);ASSERT(pWnd != NULL);ASSERT(pWnd->m_hWnd == hWnd);return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);}LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,WPARAM wParam = 0, LPARAM lParam = 0){......
 
lResult = pWnd->WindowProc(nMsg, wParam, lParam);         
......
 
return lResult;}LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam){LRESULT lResult = 0;if (!OnWndMsg(message, wParam, lParam, &lResult))lResult = DefWindowProc(message, wParam, lParam);return lResult;}LRESULT CWnd::DefWindowProc(UINT nMsg, WPARAM wParam, LPARAM lParam){       ......
return ::DefWindowProc(m_hWnd, nMsg, wParam, lParam);       ......
}
 
     分析上面的代码,MFC中的消息沿着上面的路线走就顺理成章了。
 
   那么消息到底在哪儿被处理的?如何被处理的呢?请看MFC源代码:
 
//大部分消息在这个函数中处理   
BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, 
LRESULT* pResult){
 if (message == WM_COMMAND) {  if (OnCommand(wParam, lParam))  {
......  }  return FALSE; }
  if (message == WM_NOTIFY) {
      ......  if (pNMHDR->hwndFrom != NULL && OnNotify(wParam, lParam, &lResult))   return FALSE; }
 
//比较消息映射表
const AFX_MSGMAP* pMessageMap; pMessageMap = GetMessageMap(); UINT iHash; iHash = (LOWORD((DWORD)pMessageMap) ^ message) & 
(iHashMax-1); AfxLockGlobals(CRIT_WINMSGCACHE); AFX_MSG_CACHE* pMsgCache; pMsgCache = &_afxMsgCache[iHash]; const AFX_MSGMAP_ENTRY* lpEntry;
......
}
 
   Windows消息分为三类:命令消息WM_COMMAND,控件的通告消息WM_NOTIFY,标准消息WM_XXX。MFC中的消息经过此路线一步一步传递,到达OnWndMsg,如果消息是命令消息WM_COMMAND,则交由虚函数OnCommand处理;如果是通告消WM_NOTIFY,则交由虚函数OnNotify处理;如果是标准消息WM_XXX,就比较消息映射表Message Map,找到消息处理函数。
 
二.WM_COMMAND消息在MFC中的传递路线
   WM_COMMAND消息交由OnCommand这个函数处理,我们假设消息是从CFrameWnd进来为例子来分析WM_COMMAND消息的传递路线。
首先看MFC中的源代码:

BOOL CFrameWnd::OnCommand(WPARAM wParam, LPARAM lParam){

......

  return CWnd::OnCommand(wParam, lParam);}

 

BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam){

......

return OnCmdMsg(nID, nCode, NULL, NULL);

}

 

BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo){

  if (pView != NULL && pView->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))   return TRUE;

 

 if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))   return TRUE;

 

 CWinApp* pApp = AfxGetApp(); if (pApp != NULL && pApp->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))   return TRUE;

 return FALSE;}

 

BOOL CView::OnCmdMsg(UINT nID, int nCode, void* pExtra,AFX_CMDHANDLERINFO* pHandlerInfo){  if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))//其实就是调用CCmdTarget::OnCmdMsg  return TRUE;

  if (m_pDocument != NULL) {

......   return m_pDocument->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo); }

 return FALSE;}

 

BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra,AFX_CMDHANDLERINFO*pHandlerInfo)

{

......

 for (pMessageMap = GetMessageMap(); pMessageMap != NULL;   pMessageMap = pMessageMap->pBaseMap) {  ASSERT(pMessageMap != pMessageMap->pBaseMap);

  lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, nMsg, nCode, nID);  if (lpEntry != NULL)  {    return _AfxDispatchCmdMsg(this, nID, nCode,    lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo);  }

......

}

从上面的代码可以看到,当消息WM_COMMAND从CFrameWnd进来时消息传递路径如下:View----Document----FrameWnd--WinApp.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

原创粉丝点击