MFC的来龙去脉-----消息处理,找处理函数
来源:互联网 发布:淘宝直通车类目受限 编辑:程序博客网 时间:2024/06/05 02:58
对于对话框程序,(无论是模式还是非模式),在注册窗口的时候,会指定其窗口过程处理函数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
- MFC的来龙去脉-----消息处理,找处理函数
- MFC的来龙去脉-----消息循环,找处理场所
- MFC的消息处理函数
- MFC的消息处理函数
- MFC的消息处理函数
- MFC的消息处理函数
- MFC的消息处理函数
- MFC的消息处理函数
- MFC的消息处理函数
- MFC的消息处理函数
- MFC的消息处理函数
- MFC的消息处理函数
- MFC消息处理函数
- MFC消息处理函数
- MFC默认的消息处理函数
- MFC消息映射的处理函数
- MFC对消息处理的相关函数
- MFC 的消息处理
- 关于自动拆箱的危害--慎重自动拆箱
- MFC的来龙去脉-----消息循环,找处理场所
- Docker系列(九)Kubernetes安装
- MFC的来龙去脉-----热身开胃汤
- 系统学习机器学习之神经网络(四) --SOM
- MFC的来龙去脉-----消息处理,找处理函数
- 结合CSerialPort类,实现完整的串口收发功能
- Spark系列(三)SparkContext分析
- 数据结构八大排序算法
- html 学习笔记(五)
- Servlet 3.0 新特性详解
- 数据结构(一)二叉搜索树-递归实现
- 数据结构(二)二叉搜索树-非递归实现遍历
- 性能驱动正确打开姿势