打怪升级之MFC变量小实验

news2024/12/23 18:35:42

按惯例,这一篇文章主要还是作者读《深入浅出MFC》整理的一些笔记。不过本次还加上了一些作者自己的理解。

实验的前期准备

做实验前,你最好了解一下MFC的执行流程,从winapp到各类控件的实际变化过程,可以参考博主之前的笔记。

实验描述:我们需要在各窗口控件之间自由的把各种类型的变量传来传去,不是搞那些眼花缭乱的Windows宏定义(什么这个句柄那个句柄,这个指针那个指针的),而就搞三个简单的:char、int和char*。实际上这三个实体类型变量怎么在MFC各个类之间传来传去搞明白了,那些奇奇怪怪的各种函数用得上的Windows宏定义就都明白了。

实验的一些思想准备:
1.MFC的本质是一个封装好的类库,你在实际使用时是一个类一个类的用的。需要什么功能的时候就用什么类。比如你需要一个锤子🔨,那么你需要先声明一个锤子🔨(或者说召唤一个🔨),你懂的,声明你就一定要给这个🔨取个名字,你取好名字之后,这个锤子才能真正变成实物,你才能正确的调用锤子锤东西(调用锤子的锤功能:锤())。(如果你看过fate动漫的话,类就是卫宫大侠固有结界里面的剑,你在用的时候就是剑在现实世界的投影,当然,这样的剑是想要多少把就有多少把的)

2.RC文件是你画画的地方,是外表,C++是你在画好的东西实际运作的逻辑,是内在。好比RC文件是你画的一个人的脸,这张脸有眼睛有鼻子有嘴巴,而C++是眼睛嘴巴鼻子的实际功能,是支撑眼睛嘴巴鼻子正常工作的血管、肌肉、心跳这些你看不见的逻辑东西。一个窗口完全由RC文件、图片资源、RC编译器就能构成了。RC文件会告诉你每个地方的名字(一个ID)C语言的类会加载RC文件中得ID,并在message_map和resource.h中加载这个ID给类。RC文件的格式和写法你不用了解的太多,只需要重点关注一个变量就行了:ID。你也可以在你的项目文件中找到.rc文件来具体读一读,看看文件里的id是怎么用在各个地方的。

IDD_MFCLAB_DIALOG DIALOGEX 0, 0, 322, 189
STYLE DS_SETFONT | DS_FIXEDSYS | WS_POPUP | WS_VISIBLE | WS_CAPTION | WS_SYSMENU | WS_THICKFRAME
EXSTYLE WS_EX_APPWINDOW
FONT 9, "MS Shell Dlg", 0, 0, 0x1
BEGIN
    DEFPUSHBUTTON   "确定",IDOK,211,168,50,14
    PUSHBUTTON      "取消",IDCANCEL,265,168,50,14
    LTEXT           "启动",IDC_TEXT,28,71,38,17
    PUSHBUTTON      "启动",made,22,106,50,14
    PUSHBUTTON      "复原",IDC_BUTTON2,22,126,50,14
END

从这里可以看到,不同的组件类型,所在位置,ID名称,隶属于哪个主面板,都在.rc文件中清清楚楚的写明白了。笔者建议你,最好把画画和实现功能看成两件事,分别交给RC文件和C去做,让画画的专心画画,让回调函数的专心回调。

3.在MFC的众多类中,比较重要的是CWinApp和CWnd这两个类,这两个类一个初始化程序,一个给定传统意义上的回调函数,是最常用到的两个类(不论是哪个类,你都可以在“…\VS\IDE\VC\Tools\MSVC\14.34.31933\atlmfc\src\mfc”路径下去找,去搜索)。CWinApp这个类在MFC自身执行WinMain函数的时候会调用,你只需把CWinApp的InitInstance()写好就行了。其中注意m_pMainWnd这个变量是属于CWinApp类的一个全局变量,比较特殊。

这个m_pMainWnd变量是CWnd*类型的,这个全局变量指向哪个具体的CWnd,MFC就把哪个CWnd当成主界面,优先打开这个界面。主CWnd窗口确定后,你还需要把RC文件中的ID与CWnd里的各种类、变量进行关联(message map和resource里面会有)。

关联的过程中,你只用管两件事:RC文件的某个地方具体需要一个什么样的CWnd类(根据你需要的CWnd函数来的)、在属于这个地方的RC消息函数中,哪个消息函数应该有什么响应。具体的不同类的不同功能可以去这里找。

