MFC的来龙去脉-----消息处理,找处理函数
一、處理函數的源頭
對于對話框程序,(無論是模式還是非模式),在注冊窗口的時候,會指定其窗口過程處理函數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?
?
總結
以上是生活随笔為你收集整理的MFC的来龙去脉-----消息处理,找处理函数的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: 最新ffmpeg编译和用eclipse进
- 下一篇: H.264将普及 视频编码讲坛之H.26