MFC消息映射BEGIN_MESSAGE_MAP详解

2024-01-21 12:40

本文主要是介绍MFC消息映射BEGIN_MESSAGE_MAP详解,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

         MFC的消息映射对于对开发者处理消息可谓十分方便。MFC类继承众多,虚函数表占内存大导致微软直接不采用虚函数方式。发现《VC++深入详解》只大概说了消息映射的原理,没有详细介绍其实现,所以写篇小文章探究下。

一、首先在使用消息映射之前,必须先声明DECLARE_MESSAGE_MAP()

DECLARE_MESSAGE_MAP()是个宏定义,对应的源码为:

#define DECLARE_MESSAGE_MAP()
private:
static const AFX_MSGMAP_ENTRY _messageEntries[];
protected:
static AFX_DATA const AFX_MSGMAP messageMap;
static const AFX_MSGMAP* PASCAL _GetBaseMessageMap();
virtual const AFX_MSGMAP* GetMessageMap() const;

声明添加了两个成员变量和两个成员函数:

_messageEntries: 是一个AFX_MSGMAP_ENTRY(定义了消息路由)类型数组,即路由表。​​​​​​(消息传递路径)​

struct AFX_MSGMAP_ENTRY
{UINT nMessage; //消息类型UINT nCode; // 控制码UINT nID; // 控件IDUINT nLastID; // 控件ID范围, 对于单控件消息处理,与nID相同UINT nSig; // 信号类型AFX_PMSG pfn; //回调函数,即处理函数(PMSG即Process Message)
};

messageMap: 路由信息,包含父类路由信息指针,和本类的路由表指针

struct AFX_MSGMAP
{const AFX_MSGMAP* pBaseMap; //指向父类的指针const AFX_MSGMAP_ENTRY* lpEntries; //本类路由表指针
};

二、接着我们查看BEGIN_MESSAGE_MAP在.cpp文件中的定义

BEGIN_MESSAGE_MAP(CMfc_testApp, CWinApp)
ON_COMMAND(ID_FILE_NEW, CWinApp::OnFileNew)
END_MESSAGE_MAP()

可以看到BEGIN_MESSAGE_MAP也是一个宏。然后上面的ON_COMMAND也是宏定义,全部展开后代码为:

const AFX_MSGMAP* theClass::GetMessageMap() const
{return &theClass::messageMap;
}const AFX_MSGMAP* PASCAL theClass::_GetBaseMessageMap()
{return &baseClass::messageMap;
}AFX_COMDAT AFX_DATADEF const AFX_MSGMAP theClass::messageMap =
{
&baseClass::messageMap, //基类路由信息指针
&theClass::_messageEntries[0] //本类路由表数组地址
};AFX_COMDAT const AFX_MSGMAP_ENTRY theClass::_messageEntries[] =
{
{WM_COMMAND, CN_COMMAND, (WORD)id, (WORD)id, AfxSig_vv, (AFX_PMSG)&memberFxn },
{0, 0, 0, 0, AfxSig_end, (AFX_PMSG)0 }
};

可以看到,通过宏定义和消息内嵌的方式,已经全部初始化消息路由相关的成员变量和方法,结构如下图所示:

每个消息映射入口的格式如下:

ON_Notification(id, memberFxn)

其中id指定了发送通知的控件的子窗口的ID,而memberFxn指定了处理该通知的父对象中的成员函数名

父对象的 函数原型格式如下:

afx_msg void memberFxn( );

可能的消息映射入口如下:

映射入口

何时向父对象发送消息

ON_BN_CLICKED

用户单击按钮时

ON_BN_DOUBLECLICKED

用户双击按钮时

三、然后消息循环:CWnd::OnWndMsg(位于WINCORE.cpp文件中)

if (message == pMsgCache->nMsg && pMessageMap == pMsgCache->pMessageMap)
{//处理在当前类的路由表和缓存命中
}
else
{// 当前类路由表和缓存找不到,pMsgCache->nMsg = message;pMsgCache->pMessageMap = pMessageMap;//通过pMessageMap = pMessageMap->pBaseMap递归往基类深入查找匹配for (; pMessageMap != NULL; pMessageMap = pMessageMap->pBaseMap){.....}.....
}

