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

    xiaoxiao2023-11-27  161

    一、处理函数的源头   对于对话框程序,(无论是模式还是非模式),在注册窗口的时候,会指定其窗口过程处理函数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.csdn.net/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 ---------------------  原文:https://blog.csdn.net/anribras/article/details/53462564   

    最新回复(0)