MFC消息映射机制

   每个可以接收消息或命令的框架类(framework class)都有其自己的消息映射(messge map),框架使用消息映射来连接消息或命令到其处理函数(handle function),任何从CCmdTarget类派生的类都有消息映射(有CView视图类,CWinApp应用程序类,CDocument文档类,CWnd窗口类和CFrameWnd框架窗口类,这些CCmdTarget基类的派生类都可以处理消息)。
   尽管叫作消息映射,消息映射即可以处理消息也可以处理命令,
   <消息分类>
   为之写处理函数的消息由三个主要类别:
   1.1 Windows消息
   这主要包括哪些以WM_前缀开头的消息,但WM_COMMAND除外。Windows消息由窗口和视图处理。这些消息通常具有用于确定如何处理消息的参数。
   1.2 控件通知
   这包括从控件和其他子窗口到其父窗口的WM_COMMAND通知消息。例如,当用户执行了可能更改了编辑控件中的文本的操作时,编辑控件会向其父控件发送包含EN_CHANGE控件通知代码的WM_COMMAND消息。消息的窗口处理程序以某种适当的方式响应通知消息,例如检索控件中的文本。
   该框架箱其他WM_消息一样路由控件通知消息,但一个例外是按钮在用户单击时发送的BN_CLICKED控件通知消息。该消息被特别地视为命令消息,并像其他命令一样被路由。

CWnd::WindowProc
为CWnd对象提供提供窗口程序(WindowProc)

virtual LRESULT WindowProc(  //通过窗口的消息映射message map分发消息dispatches messages
    UINT message,   //指定要被处理的窗口消息
    WPARAM wParam,  //提供处理消息要使用的额外的信息,参数值取决于消息
    LPARAM lParam); //提供处理消息要使用的额外的信息,参数值取决于消息

CWnd::OnWndMsg()

virtual BOOL OnWndMsg( //TRUE如果消息被处理 否则FALSE
    UINT message,      //指定被发送的消息
    WPARAM wParam,     //指定额外的取决于消息的信息
    LPARAM lParam,     //指定额外的取决于消息的信息
    LRESULT* pResult); //CWnd::WindowProc的返回值 取决于消息 可以为NULL

该成员函数由CWnd::WindowProc调用,或在消息反射(message reflection)期间被调用。
CWnd::OnWndMsg()判定消息类型并且|或调用适当的框架函数(例如为WM_COMMAND消息调用OnCommand()函数)|或在消息映射中找到适当的消息。

Windows控件的消息反射message reflect:
消息反射(控件消息传递给父窗口后又反射给自身处理,避免所有使用该控件的父窗口类重复处理控件消息):
Window控件经常向其父窗口发送通知消息。例如,许多控件向其父级发送控件颜色通知消息(WM_CTLCOLOR或其变体之一),以允许父级提供用户绘制控件背景的画笔。
在Windows和4.0版之前的MFC中,父窗口(通常是一个对话框)负责处理这些消息。这意味着用户处理消息的代码必须位于父窗口的类中,并且IXUS在需要处理该消息的每个类(父窗口类)中进行重复。在上述情况下,每个具有自定义背景的控件的对话框都必须处理控件颜色通知消息。如果可以编写处理自己背景色的控件类(自定义控件类),则重用代码会容易得多。
在MFC4.0中,旧机制仍然有效——父窗口可以处理(子窗口、控件发送的)通知消息。但是,此外,MFC4.0通过提供一种称为“消息反射”的功能来促进重用,该功能允许在子控件或父窗口或两者中处理这些通知消息。在控件背景色实例中,您现在可以编写一个自定义控件类,该自定义控件类通过处理反射Reflect的WM_CTLCOLOR消息来设置自己的背景色,而无需依赖父类。(注意到由于消息反射是由MFC而不是Windows实现的,所以父窗口类必须派生自CWnd来实现消息反射。)
Windows控件的消息反射

用户界面对象和命令ID
菜单项、工具栏按钮和加速键是能够生成命令的“用户界面对象”。每个这样的用户界面对象都有一个ID。通过为对象和命令分配相同的ID,可以将用户界面对象与命令相关联(例如当用户点击了某界面对象,根据该界面对象ID找到对应ID的命令,然后将其发送到父窗口的消息队列或CWnd::WindowProc)。命令被实现为特殊的消息。

CWinApp类的Run()成员函数接收消息并派发其到适当的窗口。大多数命令消息被发送到应用程序的主框架窗口(顶层窗口)。由类库(class library)预定义的WindowProc(CWnd::WindowProc())获得消息并根据收到消息的类型进行不同的路由。

