深入浅出MFC笔记3-MFC程序的消息流转
来源:互联网 发布:ddos攻击某个端口 编辑:程序博客网 时间:2024/06/06 09:45
对于上面的过程我们可能会对设置的消息处理函数有疑问,如果创建的窗口的消息处理函数是Windows默认的消息处理,那么MFC如何对各种消息进行响应呢?对于这个问题我们回到CWnd::CreateEx函数中,我们可以看到在CFrameWnd::PreCreateWindow函数调用之后有会调用AfxHookWindowCreate这个函数,进入此函数可以看到调用SetWindowsHookEx,此函数在消息处理链中塞入了函数_AfxCbtFilterHook,再来看看_AfxCbtFilterHook函数做了些什么!
先贴出源码:
消息分为命令消息、控件通行以及窗口消息,上面的函数中OnCommand处理命令消息,OnNotify处理控件通知,窗口消息的处理是查找MessageMap对应条目然后调用处理函数,查找MessageMap时向基类的MessageMap回溯,我们的例子程序中最先查找的MyFrame的MessageMap,之后依次调用CFrameWnd,CWnd,CCmdTarget,CObject的
MessageMap,从上面的说明中可以得到窗口消息的传导路径是直接从派生类传递到基类的。
下面来看看处理命令消息的OnCommand函数和处理控件通知消息的OnNotify函数。
调用OnCommand函数的窗口指针是MyFrame类型,所以实际要调用的是MyFrame::OnCommand,MyFrame中没有重写此函数,那么调用的就是CFrameWnd::OnCommand,进入后接着调用CWnd::OnCommand,在此函数中又调用了CFrameWnd::OnCmdMsg,此函数代码如下:
重上面的代码可以看出,命令消息先由CView处理,再交由CFrameWnd处理,最后给CWinApp处理,而且被其中一个类型处理之后就不会再被后面的类型处理。这里OnCmdMsg函数调用的都是CCmdTarget::OnCmdMsg函数,此函数源码如下:
此函数也是依次从派生类到基类查找消息处理表,调用处理函数,从上面的说明可以知道,一条命令消息的处理路径是先传递给CView的派生类,之后传递给CFrameWnd的派生类及基类,最后传递给CWinApp的派生类。
CWnd::OnNotify也是调用了CFrameWnd::OnCmdMsg,所以控件通知和命令消息的处理路径一样。
先贴出源码:
LRESULT CALLBACK_AfxCbtFilterHook(int code, WPARAM wParam, LPARAM lParam){ _AFX_THREAD_STATE* pThreadState = _afxThreadState.GetData(); if (code != HCBT_CREATEWND) { // wait for HCBT_CREATEWND just pass others on... return CallNextHookEx(pThreadState->m_hHookOldCbtFilter, code, wParam, lParam); } ASSERT(lParam != NULL); LPCREATESTRUCT lpcs = ((LPCBT_CREATEWND)lParam)->lpcs; ASSERT(lpcs != NULL); CWnd* pWndInit = pThreadState->m_pWndInit; BOOL bContextIsDLL = afxContextIsDLL; if (pWndInit != NULL || (!(lpcs->style & WS_CHILD) && !bContextIsDLL)) { ... ASSERT(wParam != NULL); // should be non-NULL HWND HWND hWnd = (HWND)wParam; WNDPROC oldWndProc; if (pWndInit != NULL) { AFX_MANAGE_STATE(pWndInit->m_pModuleState); // the window should not be in the permanent map at this time ASSERT(CWnd::FromHandlePermanent(hWnd) == NULL); // connect the HWND to pWndInit... pWndInit->Attach(hWnd); // allow other subclassing to occur first pWndInit->PreSubclassWindow(); WNDPROC *pOldWndProc = pWndInit->GetSuperWndProcAddr(); ASSERT(pOldWndProc != NULL); // subclass the window with standard AfxWndProc WNDPROC afxWndProc = AfxGetAfxWndProc(); oldWndProc = (WNDPROC)SetWindowLongPtr(hWnd, GWLP_WNDPROC, (DWORD_PTR)afxWndProc); ASSERT(oldWndProc != NULL); if (oldWndProc != afxWndProc) *pOldWndProc = oldWndProc; pThreadState->m_pWndInit = NULL; } else { ASSERT(!bContextIsDLL); // should never get here ... } }lCallNextHook: LRESULT lResult = CallNextHookEx(pThreadState->m_hHookOldCbtF, code, wParam, lParam); ... return lResult;}
在上面一段代码中我们看到此构造函数只拦截了窗口创建消息的处理,其他消息并没有拦截,并在处理时调用
SetWindowLongPtr(hWnd, GWLP_WNDPROC,(DWORD_PTR)afxWndProc);
如果你学过Win32编程,一定知道这个调用就是来修改窗口的处理函数的,这个函数把主窗口的处理函数修改成了afxWndProc,这个是MFC内部定义函数,其有又调用了AfxCallWndProc,此函数调用CWnd::WindowProc,在CWnd::WindowProc中又调用CWnd::OnWndMsg,在CWnd::OnWndMsg中就可以看到OnCommand,OnNotify函数调用,以及在消息映射表中查找消息的过程,精简之后源码如下: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 if (message == WM_COMMAND) { if (OnCommand(wParam, lParam)) { lResult = 1; goto LReturnTrue; } return FALSE; } // special case for notifies if (message == WM_NOTIFY) { NMHDR* pNMHDR = (NMHDR*)lParam; if (pNMHDR->hwndFrom != NULL && OnNotify(wParam, lParam, &lResult)) goto LReturnTrue; return FALSE; } ... 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) { ... } 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; }LDispatchRegistered: // for registered windows messages ASSERT(message >= 0xC000); ASSERT(sizeof(mmf) == sizeof(mmf.pfn)); mmf.pfn = lpEntry->pfn; lResult = (this->*mmf.pfn_l_w_l)(wParam, lParam);LReturnTrue: if (pResult != NULL) *pResult = lResult; return TRUE;}
消息分为命令消息、控件通行以及窗口消息,上面的函数中OnCommand处理命令消息,OnNotify处理控件通知,窗口消息的处理是查找MessageMap对应条目然后调用处理函数,查找MessageMap时向基类的MessageMap回溯,我们的例子程序中最先查找的MyFrame的MessageMap,之后依次调用CFrameWnd,CWnd,CCmdTarget,CObject的
MessageMap,从上面的说明中可以得到窗口消息的传导路径是直接从派生类传递到基类的。
下面来看看处理命令消息的OnCommand函数和处理控件通知消息的OnNotify函数。
调用OnCommand函数的窗口指针是MyFrame类型,所以实际要调用的是MyFrame::OnCommand,MyFrame中没有重写此函数,那么调用的就是CFrameWnd::OnCommand,进入后接着调用CWnd::OnCommand,在此函数中又调用了CFrameWnd::OnCmdMsg,此函数代码如下:
BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo){ CPushRoutingFrame push(this); // pump through current view FIRST CView* pView = GetActiveView(); if (pView != NULL && pView->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; // then pump through frame if (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; // last but not least, pump through app CWinApp* pApp = AfxGetApp(); if (pApp != NULL && pApp->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo)) return TRUE; return FALSE;}
重上面的代码可以看出,命令消息先由CView处理,再交由CFrameWnd处理,最后给CWinApp处理,而且被其中一个类型处理之后就不会再被后面的类型处理。这里OnCmdMsg函数调用的都是CCmdTarget::OnCmdMsg函数,此函数源码如下:
BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra, AFX_CMDHANDLERINFO* pHandlerInfo){ // determine the message number and code (packed into nCode) const AFX_MSGMAP* pMessageMap; const AFX_MSGMAP_ENTRY* lpEntry; UINT nMsg = 0; if (nCode != CN_UPDATE_COMMAND_UI) { nMsg = HIWORD(nCode); nCode = LOWORD(nCode); } // for backward compatibility HIWORD(nCode)==0 is WM_COMMAND if (nMsg == 0) nMsg = WM_COMMAND; // look through message map to see if it applies to us for (pMessageMap = GetMessageMap(); pMessageMap->pfnGetBaseMap != NULL; pMessageMap = (*pMessageMap->pfnGetBaseMap)()) { // Note: catches BEGIN_MESSAGE_MAP(CMyClass, CMyClass)! ASSERT(pMessageMap != (*pMessageMap->pfnGetBaseMap)()); lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, nMsg, nCode, nID); if (lpEntry != NULL) { return _AfxDispatchCmdMsg(this, nID, nCode, lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo); } } return FALSE; // not handled}
此函数也是依次从派生类到基类查找消息处理表,调用处理函数,从上面的说明可以知道,一条命令消息的处理路径是先传递给CView的派生类,之后传递给CFrameWnd的派生类及基类,最后传递给CWinApp的派生类。
CWnd::OnNotify也是调用了CFrameWnd::OnCmdMsg,所以控件通知和命令消息的处理路径一样。
下面借用《深入浅出MFC》中的一张图来作个总结,MFC中的消息处理路径如图中箭头所示:
- 深入浅出MFC笔记3-MFC程序的消息流转
- 深入浅出MFC:MFC的消息机制
- 深入浅出MFC学习笔记3--MFC程序流程
- 学习笔记之深入浅出MFC 第3讲 消息循环
- 深入浅出MFC学习笔记:MFC六大关键技术仿真之MFC程序的初始化过程
- MFC深入浅出--消息映射
- MFC深入浅出--消息映射
- 深入浅出MFC:消息路由
- 深入浅出MFC笔记2-MFC程序如何包装Win32程序
- 《深入浅出MFC》中关于程序生死的笔记
- 深入浅出MFC笔记(3)
- Win32到MFC的消息影射机制---MFC深入浅出
- MFC的消息分类总结(来自深入浅出MFC--侯杰)
- 深入浅出MFC笔记1-Win32和MFC程序对比
- 深入浅出MFC学习笔记2--消息映射和消息路由
- 深入浅出MFC学习笔记(第6章 :MFC程序的生死因果)
- 深入浅出MFC学习笔记(第7章:简单而完整的MFC骨干程序)
- 深入浅出MFC学习笔记(第6章 :MFC程序的生死因果)
- MFC在VS2008中对ActiveX控件添加方法 .
- TOJ 4279 Barricade / dijkstra
- 刺杀大使{未完成}
- oracle 恢复误删除的表和误更新的表
- AsyncTask,Handler,Looper,MessageQueue
- 深入浅出MFC笔记3-MFC程序的消息流转
- 【cocos2d-x】 之 模态对话框
- sql server如何求前N列的和(具体几列未知) 右侧汇总
- MFC在VS2008中为ActiveX控件添加属性 .
- Eclipse环境开发java:Selection does not contain a main type
- 终于搞定android驱动USB摄像头了!
- linux下消息队列小例子
- ServletMapping的处理
- 获取上一页面的URL和本页的URL