MFC命令消息路由过程(视图、框架、应用)

一、环境

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 command
	return 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 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-->theApp

关于消息的查找过程前面的一片博文已经分析过了这里不再赘述,大概绘制如下消息映射查找链表如下:

那么这里就仅对theApp的消息处理过程做分析

AfxWndProc--> AfxCallWndProc--> WindowProc--> OnWndMsg-->OnCommand-->OnCmdMsg

BOOL CCmdTarget::OnCmdMsg(UINT nID, int nCode, void* pExtra,
	AFX_CMDHANDLERINFO* pHandlerInfo)
{// this===pView
		for (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

时间: 2024-10-24 21:17:49

MFC命令消息路由过程(视图、框架、应用)的相关文章

菜单命令的路由

一.Windows消息的分类 ①标准消息 除WM_COMMAND之外,所有的以WM_开头的消息都是标准消息,从CWnd类派生的类,都可以接收到这类消息. ②命令消息 来自菜单.加速键.工具栏的消息.这类消息都以WM_COMMAND形式呈现.在MFC中通过菜单项ID区分不同的命 令消息,在SDK中,通过消息的wParam参数识别.从CCmdTarget派生的类,都可以接收这类消息. ③通告消息 由控件产生的消息,目的是为了向其父窗口通知事件的发生.这类消息也以WM_COMMAND形式呈现.从 CC

深入MFC中WM_COMMAND命令消息的传递

MFC将windows消息系统进行了高度的抽象和封装,其根本原理是运用C++的高级特性并结合一定的设计模式(如工厂模式,模板方法等)来实现的.一般的windows消息(WM_XXX),则一定是由派生类流向基类,没有旁流的可能.如果是命令消息(WM_COMMAND),那就有比较奇特的路线了.下面就针对多文档/单文档(Document-View).对话框两种应用程序比较讨论WM_COMMAND消息的传递处理过程.讨论前首先得明确命令消息的来源,命令消息一般是用户选择某个菜单项,或一个加速键被翻译,或

MFC消息路由

1.Command Routing(命令传递):当消息进来时,会有一个泵推动它前进.消息如何进来,以有泵函数如何推动,都是属于windows程序设计的范畴, 消息如果是从子类流向父类(纵向流动),那么事情再简单不过,整个message map消息映射表已规划出十分明确的路线.消息应该有横向流动的机会,MFC对于消息循环的规定是: 如果是一般的windows消息(WM_XXX),则一定是由派生类流向基类,没有旁流的可能. 如果是命令消息(WM_COMMAND),那就有奇特的路线了.   2.消息映

MFC中消息响应机制

由于视类窗口始终覆盖在框架类窗口之上,因此所有操作,包括鼠标单击.鼠标移动等操作都只能由视类窗口捕获.一个MFC消息响应函数在程序中有三处相关信息:函数原型.函数实现和以及用来关联消息和消息响应函数的宏. (1)在消息响应函数的原型代码中,函数声明的前部有一个afx_msg限定符,也是一个宏,该宏表明这个函数是一个消息响应函数的声明. (2)消息映射宏:在视图类的源文件中,BEGIN_MESSAGE_MAP()和 END_MASSAGE_MAP()这两个宏之间定义了消息映射表,例如对于画线,其中

MFC - windows消息大全

1 Windows窗口消息大全,全不全自己看 2 3 ////////////////////////////////////////////////////////////////////////// 4 #include "AFXPRIV.H"//消息值的定义来源 5 #include "Dde.h"//DDE消息值的定义来源 6 #include "CPL.H"//控制面板消息值的定义来源 7 #include "WFEXT.H&

MFC-消息分派

前言 由于工作需要,这几天学了一点MFC,在AFX里看到很多熟悉的东西,如类型信息,序列化,窗口封装和消息分派.几乎每个界面库都必须提供这些基础服务,但提供的手法却千差万别.MFC大量地借用了宏,映射表来实现,而VCL则更多的在语言级别上给与支持.这其实是很容易理解的,因为C++是一个标准,不会因某个应用而随便扩展语言:相反Delphi完全由一个公司掌握,因此每支持一项新技术,变化最大的往往是语言本身. 学习MFC的代码,再对照VCL的实现,这真是一个很有意思的过程,其中可以看到两个框架在一些设

MFC的消息机制

MFC的消息循环(::GetMessage,::PeekMessage)消息泵(CWinThread::PumpMessage)和MFC的消息在窗口之间的路由是两件不同的事情 分两个步骤完成: 1 "消息派送":非对话框程序(MFC Doc/View架构)和对话框程序略有不同,但原理相差不大.但除了少数的消息例如WM_QUIT,所有消息的派送流程基本一致.2 "消息路由":其中消息分有3种类型,各类型的路由过程不一样.非对话框程序和对话框程序的处理相同. 消息派送消

分布式数据库中间件–(3) Cobar对简单select命令的处理过程

友情提示:非原文链接可能会影响您的阅读体验,欢迎查看原文.(http://blog.geekcome.com) 原文地址:http://blog.geekcome.com/archives/284 在上一篇中介绍了Cobar和client初次建立连接的过程,Cobar监听端口,client发起连接请求,Cobar发送握手数据包,client发送认证数据包最后依据认证的结果Cobar向client发送认证结果. 在认证成功后Cobar会将该连接的回调处理函数由FrontendAuthenticat

MFC程序的启动过程,很清楚,但仍有待改进

原文出自:http://blog.csdn.net/yuvmen/article/details/5877271 了解MFC程序的启动过程,对于初学者来讲,了学习MFC很有帮助:对于不常用VC的人来说,过一段时间就会忘记.还是来记下来,方便以后查阅. 1.创建Application object对象theApp 程序一开始生产一个(且只有一个)Application object对象theApp,也即一个CWinApp对象,这个全局对象一产生,便执行其构造函数,因为并没有定义CMyWinApp构