MFC 消息映射机制

目录

消息映射机制概述

宏展开

宏展开的作用

消息映射机制的执行流程

消息处理


消息映射机制概述

MFC的消息映射映射机制是可以在不重写WindowProc虚函数的大前提下,仍然可以处理消息。

类必须具备的要件

类内必须添加声明宏   DECLARE_MESSAGE_MAP()

类外必须添加实现宏:

  • BEGIN_MESSAGE_MAP(theClass , baseClass)
  • END_MESSAGE_MAP()

总结:当一个类具备上述两个要件,这个类就可以按照消息映射机制来处理消息。

MFC利用消息映射机制处理消息:以处理WM_CREATE消息为例

类内:

  • 添加声明宏   DECLARE_MESSAGE_MAP()
  • 添加处理WM_CREATE消息的函数声明

类外:

  • 添加宏:BEGIN_MESSAGE_MAP(theClass , baseClass)   END_MESSAGE_MAP()
  • 实现处理WM_CREATE消息的函数定义

宏展开

对宏代码进行展开,得到下面的成果

#include <afxwin.h>
class CMyFrameWnd : public CFrameWnd {
	//	DECLARE_MESSAGE_MAP()
protected:
	static const AFX_MSGMAP* PASCAL GetThisMessageMap();
	virtual const AFX_MSGMAP* GetMessageMap() const;
public:
	LRESULT OnCreate(WPARAM wParam, LPARAM lParam);
};
//BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)
//	ON_MESSAGE( WM_CREATE, OnCreate )
//END_MESSAGE_MAP()


const AFX_MSGMAP* CMyFrameWnd::GetMessageMap() const
{
	return GetThisMessageMap();
}
const AFX_MSGMAP* PASCAL CMyFrameWnd::GetThisMessageMap()
{
	static const AFX_MSGMAP_ENTRY _messageEntries[] =
	{
		{ WM_CREATE, 0, 0, 0, AfxSig_lwl, (AFX_PMSG)(AFX_PMSGW)
			(static_cast<LRESULT(AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM)> (&OnCreate)) },

		{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }
	};
	static const AFX_MSGMAP messageMap = { &CFrameWnd::GetThisMessageMap, &_messageEntries[0] };
	return &messageMap;
}

LRESULT CMyFrameWnd::OnCreate(WPARAM wParam, LPARAM lParam) {
	AfxMessageBox("WM_CREATE");
	return 0;
}

class CMyWinApp : public CWinApp {
public:
	virtual BOOL InitInstance();
};
CMyWinApp theApp;//爆破点
BOOL CMyWinApp::InitInstance() {
	CMyFrameWnd* pFrame = new CMyFrameWnd;
	pFrame->Create(NULL, "MFCCreate");
	m_pMainWnd = pFrame;
	pFrame->ShowWindow(SW_SHOW);
	pFrame->UpdateWindow();
	return TRUE;
}

 声明了两个成员函数

DECLARE_MESSAGE_MAP()

声明两个函数:

protected:
	static const AFX_MSGMAP* PASCAL GetThisMessageMap();
	virtual const AFX_MSGMAP* GetMessageMap() const;

拓展:在C++中,static修饰函数可以有以下两种含义:

(1) 静态成员函数,特点包括:

  1. 不属于类的任何特定对象,而是属于整个类。
  2. 可以访问类的静态成员变量和其他静态成员函数,但不能直接访问类的非静态成员变量和非静态成员函数。
  3. 不能使用this指针,因为this指针指向类的对象实例,而静态成员函数并不属于任何特定对象。
  4. 静态成员函数可以直接通过作用域解析运算符(::)访问类的静态成员变量和静态成员函数,无需通过对象。

静态成员函数通常用于执行与类相关的操作,而不依赖于特定对象的状态。例如,可以在静态成员函数中计算或处理类的静态成员变量,或者实现与类相关的全局操作。

(2) 文件作用域的静态函数

文件作用域的静态函数是指在C或C++中使用static关键字声明的函数,这种函数的作用域限定在当前文件内,不能被其他文件访问或调用。在文件中使用static修饰的函数通常用于实现模块内部的辅助函数或者限制函数的作用域,以减少全局命名空间的污染。

拓展:在C++中,const修饰函数可以分为两种情况:const成员函数和const修饰的非成员函数。

(1)  const成员函数:const成员函数是指在函数声明或定义的末尾加上const关键字,用于表示该成员函数不会修改对象的状态。在const成员函数中,不能修改成员变量的值,也不能调用非const成员函数,以确保该函数不会改变对象的状态。