4.消息体简述,如果你从消息函数映射、消息体的执行角度去理清所有的过程的话,一级一级的宏展开太不好了,真的很难读(单如果你能读懂,也会有很多启发,知道原来封装类是这么干的)。有一个简单的办法可以跳过这些繁琐的步骤,这个办法需要你有一点点灵动的直觉。消息者,事物之变化也。你看着电脑屏幕,敏锐的觉察到哪里有变化,哪里就一定有消息。

哲学的说,操作系统中给了你很多描述操作系统状态的变量,这些变量跟你的实际系统状态一一对应,实际上构成了对应操作系统的状态变量空间(类似于热力学中的相空间),你把两个不同的操作系统状态做对比,哪个系统状态量不一样(也就是描述两个操作系统的变量在相空间中分别属于两个点),我们就可以把这个不一样的变化看作一个消息。你就可以用此消息做一个消息体,也可以相应的写这个消息体的回调函数。

消息者,事物之变化也,Windows的系统变量空间是固定的,就那么多。如果你敏锐的发现系统发生了一点变化,那么对应的,系统的变量状态空间也一定会有所变化,所以说,只要你能找到的相空间中不同的点,点到点之间的过程,都可以作为消息体。

举例来说就是:鼠标点击物件可以作为消息体,鼠标点击物件两次,也可以作为消息体(时间与鼠标点击次数自然是Windows系统变量空间的一部分,且这两个事件对应了状态空间中不同的两个点)。
在这里插入图片描述

5.消息体与回调函数的绑定是在message_map中完成的,具体来说是在message_map一系列眼花缭乱宏展开之中最核心的AFX_MSGMAP_ENTRY _messageEntries[]数组内定义的,如果你不清楚这个数组是什么东西,你可以去看我之前讲message_map的博客。

这里简单的描述一下,MFC有一个自动机,会顺序地读取_messageEntries[]数组内的所有绑定起来的元素,并将每个你绑定好消息体和回调函数的_messageEntries结构体放在传统的WndProc函数中执行,从而达到消息体与回调效应绑定的作用。

如果你还搞不清楚,你可以这样理解:回调函数是一定要你自己写的函数,在此决定了触发后的响应。从4可知,你只要确定一个消息,你就可以让系统在监听到这个消息的时候做出一点事情,做的事情就放在你写的回调函数中,也是由操作系统帮你去做,你只需要在message_map里把消息和回调函数绑定就可以了,系统会明白的。

6.RC中的各类组件只是给了你一个框架,你还需要往这个框架里注入灵魂,也就是你的RC中声明的各类控件ID都需要给他们配备对应的CWnd类。你注入的CWnd类也需要取一个变量名称,如果你注入的类需要执行具体的功能(也就是执行类自带的跟操作系统交互的函数),那么你在定义CWnd类时要声明这是一个control变量(也就是可以调用系统交接函数的投影出来的🔨),否则,如果你只是想给某个类添加一个方便你使用的数据,那么你可以定义一个值变量(就是定义一个int、char、或者系统宏CString之类的不会跟系统交互的变量)。

给RC控件ID注入实际的CWnd类,有助于你彻底确定这一类可能跟哪些种类的消息体发生关系,在这一CWnd类中,你可以从各种可能的消息体(比如单击、双击、或者长按)挑出一个你想用的,作为回调函数的触发条件,前往message_map绑定即可。

回调函数内部,多半也是在写你给出的实例类变量中的各种功能函数。

实验的内容

本次实验使用两个具体组件:static_text、button。要求是在按下button时static_text显示我们准备好的字符串,并且在按下不同的button时,让一个变量可以在不同的组件之间传递。

第一步:在RC编辑器中画画

在这里插入图片描述

在你给定的编辑器中,添加两个static_text部件和三个button部件,更改他们的描述性字符串,更改他们的控件ID(ID和描述字符串不一样的)。

第二步:为RC文件中的各类控件ID添加实例化的CWnd子类

打开ClassWizard(右键菜单中的类向导),在成员变量中添加各变量。

在这里插入图片描述

如上图所示,最左边的是RC画画中画出来的控件ID,你可以在此处针对不同的控件ID赋予不同的CWnd子类(投影🔨),你也可以针对不同的控件做本地化的变量。变量在头文件中声明,并在DoDataExchange()函数中绑定,详见如下代码:

//在头文件中新加类变量的定义:
// 构造
public:
	CMFCLabDlg(CWnd* pParent = nullptr);	// 标准构造函数
	virtual ~CMFCLabDlg();

// 对话框数据
#ifdef AFX_DESIGN_TIME
	enum { IDD = IDD_MFCLAB_DIALOG };
#endif

	protected:
	virtual void DoDataExchange(CDataExchange* pDX);	// DDX/DDV 支持


// 实现
protected:
	CMFCLabDlgAutoProxy* m_pAutoProxy;
	HICON m_hIcon;

	BOOL CanExit();

	// 生成的消息映射函数
	virtual BOOL OnInitDialog();
	afx_msg void OnSysCommand(UINT nID, LPARAM lParam);
	afx_msg void OnPaint();
	afx_msg HCURSOR OnQueryDragIcon();
	afx_msg void OnClose();
	DECLARE_MESSAGE_MAP()
public:
	CButton class_R;
	CStatic text_L;
	CStatic text_R;
	CButton class_A;
	CButton class_B;
	afx_msg void OnBnClickedA();
	afx_msg void OnBnClickedB();
	afx_msg void OnBnClickedgotor();
};
//将新添加的类变量添加到主对话框中去

上述头文件中定义了新添加的类变量,并在下面函数中进行绑定:

//这个方法一般写在主对话框的cpp文件中
void CMFCLabDlg::DoDataExchange(CDataExchange* pDX)
{
	CDialogEx::DoDataExchange(pDX);
	DDX_Control(pDX, gotoR, class_R);
	DDX_Control(pDX, Left, text_L);
	DDX_Control(pDX, Right, text_R);
	DDX_Control(pDX, set_A, class_A);
	DDX_Control(pDX, set_B, class_B);
}

第三步:挑选你认为合适的消息体作为触发器,写对应的回调函数并在message_map中绑定

你可以在Classwizard中找到不同CWnd子类常用的消息体(也并不是就只能用这些了,CWnd子类你可以自己写的):

在这里插入图片描述

根据你实际的控件ID种类的不同,Classwizard已经把常见的消息体列出来了,比如针对gotoR这个按钮而言,BN_CLIKED、BCN_DROPDOWN、BCN_HOTITEMCHANGE、BN_DOUBLECLICKED、BN_KILLFOCUS这些东西都是某个消息体的宏名。

下面是各类宏的溯源记录:

//MESSAGE_MAP
BEGIN_MESSAGE_MAP(CMFCLabDlg, CDialogEx)
	ON_WM_SYSCOMMAND()
	ON_WM_CLOSE()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_BN_CLICKED(set_A, &CMFCLabDlg::OnBnClickedA)
	ON_BN_CLICKED(set_B, &CMFCLabDlg::OnBnClickedB)
	ON_BN_CLICKED(gotoR, &CMFCLabDlg::OnBnClickedgotor)
END_MESSAGE_MAP()

// User Button Notification Codes
#define ON_BN_CLICKED(id, memberFxn) \
	ON_CONTROL(BN_CLICKED, id, memberFxn)
#define ON_BN_DOUBLECLICKED(id, memberFxn) \
	ON_CONTROL(BN_DOUBLECLICKED, id, memberFxn)
#define ON_BN_SETFOCUS(id, memberFxn) \
	ON_CONTROL(BN_SETFOCUS, id, memberFxn)
#define ON_BN_KILLFOCUS(id, memberFxn) \
	ON_CONTROL(BN_KILLFOCUS, id, memberFxn)

// for general controls
#define ON_CONTROL(wNotifyCode, id, memberFxn) \
	{ WM_COMMAND, (WORD)wNotifyCode, (WORD)id, (WORD)id, AfxSigCmd_v, \
		(static_cast< AFX_PMSG > (memberFxn)) },

#define WM_COMMAND                      0x0111

可以看到,从message_map中绑定的消息体宏名可以一路展开到具体的消息体地址、消息体id、消息体实际AfxSigCmd_v函数。

第四步:在回调函数中写你认为需要执行的操作

由于窗口的各类物件都是实在的类对象,所以我们可以用类对象本身的函数实现特定的功能,比如让类对象本身的数值变化。

我们在三个按钮的回调函数中做如下动作:

void CMFCLabDlg::OnBnClickedA()
{
	// TODO: 在此添加控件通知处理程序代码
	text_L.SetWindowText(_T("A"));
}