系统为每个窗体或控件都提供了默认的消息处理函数,如果在当前类中重写了消息处理函数,则采用当前消息处理函数,如果没有重写,则调用默认消息处理函数,最终保证消息得到处理。

这篇关于MFC消息映射BEGIN_MESSAGE_MAP详解的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/629580

相关文章

C++使用栈实现括号匹配的代码详解

《C++使用栈实现括号匹配的代码详解》在编程中,括号匹配是一个常见问题,尤其是在处理数学表达式、编译器解析等任务时,栈是一种非常适合处理此类问题的数据结构,能够精确地管理括号的匹配问题,本文将通过C+... 目录引言问题描述代码讲解代码解析栈的状态表示测试总结引言在编程中,括号匹配是一个常见问题,尤其是在

Debezium 与 Apache Kafka 的集成方式步骤详解

《Debezium与ApacheKafka的集成方式步骤详解》本文详细介绍了如何将Debezium与ApacheKafka集成,包括集成概述、步骤、注意事项等,通过KafkaConnect,D... 目录一、集成概述二、集成步骤1. 准备 Kafka 环境2. 配置 Kafka Connect3. 安装 D

Java中ArrayList和LinkedList有什么区别举例详解

《Java中ArrayList和LinkedList有什么区别举例详解》:本文主要介绍Java中ArrayList和LinkedList区别的相关资料,包括数据结构特性、核心操作性能、内存与GC影... 目录一、底层数据结构二、核心操作性能对比三、内存与 GC 影响四、扩容机制五、线程安全与并发方案六、工程

Spring Cloud LoadBalancer 负载均衡详解

《SpringCloudLoadBalancer负载均衡详解》本文介绍了如何在SpringCloud中使用SpringCloudLoadBalancer实现客户端负载均衡,并详细讲解了轮询策略和... 目录1. 在 idea 上运行多个服务2. 问题引入3. 负载均衡4. Spring Cloud Load

Springboot中分析SQL性能的两种方式详解

《Springboot中分析SQL性能的两种方式详解》文章介绍了SQL性能分析的两种方式:MyBatis-Plus性能分析插件和p6spy框架,MyBatis-Plus插件配置简单,适用于开发和测试环... 目录SQL性能分析的两种方式:功能介绍实现方式:实现步骤:SQL性能分析的两种方式:功能介绍记录

在 Spring Boot 中使用 @Autowired和 @Bean注解的示例详解

《在SpringBoot中使用@Autowired和@Bean注解的示例详解》本文通过一个示例演示了如何在SpringBoot中使用@Autowired和@Bean注解进行依赖注入和Bean... 目录在 Spring Boot 中使用 @Autowired 和 @Bean 注解示例背景1. 定义 Stud

如何通过Python实现一个消息队列

《如何通过Python实现一个消息队列》这篇文章主要为大家详细介绍了如何通过Python实现一个简单的消息队列,文中的示例代码讲解详细,感兴趣的小伙伴可以跟随小编一起学习一下... 目录如何通过 python 实现消息队列如何把 http 请求放在队列中执行1. 使用 queue.Queue 和 reque

如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解

《如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别详解》:本文主要介绍如何通过海康威视设备网络SDK进行Java二次开发摄像头车牌识别的相关资料,描述了如何使用海康威视设备网络SD... 目录前言开发流程问题和解决方案dll库加载不到的问题老旧版本sdk不兼容的问题关键实现流程总结前言作为

SQL 中多表查询的常见连接方式详解

《SQL中多表查询的常见连接方式详解》本文介绍SQL中多表查询的常见连接方式,包括内连接(INNERJOIN)、左连接(LEFTJOIN)、右连接(RIGHTJOIN)、全外连接(FULLOUTER... 目录一、连接类型图表(ASCII 形式)二、前置代码(创建示例表)三、连接方式代码示例1. 内连接(I

Go路由注册方法详解

《Go路由注册方法详解》Go语言中,http.NewServeMux()和http.HandleFunc()是两种不同的路由注册方式,前者创建独立的ServeMux实例,适合模块化和分层路由,灵活性高... 目录Go路由注册方法1. 路由注册的方式2. 路由器的独立性3. 灵活性4. 启动服务器的方式5.