(2)  const修饰的非成员函数: const修饰的非成员函数是指在函数声明或定义的末尾加上const关键字,用于表示函数的返回值是常量。

示例:

const int getValue() {
    return 10; // 返回一个常量值
}

const string& getName() {
    static const string name = "John";
    return name; // 返回一个常量引用
}

实现了两个函数

BEGIN_MESSAGE_MAP(CMyFrameWnd, CFrameWnd)

END_MESSAGE_MAP()

第一个函数的返回值是第二个函数的返回值 

const AFX_MSGMAP* CMyFrameWnd::GetMessageMap() const
{
	return GetThisMessageMap();
}
const AFX_MSGMAP* PASCAL CMyFrameWnd::GetThisMessageMap()
{
	static const AFX_MSGMAP_ENTRY _messageEntries[] =
	{
		{ WM_CREATE, 0, 0, 0, AfxSig_lwl, (AFX_PMSG)(AFX_PMSGW)
			(static_cast<LRESULT(AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM)> (&OnCreate)) },

		{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }
	};
	static const AFX_MSGMAP messageMap = { &CFrameWnd::GetThisMessageMap, &_messageEntries[0] };
	return &messageMap;
}

ON_MESSAGE( WM_CREATE, OnCreate )  相当于

{ WM_CREATE, 0, 0, 0, AfxSig_lwl, (AFX_PMSG)(AFX_PMSGW)
			(static_cast<LRESULT(AFX_MSG_CALL CWnd::*)(WPARAM, LPARAM)> (&OnCreate)) },

宏展开的作用

首先学习两个数据结构

这个结构体,与我们需要处理的消息有关,主要需要关注第一个与最后一个即可

struct AFX_MSGMAP_ENTRY
{
	UINT nMessage;    
	UINT nCode;       
	UINT nID;       
	UINT nLastID;    
	UINT_PTR nSig;     
	AFX_PMSG pfn;    
};

这个结构体的成员表示如下:

  • 消息ID,用于标识Windows消息类型的整数值。
  • 通知码,标识控件ID值
  • 命令ID,用于区分控件的不同命令,比如:有一个“打开”菜单项和一个“保存”菜单项,它们分别对应着打开文件和保存文件的操作。在程序内部,为了识别用户点击了哪个菜单项,就需要为每个菜单项分配一个唯一的命令ID。当用户点击“打开”菜单项时,程序就会根据这个命令ID来执行打开文件的操作;当用户点击“保存”菜单项时,程序则会根据另一个命令ID来执行保存文件的操作。
  • 最后一个命令ID,用于标识此消息关联的前一个命令的整数值。
  • 处理消息的函数类型
  • 处理消息的函数名(地址)

这个结构体主要和遍历链表有关

struct AFX_MSGMAP
{
	const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
	const AFX_MSGMAP_ENTRY* lpEntries;
};

这个结构体的成员表示如下:

  • 父类宏展开的静态变量地址
  • 本类宏展开的静态数组首地址

宏展开各部分的作用

局部静态变量:与普通的局部变量不同之处在于其生存期和作用域。当函数被调用时,静态局部变量不会被销毁,而是保留其数值,直到程序运行结束。此外,在函数内部,静态局部变量的作用域仅限于声明它的函数内部。

GetThisMessageMap():静态函数

作用:定义静态变量和静态数组,并返回本类静态变量地址(获取链表头)

_messageEntries[]:静态数组(进程级声明周期)

作用:数组每个元素,保存为 消息ID 和 处理消息的函数名(地址)

messageMap:静态变量(进程级声明周期)

作用:第一个成员,保存父类宏展开的静态变量地址(负责连接链表)

           第二个成员,保存本类的静态数组首地址

GetMessageMap():虚函数

作用:返回本类静态变量地址(获取链表头)

两个结构体在消息映射机制实现的作用

  • messageMap第一个是父类GetThisMessageMap函数地址,第二个是本类的_messageEntries数组地址
  • _messageEntries结构体数组地址中的每一个元素都是消息和对应处理函数地址

CMyFrameWnd有一套这样的局部静态本类,CFrameWnd,CWnd都有,到此为止,CWnd的父类就没有了

这就构成一个链表的结构:

在 CFrameWnd,CWnd 类中都有对消息的处理函数

消息映射机制的执行流程

下载WM_CREATE消息处理函数下断点