void CMFCLabDlg::OnBnClickedB()
{
	// TODO: 在此添加控件通知处理程序代码
	text_L.SetWindowText(_T("B"));
}


void CMFCLabDlg::OnBnClickedgotor()
{
	// TODO: 在此添加控件通知处理程序代码
	CString buff;
	text_L.GetWindowText(buff);
	text_R.SetWindowText(buff);
}

实际效果为:

在这里插入图片描述

点A按钮,左边变A,点B按钮,左边变B,点gotoR按钮,右边与左边保持一致。

至此实验结束。

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

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

相关文章

SOTA!目标检测开源框架YOLOv6 3.0版本来啦

近日&#xff0c;美团视觉智能部发布了 YOLOv6 3.0 版本&#xff0c;再一次将目标检测的综合性能推向新高。YOLOv6-L6 检测精度和速度超越 YOLOv7-E6E&#xff0c;取得当前实时目标检测榜单 SOTA。本文主要介绍了 YOLOv6 3.0 版本中引入的技术创新和优化&#xff0c;希望能为从…

64. 最小路径和

64. 最小路径和 给定一个包含非负整数的 m∗nm * nm∗n 网格 grid &#xff0c;请找出一条从左上角到右下角的路径&#xff0c;使得路径上的数字总和为最小。 说明&#xff1a;每次只能向下或者向右移动一步。 示例 1&#xff1a; 输入&#xff1a;grid [[1,3,1],[1,5,1],[…

Hudi的7种索引

1、Bloom Index Bloom Index (default) 使用根据记录键构建的bloom过滤器&#xff0c;也可以使用记录键范围修剪候选文件.原理为计算RecordKey的hash值然后将其存储到bitmap中&#xff0c;为避免hash冲突一般选择计算3次 HoodieKey 主键信息&#xff1a;主要包含recordKey 和p…

[ IFRS 17 ] 新准则下如何确认保险合同

本系列文章&#xff1a;[ IFRS 17 ] 针对保险新准则 IFRS 17 进行一些列文章更新。如若文中如有所疑问或者不同见解&#xff0c;欢迎留言互动交流。 注&#xff1a;本系列文章受众群体较小众&#xff0c;如若对你感到不适&#xff0c;请立刻点击右上角的 【】 本系列文章适用…

RDD持久化原理和共享变量

&#xff08;一&#xff09; RDD持久化原理 Spark中有一个非常重要的功能就是可以对RDD进行持久化。当对RDD执行持久化操作时&#xff0c;每个节点都会将自己操作的RDD的partition数据持久化到内存中&#xff0c;并且在之后对该RDD的反复使用中&#xff0c;直接使用内存中缓存…

互联网工程师 1480 道 Java 面试题及答案整理 ( 2023 年 整理版)

最近很多粉丝朋友私信我说&#xff1a;熬过了去年的寒冬却没熬过现在的内卷&#xff1b;打开 Boss 直拒一排已读不回&#xff0c;回的基本都是外包&#xff0c;薪资还给的不高&#xff0c;对技术水平要求也远超从前&#xff1b;感觉 Java 一个初中级岗位有上千人同时竞争&#…

安卓逆向_4 --- 定位关键Smali、monitor使用、log插桩、栈追踪、methodprofiling(方法分析)

1、快速定位关键 smali 代码 1.分析流程 搜索特征字符串 搜索关键api 通过方法名来判断方法的功能 2.快速定位关键代码 反编译 APK 程序&#xff0c;AndroidManifest.xml > 包名/系统版本/组件 程序的主 activity(程序入口界面) 每个…

Allegro如何画半圆形的线操作指导

Allegro如何画半圆形的线操作指导 在用Allegro设计PCB的时候,在某些应用场合会需要画半圆形,如下图 如何画半圆形,具体操作如下 点击Add点击Arc w/Radius

WebRTC QoS方法之Pacer实现

本文将解读WebRTC中Pacer算法的实现。WebRTC有两套Pacer算法&#xff1a;TaskQueuePacedSender、PacedSender。本文仅介绍PacedSender的实现。&#xff08;文章中引用的WebRTC代码基于master&#xff0c;commit&#xff1a;3f412945f05ce1ac372a7dad77d85498d23deaae源码分析&a…

算法练习(八)区域搜索

