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

     我写的每一遍文章都是有准备和有原因的,我绝不是凭个人的心情去做这件事。
   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 CALLBACK
AfxWndProc(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.

 

 

 

 

 

 

 

 

 

 

 

 

 

 

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值