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中查到

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.hqwc.cn/news/295520.html

如若内容造成侵权/违法违规/事实不符,请联系编程知识网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【C++】bind绑定包装器全解(代码演示,例题演示)

前言 大家好吖&#xff0c;欢迎来到 YY 滴C系列 &#xff0c;热烈欢迎&#xff01; 本章主要内容面向接触过C的老铁 主要内容含&#xff1a; 欢迎订阅 YY滴C专栏&#xff01;更多干货持续更新&#xff01;以下是传送门&#xff01; YY的《C》专栏YY的《C11》专栏YY的《Linux》…

即将来临的2024年,汽车战场再起波澜?

我们来简要概况一下11月主流车企的销量表现&#xff1a; 根据数据显示&#xff0c;11月吉利集团总销量29.32万辆&#xff0c;同比增长28%。这在当月国内主流车企中综合实力凌厉&#xff0c;可谓表现得体。而与吉利直接竞争的比亚迪&#xff0c;尽管数据未公布&#xff0c;但我们…

邦火策划的餐饮品牌策划,到底是不是打破常规的创新之选?

在餐饮业的浩瀚市场中&#xff0c;邦火策划以其独特的餐饮品牌策划服务&#xff0c;引起了业界的关注。邦火策划是否真的是打破常规的创新之选&#xff1f;让我们深入了解&#xff0c;探寻其中的奥秘。 首先&#xff0c;邦火策划在餐饮品牌策划中的创新之处在于对市场的深度洞…

如何查看内存卡使用记录-查看的设备有:U盘、移动硬盘、MP3、SD卡等-供大家学习研究参考

主要功能 USB Viewer&#xff08;USB移动存储设备使用记录查看器&#xff09;可用于查看本机的USB移动存储设备使用记录。可查看的设备有&#xff1a;U盘、移动硬盘、MP3、SD卡……等。   可用于兵器、航空、航天、政府、军队等对保密要求较高的单位&#xff0c;可在计算机保…

【操作系统】学习操作系统知识

文章目录 前言测量系统调用和上下文切换的成本purify 和 valgrindxx3 的执行过程 前言 ref&#xff1a;http://ges.cs.wisc.edu/~remzi/OSTEP/Chinese 零散的记录知识&#xff0c;看《操作系统引论》 测量系统调用和上下文切换的成本 上下文切换需要多长时间&#xff1f;甚…

C++面向对象(OOP)编程-STL详解(vector)

本文主要介绍STL六大组件&#xff0c;并主要介绍一些容器的使用。 目录 1 泛型编程 2 CSTL 3 STL 六大组件 4 容器 4.1 顺序性容器 4.1.1 顺序性容器的使用场景 4.2 关联式容器 4.2.1 关联式容器的使用场景 4.3 容器适配器 4.3.1 容器适配器的使用场景 5 具体容器的…

建筑模板的规格是多大的呢?

建筑模板的规格有多种&#xff0c;它们的尺寸可以根据不同的建筑需求和标准而有所不同。常见的建筑模板规格包括但不限于以下几种&#xff1a; 1. 木模板&#xff1a; - 常见的木模板尺寸包括915mm x 1830mm&#xff08;3英尺 x 6英尺&#xff09;和1220mm x 2440mm&#xff08…

linux设置线程优先级以及调度策略浅析

linux线程调度策略 Linux内核会根据线程的优先级和调度策略来分配处理器时间。线程的优先级越高&#xff0c;它在竞争处理器时间时就越有可能被选中执行。调度策略定义了内核在选择下一个要执行的线程时所遵循的规则。 在Linux中&#xff0c;有以下几种常见的调度策略&#x…

屏幕颜色吸取器

前言 屏幕颜色吸取器。 前端工程师的福音&#xff0c;获取全屏幕上所有位置的颜色。 运行在window上的软件 屏幕颜色吸取器 前言1 下载解压2 使用 1 下载解压 下载地址&#xff1a;https://download.csdn.net/download/qq_44850489/11943229 下载下来之后解压 如下图&#…

数据流图_DFD图_精简易上手

数据流图(DFD)是一种图形化技术,它描绘信息流和数据从输人移动到输出的过程中所经受的变换。 首先给出一个数据流图样例 基本的四种图形 直角矩形:代表源点或终点,一般来说,是人,如例图的仓库管理员和采购员圆形(也可以画成圆角矩形):是处理,一般来说,是动作,是动词名词的形式…

Java开发框架和中间件面试题(4)

27.如何自定义Spring Boot Starter&#xff1f; 1.实现功能 2.添加Properties 3.添加AutoConfiguration 4.添加spring.factory 在META INF下创建spring.factory文件 6.install 28.为什么需要spring boot maven plugin? spring boot maven plugin 提供了一些像jar一样打包…

生物信息学R分析工具包ggkegg的详细使用方法

ggkegg介绍 ggkegg 是一个用于生物信息学研究的工具&#xff0c;可以用于分析和解释基因组学数据&#xff0c;并将其与已知的KEGG数据库进行比较。ggkegg 是从 KEGG 获取信息并使用 ggplot2 和 ggraph 进行解析、分析和可视化的工具包&#xff0c;结合其他使用 KEGG 进行生物功…