消息产生进入窗口处理函数(AfxWndProc),对此函数下断点开始分析,前三个消息不是WM_CREATE消息,先F5放过,直到 nMsg 值为1

通过句柄拿到框架窗口对象

CWnd* pWnd = CWnd::FromHandlePermanent(hWnd);

之后调用AfxCallWndProc,在之后调用WindowProc

lResult = pWnd->WindowProc(nMsg, wParam, lParam);

再之后调用OnWndMsg

	if (!OnWndMsg(message, wParam, lParam, &lResult))
		lResult = DefWindowProc(message, wParam, lParam);

在这里对不同的消息处理都不一样

获取本类宏站开的静态变量的地址(链表头结点)

const AFX_MSGMAP* pMessageMap; pMessageMap = GetMessageMap();

F11,回到我们的代码了

开始遍历结构体数组,循环中每次迭代条件就是获得父类GetThisMessageMap函数地址

如果找到返回找到的数组元素的地址,如果没找到返回NULL,找到之后goto跳出循环

if ((lpEntry = AfxFindMessageEntry(pMessageMap->lpEntries,message, 0, 0)) != NULL)
{
	pMsgCache->lpEntry = lpEntry;
	winMsgLock.Unlock();
	goto LDispatch;
}
lpEntry->pfn; //CMyFrameWnd::OnCreate

调用CMyFrameWnd::OnCreate函数完成消息的处理

LRESULT CMyFrameWnd::OnCreate(WPARAM wParam, LPARAM lParam) {
	AfxMessageBox("WM_CREATE");
	return 0;
}

执行完后,再次会到这里

总结:

  1. 消息产生进入窗口处理函数(AfxWndProc )
  2. 根据已知窗口句柄,找到和它绑定在一起的框架类对象地址( pFrame )。
  3. 利用框架类对象地址( pFrame )调用框架类成员虚函数WindowProc
  4. 获取本类对应的静态变量,并到对应数组中匹配查找。
  5. 如果没有找到获取父类对应的静态变量,并到对应数组中匹配查找。
  6. 如果找到了,利用找到的数组元素的最后一个成员,并调用之,完成消息处理。
     

消息分类

主要有以下三类:

  • 标准windows消息:ON_WM_XXX
  • 自定义消息:ON_MESSAGE
  • 命令消息:ON_COMMAND,暂且不管

第一类消息处理函数的返回值,参数都是固定的,第二类就不是了。这些规定可以再MSDN中查到

  • 32
    点赞
  • 28
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
MFC(Microsoft Foundation Classes)是一组用于开发 Windows 应用程序的 C++ 类库。在 MFC 中,消息映射是一种机制,用于将 Windows 消息与相应的消息处理函数关联起来。 消息映射是通过在类的消息映射表中定义消息处理函数来实现的。消息映射表是一个由宏定义和消息映射项组成的静态数组。每个消息映射项指定了一个 Windows 消息的 ID 和相应的消息处理函数。 消息处理函数是类中的成员函数,用于处理特定的 Windows 消息。当一个窗口接收到一个消息时,MFC 会根据消息映射表中的定义找到对应的消息处理函数,并执行该函数来处理该消息。 下面是一个简单的示例,展示了如何使用消息映射消息处理函数: ```cpp // 声明消息映射表 BEGIN_MESSAGE_MAP(CMyWnd, CWnd) ON_WM_PAINT() ON_WM_LBUTTONDOWN() END_MESSAGE_MAP() // 定义消息处理函数 void CMyWnd::OnPaint() { // 处理 WM_PAINT 消息 // ... } void CMyWnd::OnLButtonDown(UINT nFlags, CPoint point) { // 处理 WM_LBUTTONDOWN 消息 // ... } ``` 在上面的示例中,`CMyWnd` 是一个继承自 `CWnd` 的自定义窗口类。通过在消息映射表中使用 `ON_WM_PAINT` 宏和 `ON_WM_LBUTTONDOWN` 宏,将 `OnPaint` 函数和 `OnLButtonDown` 函数与 `WM_PAINT` 消息和 `WM_LBUTTONDOWN` 消息关联起来。 当窗口接收到相应的消息时,就会调用对应的消息处理函数进行处理。 需要注意的是,消息映射表中的消息处理函数必须是类的成员函数,并且符合特定的函数签名。 希望这个简单的解释能够帮助你理解 MFC 中的消息映射消息处理机制。如果还有其他问题,请随时提问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

Dao-道法自然

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值