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.mzph.cn/news/235498.shtml

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

相关文章

linux 应用开发笔记---【线程】

1.概念&#xff1a; 线程是参与系统调度的最小单位&#xff0c;它被包含在进程中&#xff0c;是进程的实际运行单位 一个进程可以创建多个线程&#xff0c;多个线程并发运行&#xff0c;每个线程执行不同的任务 2.如何创建线程 当一个程序启动的时候&#xff0c;一个进程被…

Cesium 3DTiles数据格式详解

目录 0 引言1 3DTiles1.1 起源1.2 后缀类型及特点1.2.1 b3dm1.2.2 i3dm1.2.3 pnts1.2.4 cmpt1.2.5 json1.2.6 总结 &#x1f64b;‍♂️ 作者&#xff1a;海码007&#x1f4dc; 专栏&#xff1a;CesiumforUnreal专栏&#x1f4a5; 标题&#xff1a;Cesium 3DTiles数据格式详解❣…

【Netty】Netty核心API及使用

目录 Netty核心APIChannelHandler及其实现类ChannelPipelineChannelHandlerContextChannelOptionChannelFutureEventLoopGroup和实现类NioEventLoopGroupServerBootstrap和BootstrapUnpooled类 Netty入门案例引入依赖Netty服务端编写Netty客户端编写 Netty核心API ChannelHand…

智能优化算法应用:基于龙格-库塔算法3D无线传感器网络(WSN)覆盖优化 - 附代码

智能优化算法应用&#xff1a;基于龙格-库塔算法3D无线传感器网络(WSN)覆盖优化 - 附代码 文章目录 智能优化算法应用&#xff1a;基于龙格-库塔算法3D无线传感器网络(WSN)覆盖优化 - 附代码1.无线传感网络节点模型2.覆盖数学模型及分析3.龙格-库塔算法4.实验参数设定5.算法结果…

FPGA模块——以太网芯片MDIO读写

FPGA模块——以太网MDIO读写 MDIO接口介绍MDIO接口代码&#xff08;1&#xff09;MDIO接口驱动代码&#xff08;2&#xff09;使用MDIO驱动的代码 MDIO接口介绍 MDIO是串行管理接口。MAC 和 PHY 芯片有一个配置接口&#xff0c;即 MDIO 接口&#xff0c;可以配置 PHY 芯片的工…

读书笔记产品经理学习笔记1-忘掉技术,先看客户需求

技术到产品思维的转换 以前做技术的时候&#xff0c;扮演的角色是怎样多快好省的完成开发。现在做产品了&#xff0c;你得自己定产品方案&#xff0c;让别人来开发。最重要的是先弄清楚客户的需求是什么&#xff0c;要解决什么问题&#xff0c;再看产品怎么设计&#xff0c;然…

你想改win11系统中窗口、菜单等的字体? 微软不想让你改

如果你感觉win11系统中显示的字体不好看&#xff0c;想换。等一等&#xff0c;微软本不想让你改。 Windows 11 在默认情况下并没有提供直接修改系统默认字体的选项&#xff0c;而需要进行注册表或其他高级设置来更改系统字体。这可能是因为微软希望保持系统的一致性和稳定性&a…

[Realtek sdk-3.4.14b]RTL8197FH-VG+RTL8812F WiFi使用功率限制功能使用说明

sdk说明 ** Gateway/AP firmware v3.4.14b – Aug 26, 2019**  Wireless LAN driver changes as:  Refine WiFi Stability and Performance  Add 8812F MU-MIMO  Add 97G/8812F multiple mac-clone  Add 97G 2T3R antenna diversity  Fix 97G/8812F/8814B MP issu…

可狱可囚的爬虫系列课程 07:BeautifulSoup4(bs4)库的使用

前面一直在讲 Requests 模块如何使用&#xff0c;那都是在请求阶段要做的事情&#xff0c;相信很多网友都在等一个能够开始爬网站信息的教程&#xff0c;今天它来了&#xff0c;今天我要给大家讲一个很简单易懂的库&#xff1a;BeautifulSoup4。 一、概述&安装 Beautiful…

KylinV10 安装 MySQL 教程(可防踩雷)

KylinV10 安装 MySQL 教程&#xff08;可防踩雷&#xff09; 1、直接用 apt 快捷安装 MySQL $ sudo apt-get update #更新软件源 $ sudo apt-get install mysql-server #安装mysql然后你会发现&#xff0c;KylinV10 安装畅通无阻&#xff0c;并没有设置密码的场景&#xff0c…

