MFC命令消息路由过程(视图、框架、应用)
来源:互联网 发布:ip域名是什么意思 编辑:程序博客网 时间:2024/06/07 18:13
一、环境
IDE:VC6.0
OS:WindowsXp
二、编写测试代码和环境配置
新建一个Win32 Application 选择 “A Simple Win32 Application”
打开stdafx.h头文件把
#include <windows.h>
更改为:
#include <afxwin.h>
修改工程设置使用MFC静态库以便能够查看微软提供的MFC源代码
Project->Settings->MicrosoftFoundation classes:Use MFC in a Static Library
回到工程cpp文件中编写如下测试代码:
#include "stdafx.h"#include "resource.h"class CMyView : public CView{DECLARE_MESSAGE_MAP ()public:virtual void OnDraw (CDC* pDC);afx_msg void OnFileNew ();};BEGIN_MESSAGE_MAP (CMyView, CView) //ON_COMMAND (ID_NEW, OnFileNew)END_MESSAGE_MAP ()void CMyView::OnDraw (CDC* pDC){pDC->Ellipse (100, 100, 400, 400);}void CMyView::OnFileNew (){AfxMessageBox ("CMyView::OnFileNew");}class CMyFrameWnd : public CFrameWnd{DECLARE_MESSAGE_MAP ()public:afx_msg int OnCreate (LPCREATESTRUCT lpCreateStruct);afx_msg void OnFileNew ();};BEGIN_MESSAGE_MAP (CMyFrameWnd, CFrameWnd) ON_WM_CREATE () //ON_COMMAND (ID_NEW, OnFileNew)END_MESSAGE_MAP ()int CMyFrameWnd::OnCreate (LPCREATESTRUCT lpCreateStruct){CMyView *pView = new CMyView;pView->Create (NULL, "", WS_CHILD | WS_VISIBLE | WS_BORDER, CRect (0,0,100,100), this, /*1001*/AFX_IDW_PANE_FIRST);m_pViewActive = pView;return CFrameWnd::OnCreate (lpCreateStruct);}void CMyFrameWnd::OnFileNew (){AfxMessageBox ("CMyFrameWnd::OnFileNew");}class CMyWinApp : public CWinApp{DECLARE_MESSAGE_MAP ()public:virtual BOOL InitInstance ();afx_msg void OnNewFile ();};BEGIN_MESSAGE_MAP (CMyWinApp, CWinApp) ON_COMMAND (ID_NEW, OnNewFile)END_MESSAGE_MAP ()void CMyWinApp::OnNewFile (){AfxMessageBox ("CMyWinApp::OnNewFile");}CMyWinApp theApp;BOOL CMyWinApp::InitInstance (){CMyFrameWnd *pFrame = new CMyFrameWnd;pFrame->Create (NULL, "MFCSplit", WS_OVERLAPPEDWINDOW, CFrameWnd::rectDefault, NULL, MAKEINTRESOURCE(IDR_MENU1));m_pMainWnd = pFrame;pFrame->ShowWindow (SW_SHOW);pFrame->UpdateWindow ();return TRUE;}
注:工程中还需要添加一个菜单资源,菜单只需一个下来菜单项,ID更改为ID_NEW,名字这里为:FileNew
上面的代码值得注意的是:创建视图对象以及窗口应该在框架窗口的OnCreate虚函数中完成,并且把创建的视图对象赋值给框架窗口的m_pViewActive以便程序已启动视图窗口就处于活动状态(并且建立了视图窗口和框架窗口的联系)。
另外:创建的视图窗口时指定了父窗口为框架窗口(this),这样结合在应用程序InitInstance函数中(m_pMainWnd = pFrame)的赋值可以总结如下对象的结构图
对象结构图:
m_pMainWnd =pFarame
m_pViewActive =pView
CMyWinApptheApp;//全局对象,随用随取
另外:AfxGetApp () 、AfxGetThread()均可放回theApp对象地址
theApp
|----->m_pMainWnd
|----------->m_pViewActice
三、断点跟踪分析
上面的代码中:视图类、框架类、窗口类我都添加了响应菜单的消息映射宏,如果想简单验证结论的话分别注释其中的任意一个测试即可得出如下命令的路由顺序:
视图类-->框架类-->应用程序类
当然这里我要从微软提供的源代码中一探究竟: 注释视图类和窗口类中的命令消息响应宏
在CMyView:;OnFileNew的入口处下断,F5调试启动程序,点击我们的FileNew菜单触发消息,那我我们就来到了断点处
在VC的工具栏:View-->DebugWindows-->CallStack可以看到CMyFileView::OnFileNew的调用堆栈,双击AfxWndProc进入该函数的入口处设置断点
停止当前调试,重新F5调试启动程序,来到AfxWndProc入口的断点处
AfxWndProc(HWND hWnd, UINT nMsg, WPARAM wParam, LPARAM lParam){// 参数hWnd保存了消息产生的窗口句柄......................................// 通过hWnd找到与之关联的类对象CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);.......................................return AfxCallWndProc(pWnd, hWnd, nMsg, wParam, lParam);}
跟进
AfxWndProc->AfxCallWndProc
LRESULT AFXAPI AfxCallWndProc(CWnd* pWnd, HWND hWnd, UINT nMsg,WPARAM wParam = 0, LPARAM lParam = 0){// pWnd为产生消息的对象pFrame ................................... // 利用pFrame调用WinddowProc,WindowProc为虚函数可以重写 // 而调用我们定义的窗口处理函数 lResult = pWnd->WindowProc(nMsg, wParam, lParam);}
跟进
AfxWndProc--> AfxCallWndProc-->WindowProc
LRESULT CWnd::WindowProc(UINT message, WPARAM wParam, LPARAM lParam){// this==pFrame..................................................if (!OnWndMsg(message, wParam, lParam, &lResult))lResult = DefWindowProc(message, wParam, lParam);return lResult;}
跟进
AfxWndProc-->AfxCallWndProc--> WindowProc-->OnWndMsg
BOOL CWnd::OnWndMsg(UINT message, WPARAM wParam, LPARAM lParam, LRESULT* pResult){// this===pFarme...........................................// 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;}// 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;}..........................................................}
在OnWndMsg中的if (message == WM_COMMAND)命令消息的处理分支中设置断点按F5运行并触发消息运行到该断点
跟进
AfxWndProc-->AfxCallWndProc-->WindowProc-->OnWndMsg-->OnCommand
BOOL CFrameWnd::OnCommand(WPARAM wParam, LPARAM lParam){// this===pFrame ....................................// route as normal commandreturn CWnd::OnCommand(wParam, lParam);}
跟进
AfxWndProc-->AfxCallWndProc-->WindowProc-->OnWndMsg-->OnCommand-->OnCommand
BOOL CWnd::OnCommand(WPARAM wParam, LPARAM lParam){// this===pFrame ........................................return OnCmdMsg(nID, nCode, NULL, NULL);}
跟进
AfxWndProc-->AfxCallWndProc-->WindowProc--> OnWndMsg-->OnCommand-->OnCommand-->OnCmdMsg
BOOL CFrameWnd::OnCmdMsg(UINT nID, int nCode, void* pExtra,AFX_CMDHANDLERINFO* pHandlerInfo){// this==pFrame ....................................// pump through current view FIRSTCView* pView = GetActiveView();if (pView != NULL && pView->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))return TRUE;// then pump through frameif (CWnd::OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))return TRUE;// last but not least, pump through appCWinApp* pApp = AfxGetApp();if (pApp != NULL && pApp->OnCmdMsg(nID, nCode, pExtra, pHandlerInfo))return TRUE;return FALSE;}
在这个函数中可以清晰的看到命令消息的路由处理过程,CView-->CFrameWnd-->theApp
关于消息的查找过程前面的一片博文已经分析过了这里不再赘述,大概绘制如下消息映射查找链表如下:
那么这里就仅对theApp的消息处理过程做分析
跟进
AfxWndProc--> AfxCallWndProc--> WindowProc--> OnWndMsg-->OnCommand-->OnCmdMsg
BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra,AFX_CMDHANDLERINFO* pHandlerInfo){// this===pViewfor (pMessageMap = GetMessageMap(); pMessageMap != NULL;pMessageMap = pMessageMap->pBaseMap){// pMessageMap = GetMessageMap()拿到消息映射链表的头结点也即theApp对象中的静态成员messageMap的地址// 到_messageEntries数组中查找和包含消息对应的处理函数的元素,若找到lpEntry则指向该元素否则为空继续查找const AFX_MSGMAP_ENTRY* lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries, nMsg, nCode, nID);.......................................// 如果查找到直接派发调用该消息对象对应的处理函数return _AfxDispatchCmdMsg(this, nID, nCode,lpEntry->pfn, pExtra, lpEntry->nSig, pHandlerInfo);}.........................................................}
好了,到这里命令消息的路由过程已经分析完毕了
pView-->pFarme-->theApp
- MFC命令消息路由过程(视图、框架、应用)
- MFC文档视图(三):命令消息路由
- MFC文档视图(三):命令消息路由
- MFC文档视图(三):命令消息路由
- MFC中框架文档视图结构的命令路由处理
- MFC 后台执行过程--消息路由
- MFC命令消息路由 ------ (图)
- MFC消息映射和命令路由
- 菜单命令消息路由过程分析
- 菜单命令消息路由过程分析
- 如何为SDI程序中多个不同视图路由命令消息
- MFC框架下消息的处理过程
- MFC中的消息路由
- MFC的消息路由
- 深入浅出MFC:消息路由
- MFC消息路由
- MFC 命令和消息路由 (command and message route)
- MFC中文档框架视图处理消息的顺序
- 数值的整数次方
- JavaScript中in运算符和instanceof运算符demo,java中instanceof用法
- 提高程序员项目设计水平的11条建议
- 00_打电话的广播接收者
- Shell命令sshpass非交互SSH密码验证
- MFC命令消息路由过程(视图、框架、应用)
- 计算几何题集
- 王垠:对博士学位说永别(我对清华更崇拜了)
- 数组——某年的第几天
- 数组指针和指针数组
- hdu4292 Food 网络最大流 拆点
- POJ_3090_Visible Lattice Point_欧拉函数
- 通过Gradle自动生成通过Git提交的version
- uva 270 Lining Up