HRESULT Run(_In_ int nShowCmd = SW_HIDE) throw()
	{
		HRESULT hr = S_OK;

		T* pT = static_cast<T*>(this);
		hr = pT->PreMessageLoop(nShowCmd);

		// Call RunMessageLoop only if PreMessageLoop returns S_OK.
		if (hr == S_OK)
		{
			pT->RunMessageLoop();
		}

		// Call PostMessageLoop if PreMessageLoop returns success.
		if (SUCCEEDED(hr))
		{
			hr = pT->PostMessageLoop();
		}

		ATLASSERT(SUCCEEDED(hr));
		return hr;
	}
   一个消息的初始的接收者必须是一个窗口对象。Windows消息通常直接被其窗口对象处理。命令消息,通常源自于应用程序的主框架窗口,被路由到命令-目标链(见下命令路由)

命令路由Command Routing
添加链接描述
程序员对命令有关的操作只限于构造消息映射以联系命令与其处理函数,这也是使用MFC类先导的一项任务。同时也要为命令处理函数编写代码。
Windows消息通常发送给主框架窗口,但命令消息随后被路由到其他对象(例如菜单栏保存文件命令被CFrameWnd主框架窗口类路由到CDocument文档类执行保存文件函数)。框架通过一个标注的命令-目标对象序列(sequence of command-target objects)来路由命令,一个命令-目标对象被期望为一个命令指定一个处理函数。每个命令-目标对象检查其映射看其是否能处理传入的消息。
不同的命令-目标对象类在不同的时间检查他们的消息映射(即执行其(虚)继承自CCmdTarget类的OnCmdMsg()函数找响应函数)。通常,类将命令路由到某些其他对象,使他们有机会优先使用该命。如果这些对象都不处理命令,则原始类检查其自己的消息映射。然后,如果它本身不能提供处理程序,则可能会将命令路由到其他命令-目标对象。下表 标准命令路由表 显示了每个类如何构造此序列。命令-目标路由命令的一般顺序为:
1、到其当前活动的子命令-目标对象;
2、本身;
3、到其他命令-目标对象

   与您的处理程序响应命令所执行的操作相比,此路由机制的成本低。请记住,仅当用户与用户界面对象进行交互时,框架窗口才会生成命令

标准路由路线:
在这里插入图片描述
https://www.jianshu.com/p/5fd5bdaac69c
CCmdTarget::OnCmdMsg()处理程序(可虚继承)
为了完成命令路由,每个命令目标对象(CCmdTarget类及其派生类对象)按序调用下个命令目标对象的OnCmdMsg()成员函数。命令目标对象使用其OnCmdMsg()函数决定其是否能处理该命令消息,如果不能则将其路由到另一个命令目标对象。
每个命令目标对象类都可以重载CCmdTarget::OnCmdMsg()成员函数。重载使每个类路由命令消息到指定的下个目标对象。例如,一个框架窗口frame window总是路由命令消息到其当前子窗口或当前视图CView。像标准命令路由表中所示。
”OnCmdMsg()函数的默认CCmdTarget基类实现“使用命令-目标对象类的消息映射去为其收到的每个命令消息搜索处理函数,与标注消息被搜索的方式一样。如果找到匹配的消息响应函数则调用之。
例如用户点击菜单项(或控件)后发出COMMAND消息(或Window消息)后 函数调用过程(消息路由过程)如下:(如果没有任何对象处理该条WM_COMMAND消息,最后会被发出该消息的CWnd对象的DefWindowProc()函数处理)
在这里插入图片描述
windows消息机制(MFC)
每个有能力接收消息或命令的对象都有其消息映射将消息或命令与其处理函数组对。

派生消息映射

在消息处理过程中,检查类自己的消息映射并不是消息映射故事的结尾。如果类CMyView(源自CView)没有匹配的消息条目,会发生什么情况?
CMyView的基类CView又是派生自CWnd,因此CMyView是一个CView也是一个CWnd。这些类中的每一个都有其自己的消息映射。但CMyView对象是具有所有三个类特征的单个对象。
在这里插入图片描述
因此,如果消息不能再CMyClass类的消息映射中匹配,则框架还会搜索其直接基类的消息映射。BEGIN_MESSAGE_MAP消息映射开头的宏执行两个类名作为其参数:

BEGIN_MESSAGE_MAP(CMyView, CFormView)
   第一个参数命名消息映射所属的类。第二个参数声明了直接基类,提供了与直接基类的连接,因此框架也可以搜索基类的消息映射。
   因此,派生类继承了基类中提供的消息处理程序(基类一般会有同名的消息处理程序 防止派生类忘记定义或没找到)。这与正常的虚继承成员函数非常相似,无需将所有响应成员函数设为虚拟。
   如果在任何基类消息映射中均未找到处理程序,则将对消息进行默认处理。如果消息是命令,则框架会将其路由到下一个CCmdTarget对象。如果它是标准Windows消息,则该消息将传递到适当的默认窗口过程,CWnd::DefWindowProc(()。
   为了加快消息映射的匹配速度,框架会缓存最近的匹配结果,以防再次收到相同的消息。其结果之一是,该框架非常有效地处理了未处理的消息。消息映射也比使用虚函数的实现更节省空间(即将所有消息响应函数从基类虚继承但却又调用基类的同名消息响应函数 消息映射只虚继承派生类要自己实现的消息响应函数)。
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值