你相信光吗?2D 后效与光照技术分享!

“ 很多朋友提到后效&#xff0c;就会想起那些 3D 游戏大作&#xff0c;但实际上&#xff0c;后效在 2D 游戏开发中的应用也是非常广泛的。恰当地使用后效&#xff0c;可以使一款 2D 游戏的画质提升好几个台阶。今天邀请到了社区大佬 wing&#xff0c;给大家分享一下 2D 后效框…

MyBatis ORM映射

MyBatis只能自动维护库表”列名“与”属性名“相同时的对应关系&#xff0c;二者不同时无法自动ORM 因此需要使用到ORM映射。 共有两种解决办法&#xff1a;1.列的别名 2.结果映射 1.列的别名 在SQL中使用 as 为查询字段添加列别名&#xff0c;以匹配属性名 public List<…

nodejs+vue+微信小程序+python+PHP高校成绩分析系统-计算机毕业设计推荐

综合购物商城管理经历和对网上信息归纳整理的结果&#xff0c;在实际应用中&#xff0c;将用户分为两种&#xff1a;管理员和高校成绩分析系统综合网络空间开发设计要求。目的是将高校成绩分析从传统管理方式转换为在网上管理&#xff0c;完成高校成绩分析管理的方便快捷、安全…

pyCharm 创建一个FastApi web项目,实现接口调用

FastApi和Django区别 我这边演示项目使用的fastApi作为web框架&#xff0c;当然主流一般都是使用Django做web框架&#xff0c;但是Django是一个重量级web框架他有很多组件&#xff0c;如授权&#xff0c;分流等全套web功能。我这边呢只需要有个接口可以被别人调用&#xff0c;…

【超详细前后端项目搭建】前端vue3+ts项目(引入ElementPlus、Axios)、后端springboot搭建(创建接口操作mysql数据库)实现前后端联调

目录 前言一、前端项目1、使用vue脚手架创建项目1.1检查vue版本1.2 使用vue脚手架创建项目 2、删除项目多余文件&#xff0c;修改配置项目2.1、删除以下文件2.1、在views下创建index文件2.2、修改router/index.ts路由文件&#xff1a;2.3、修改App.vue文件&#xff1a;2.4、初始…

气候变化与环境保护:全球研究与未来趋势

导言 气候变化和环境保护是当今社会亟待解决的全球性难题。本文将深入探讨这一主要流行研究方向的发展历程、遇到的问题、解决过程&#xff0c;以及未来的可用范围&#xff0c;着重分析在各国的应用和未来的研究趋势&#xff0c;以探讨在哪些方面能够取得胜利&#xff0c;以及在…

PySpark中DataFrame的join操作

内容导航 类别内容导航机器学习机器学习算法应用场景与评价指标机器学习算法—分类机器学习算法—回归机器学习算法—聚类机器学习算法—异常检测机器学习算法—时间序列数据可视化数据可视化—折线图数据可视化—箱线图数据可视化—柱状图数据可视化—饼图、环形图、雷达图统…

期货开平规则(期货交易开平规则解析)

什么是期货开平规则 期货开平规则&#xff0c;简单来说是指期货交易中的开仓和平仓所遵循的一系列规定。具体而言&#xff0c;开仓是指买入或卖出期货合约&#xff0c;建立一个新的持仓&#xff1b;平仓则是指买入或卖出相应数量的期货合约&#xff0c;用以解除原有持仓。开平…

什么是数据仪表板?数据可视化仪表盘怎么制作?

在数据经济时代&#xff0c;分析数据是每个企业做出最佳决策的关键。但是&#xff0c;手动分析和解释大量数据是不可行的。数据可视化对于分析数据中存在的各种有价值信息至关重要&#xff0c;包括可见趋势和隐藏趋势等。仪表盘显示可视化趋势和信息&#xff0c;例如 KPI、趋势…

简易实现 STL--list

实现 list 的主要思想及过程 首先&#xff0c;实现过程中的所有代码必须放在自己定义的命名空间中。 定义一个结点的结构体类模板&#xff0c;结点的数据类型就应该是模板类型 T&#xff0c;定义的 next指针和 prev指针都应该是模板指针类型&#xff0c;并且结构体类中药有构…