MFC的来龙去脉-----消息处理,找处理函数

本文详细解析了MFC中消息的处理流程,包括从DispatchMessage出发直至消息被具体处理的过程。涉及WinProc、AfxWndProc等核心函数,以及消息如何被分发至WM_COMMAND、WM_NOTIFY或MessageMap进行处理。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、处理函数的源头

  对于对话框程序,(无论是模式还是非模式),在注册窗口的时候,会指定其窗口过程处理函数WinProc;当消息找到了对应的窗口,DispatchMessage便开始让内核user32.dll执行WinProc,它负责调用真正的消息处理函数;

if (pState->m_msgCur.message != WM_KICKIDLE && !AfxPreTranslateMessage(&(pState->m_msgCur)))//将消息分发至各个窗口
{  
  ::TranslateMessage(&(pState->m_msgCur)); //消息解析
  ::DispatchMessage(&(pState->m_msgCur)); //最终由内核调用WinProc,从而调用消息对应的处理函数
} return TRUE;

  有必要搞清楚WinProc是如何指定的。可以详见以下文章:

     http://www.yesky.com/20010202/157454_1.shtml 或 http://www.cnblogs.com/feng801/archive/2010/05/21/1740971.html

二、路径

  DispatchMessage->user32.dll->AfxWinProc->AfxCallWndProc->【pWnd->WindowProc】->【pWnd->OnWndMsg】

  为什么要采用【DispatchMessage->user32.dll->AfxWinProc-】如此复杂的windows回调机制?因为Windows想把控在不同进程间窗口间的消息处理的程序状态。通过消回调函数的返回,它可以分配不同时间片给不同任务。据说这部分是windows除COM外最难懂的。

  注意pWnd,CWnd* pWnd = CWnd::FromHandlePermanent(hWnd),也即指向Msg指定的目标窗口。该对象的虚函数若有重载,一定是调用已重载,而不是基类的;

  在【pWnd->OnWndMsg】之后,消息将有三个主要流向,一种是WM_COMMAND的处理,一种是WM_NOTIFY处理,一种直接搜索MessageMap;<深入浅出MFC>中,侯杰详细介绍了这三种流向,及这样的设计带来的好处。其中WM_COMMAND的处理非常有意思,也可参考:

     http://blog.youkuaiyun.com/zhangxinrun/article/details/5797154

  在【pWnd->OnWndMsg】处理后,若能找到消息处理入口,最好;若不能,则只能调用默认的处理函数DefWindowProc;它是windows的缺省消息处理函数。

  

三、详情

AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam)
{
    // special message which identifies the window as using AfxWndProc
    if (nMsg == WM_QUERYAFXWNDPROC)
        return 1;

    // all other messages route through message map   
    CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);   //检查窗口对象是否和windows窗口的句柄一一对应,pWnd后续的虚函数重载后的真正的执行对象
    ASSERT(pWnd != NULL);                    
    ASSERT(pWnd==NULL || pWnd->m_hWnd == hWnd);
    if (pWnd == NULL || pWnd->m_hWnd != hWnd)    //若没有句柄对应,说明是一个无窗口的线程,调用默认处理函数
        return ::DefWindowProc(hWnd, nMsg, wParam, lParam);
    return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam); //否则继续

}

pWnd->WindowProc意味着不同窗口,可以重载该函数,执行各自的WindowProc,前提是,各自窗口都注册了,并指定了相应的Proc函数;

LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,
    WPARAM wParam = 0, LPARAM lParam = 0)
{
    _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData();
    MSG oldState = pThreadState->m_lastSentMsg;   // save for nesting
    pThreadState->m_lastSentMsg.hwnd = hWnd;
    pThreadState->m_lastSentMsg.message = nMsg;
    pThreadState->m_lastSentMsg.wParam = wParam;
    pThreadState->m_lastSentMsg.lParam = lParam;


    // Catch exceptions thrown outside the scope of a callback
    // in debug builds and warn the user.
    LRESULT lResult;
    TRY
    {
#ifndef _AFX_NO_OCC_SUPPORT
        // special case for WM_DESTROY
        if ((nMsg == WM_DESTROY) && (pWnd->m_pCtrlCont != NULL))
            pWnd->m_pCtrlCont->OnUIActivate(NULL);                
#endif

        // special case for WM_INITDIALOG
        CRect rectOld;
        DWORD dwStyle = 0;
        if (nMsg == WM_INITDIALOG)
            _AfxPreInitDialog(pWnd, &rectOld, &dwStyle);

        // delegate to object's WindowProc
        lResult = pWnd->WindowProc(nMsg, wParam, lParam);

        // more special case for WM_INITDIALOG
        if (nMsg == WM_INITDIALOG)
            _AfxPostInitDialog(pWnd, rectOld, dwStyle);
    }
    CATCH_ALL(e)
    {
        lResult = AfxProcessWndProcException(e, &pThreadState->m_lastSentMsg);
        TRACE(traceAppMsg, 0, "Warning: Uncaught exception in WindowProc (returning %ld).\n",
            lResult);
        DELETE_EXCEPTION(e);
    }
    END_CATCH_ALL

    pThreadState->m_lastSentMsg = oldState;
    return lResult;
}

Winproc调用OnWndMsg,非常关键的消息路由函数

LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam)
{
    // OnWndMsg does most of the work, except for DefWindowProc call
    LRESULT lResult = 0;
    if (!OnWndMsg(message, wParam, lParam, &lResult))
        lResult = DefWindowProc(message, wParam, lParam);
    return lResult;
}

虚函数的妙用,注意OnWndMsg实际执行的是pWnd所指对象的OnWndMsg;它让消息的游走兵分三路(实际上还有一些special case,但我们主要关心三种去向):

BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult)
{
    LRESULT lResult = 0;
    union MessageMapFunctions mmf;
    mmf.pfn = 0;
    CInternalGlobalLock winMsgLock;
    // special case for commands      //第一路,WM_COMMAND命令走这里
    if (message == WM_COMMAND)
    {
        if (OnCommand(wParam, lParam))
        {
            lResult = 1;
            goto LReturnTrue;
        }
        return FALSE;
    }

    // special case for notifies           //第二路,WM_NOTIFY消息走这里
    if (message == WM_NOTIFY)                      
    {
        NMHDR* pNMHDR = (NMHDR*)lParam;
        if (pNMHDR->hwndFrom != NULL && OnNotify(wParam, lParam, &lResult))
            goto LReturnTrue;
        return FALSE;
    }

    // special case for activation
    if (message == WM_ACTIVATE)
        _AfxHandleActivate(this, wParam, CWnd::FromHandle((HWND)lParam));

    // special case for set cursor HTERROR
    if (message == WM_SETCURSOR &&
        _AfxHandleSetCursor(this, (short)LOWORD(lParam), HIWORD(lParam)))
    {
        lResult = 1;
        goto LReturnTrue;
    }

   // special case for windows that contain windowless ActiveX controls
   BOOL bHandled;

   bHandled = FALSE;
   if ((m_pCtrlCont != NULL) && (m_pCtrlCont->m_nWindowlessControls > 0))
   {
      if (((message >= WM_MOUSEFIRST) && (message <= AFX_WM_MOUSELAST)) ||
         ((message >= WM_KEYFIRST) && (message <= WM_IME_KEYLAST)) ||
         ((message >= WM_IME_SETCONTEXT) && (message <= WM_IME_KEYUP)))
      {
         bHandled = m_pCtrlCont->HandleWindowlessMessage(message, wParam, lParam, &lResult);
      }
   }
   if (bHandled)
   {
      goto LReturnTrue;
   }

  //第三路,在MessageMap中查找,从子类到基类,逐个寻找消息处理函数入口!此路径将处理绝大多数winodws消息;
const AFX_MSGMAP* pMessageMap; pMessageMap = GetMessageMap(); UINT iHash; iHash = (LOWORD((DWORD_PTR)pMessageMap) ^ message) & (iHashMax-1); winMsgLock.Lock(CRIT_WINMSGCACHE); AFX_MSG_CACHE* pMsgCache; pMsgCache = &_afxMsgCache[iHash]; const AFX_MSGMAP_ENTRY* lpEntry; if (message == pMsgCache->nMsg && pMessageMap == pMsgCache->pMessageMap) { // cache hit lpEntry = pMsgCache->lpEntry; winMsgLock.Unlock(); if (lpEntry == NULL) return FALSE; // cache hit, and it needs to be handled if (message < 0xC000) goto LDispatch; else goto LDispatchRegistered; } else { // not in cache, look for it pMsgCache->nMsg = message; pMsgCache->pMessageMap = pMessageMap;
 
        for (/* pMessageMap already init'ed */; pMessageMap->pfnGetBaseMap != NULL; 
            pMessageMap = (*pMessageMap->pfnGetBaseMap)())
        {
            // Note: catch not so common but fatal mistake!!
            //      BEGIN_MESSAGE_MAP(CMyWnd, CMyWnd)
            ASSERT(pMessageMap != (*pMessageMap->pfnGetBaseMap)());
            if (message < 0xC000)
            {
                // constant window message
                if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,
                    message, 0, 0)) != NULL)
                {
                    pMsgCache->lpEntry = lpEntry;
                    winMsgLock.Unlock();
                    goto LDispatch;
                }
            }
            else
            {
                // registered windows message
                lpEntry = pMessageMap->lpEntries;
                while ((lpEntry = AfxFindMessageEntry(lpEntry, 0xC000, 0, 0)) != NULL)
                {
                    UINT* pnID = (UINT*)(lpEntry->nSig);
                    ASSERT(*pnID >= 0xC000 || *pnID == 0);
                        // must be successfully registered
                    if (*pnID == message)
                    {
                        pMsgCache->lpEntry = lpEntry;
                        winMsgLock.Unlock();
                        goto LDispatchRegistered;
                    }
                    lpEntry++;      // keep looking past this one
                }
            }
        }

        pMsgCache->lpEntry = NULL;
        winMsgLock.Unlock();
        return FALSE;
    }

LDispatch:
    ASSERT(message < 0xC000);

    mmf.pfn = lpEntry->pfn;

    switch (lpEntry->nSig)
    {
    default:
        ASSERT(FALSE);
        break;
    case AfxSig_l_p:
        {
            CPoint point(lParam);        
            lResult = (this->*mmf.pfn_l_p)(point);
            break;
        }        
    case AfxSig_b_D_v:
        lResult = (this->*mmf.pfn_b_D)(CDC::FromHandle(reinterpret_cast<HDC>(wParam)));
        break;
  case:xxx
  case:xxx

 

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值