一、腐烂的橘子 1、题目描述&#xff1a; 在给定的 m x n 网格 grid 中&#xff0c;每个单元格可以有以下三个值之一&#xff1a; 值 0 代表空单元格&#xff1b; 值 1 代表新鲜橘子&#xff1b; 值 2 代表腐烂的橘子。 每分钟&#xff0c;腐烂的橘子 周围 4 个方向上相邻 的…

从零开始:学习使用 Hugo 构建自己的静态网站

1、什么是 Hugo 1.1、简介 Hugo 是一个由 Go 语言编写的静态网站生成器。它可以帮助用户快速构建高性能的静态网站&#xff0c;特别是博客、文档和个人网站等。与其他静态网站生成器相比&#xff0c;Hugo 的特点是速度快、易于使用、可扩展性强等。Hugo 使用简单的 Markdown …

【项目】游戏-我在万科转生成了一只狗

文章目录学习unity一些基操..位置坐标系父子关系常用工具导入游戏模型资源商店创建地形为地形化妆--纹理绘制脚本组件脚本的生命周期脚本执行顺序标签和图层的作用向量的运算和意义欧拉角和四元数-常用C#预制体-类与对象Debug的使用C#物体属性使用游戏时间使用-C#计时器的设置路…

无需手动编码的XGBoost中的分类特征

无需手动编码的XGBoost中的分类特征 XGBoost 是一种基于梯度提升的基于决策树的集成机器学习算法。 然而&#xff0c;直到最近&#xff0c;它还没有原生支持分类数据。 在将分类特征用于训练或推理之前&#xff0c;必须对其进行手动编码。 在序数类别的情况下&#xff0c;例如…

视觉SLAM十四讲ch4 李群和李代数笔记

视觉SLAM十四讲ch4 李群和李代数视觉SLAM十四讲ch4 李群和李代数李群和李代数基础指数映射与对数映射李代数求导与扰动模型视觉SLAM十四讲ch4 李群和李代数 李群和李代数基础 可以将SO3看成旋转矩阵集合&#xff0c;SE3看成变换矩阵集合 李代数是6个自由度的向量空间…

qsort函数的应用以及模拟实现

前言 &#x1f388;个人主页:&#x1f388; :✨✨✨初阶牛✨✨✨ &#x1f43b;推荐专栏: &#x1f354;&#x1f35f;&#x1f32f; c语言进阶 &#x1f511;个人信条: &#x1f335;知行合一 &#x1f349;本篇简介:>:介绍库函数qsort函数的模拟实现和应用 金句分享: ✨追…

Docker之部署Mysql

通过docker对Mysql进行部署。 如果没有部署过docker&#xff0c;看我之前写的目录拉取镜像运行容器开放端口拉取镜像 前往dockerHub官网地址&#xff0c;搜索mysql。 找到要拉取的镜像版本&#xff0c;在tag下找到版本。 拉取mysql镜像&#xff0c;不指定版本数&#xff0c…

04 Android基础--RelativeLayout

04 Android基础--RelativeLayout什么是RelativeLayout&#xff1f;RelativeLayout的常见用法&#xff1a;什么是RelativeLayout&#xff1f; 相对布局&#xff08;RelativeLayout&#xff09;是一种根据父容器和兄弟控件作为参照来确定控件位置的布局方式。 根据父容器定位 在相…

JavaWeb--RequestResponse

Request&Response1 Request和Response的概述2 Request对象2.1 Request继承体系2.2 Request获取请求数据2.2.1 获取请求行数据2.2.2 获取请求头数据2.2.3 获取请求体数据2.2.4 小结2.2.5 获取请求参数的通用方式2.3 IDEA快速创建Servlet2.4 请求参数中文乱码问题2.4.1 POST请…

智能家居Homekit系列一智能通断开关

智能通断器&#xff0c;也叫开关模块&#xff0c;可以非常方便地接入家中原有开关、插座、灯具、电器的线路中&#xff0c;通过手机App或者语音即可控制电路通断&#xff0c;轻松实现原有家居设备的智能化改造。 随着智能家居概念的普及&#xff0c;越来越多的人想将自己的家改…

WebRTC系列分享 | WebRTC视频QoS全局技术栈

概述目前总结出WebRTC用于提升QoS的方法有&#xff1a;NACK、FEC、SVC、JitterBuffer、IDR Request、Pacer、Sender Side BWE、Probe、VFR&#xff08;动态帧率调整策略&#xff09;、AVSync&#xff08;音视频同步&#xff09;、动态分辨率调整。这几种方法在WebRTC架构分布如…