Visual Studio 2022的MFC框架——theApp全局对象

我是荔园微风,作为一名在IT界整整25年的老兵,今天我们来重新审视一下Visual Studio 2022下开发工具的MFC框架知识。
MFC中的WinMain函数是如何与MFC程序中的各个类组织在一起的呢?MFC程序中的类是如何与WinMain函数关联起来的呢?
面对这个问题,我们来分析一下。
双击我在我的《Visual Studio 2022的MFC框架——应用程序向导》一文中的项目例子中的类视图窗口中的CMfcApp类,跳转到该类的定义文件(Mfc.h)中。可以发现CMfcApp派生于 CWinApp类,后者表示应用程序类。
我们在类视图窗口中双击该类的构造函数,就跳转到该类的源文件(Mfc.cpp)中。在CMfcApp构造函数处设置一个断点,然后调试运行Mfc程序,将发现程序首先停在CMfcApp类的构造函数处,继续运行该程序。
这时程序才进入WinMain函数,即停在先前我在我的《Visual Studio 2022的MFC框架——WinMain函数》一文中的WinMain函数中设置的断点处。
按我们过去在C/C++编程的理解中,WinMain函数是程序的入口函数。也就是说,程序运行时首先应该调用的是WinMain函数,那么为什么这里程序会首先调用CMfcApp类的构造函数呢?
看一下CMfcApp的源文件,可以发现在程序中定义了一个CMfcApp类型的全局对象:theApp。代码如下。
//唯一的 CMfcApp对象CMfcApp theApp;
MFC程序的全局变量都放置在类视图窗口中的“全局函数和变量”分支下,单击该分支即可看到程序当前所有的全局函数和变量。双击某个全局变量,即可定位到该变量的定义处。
在这个全局对象定义处设置一个断点,然后调试运行Mfc程序,将发现程序执行的顺序依次是:
1.theApp全局对象定义处
2.MfcApp构造函数
3.WinMain函数。
为了更好地解释这一过程,我们在项目解决方案下,添加一个新的“Windows控制台应用程序”项目,该项目的名称为:findwm。
接下来,在findwm.cpp文件中输入如下所示的代码。
#include "pch.h"
#include <iostream>using namespace std; int s=100;int main(){cout<<s<<endl;return 0;}
上述代码首先定义了一个int类型的全局变量s, 并给它赋了一个初值100。然后在main函数中将全局变量s的值输出到标准输出cout上。
将该项目设置为启动项目, 然后在main函数处设置一个断点, 调试运行该程序, 将会发现程序在进入main函数时, s的值已经是100了。在程序入口main函数加载时,系统就已经为全局变量或全局对象分配了存储空间,并为它们赋了初始值。
接下来,把全局变量s换成一个全局对象,看看结果如何。修改代码, 新定义一个CPoint类, 并定义该类的一个全局变量pt。
#include"pch.h"
#include<iostream>using namespace std;class CPoint
{
public:CPoint(){}
};CPoint pt;
void main()
{}
设置三个断点:CPoint构造函数处、pt全局对象定义处和 main函数定义处。选择调试运行 main函数,将会看到程序代码执行的先后顺序。
这时我们将发现findwm程序首先到达pt全局对象定义处;继续运行程序,程序到达CPoint类的构造函数;再继续运行程序,程序到达main函数处。
由此可见,无论是全局变量,还是全局对象,程序在运行时,在加载main函数之前,就已经为全局变量或全局对象分配了内存空间。
对一个全局对象来说,此时就会调用该对象的构造函数构造该对象,并进行初始化操作。
这解释了先前创建的Mfc程序的运行顺序为什么全局变量theApp的构造函数会在 WinMain 函数之前执行。
那么,为什么要定义一个全局对象theApp,让它在WinMain函数之前执行呢?该对象的作用是什么呢?我们回到Mfc项目,并将该项目设置为启动项目。
Win32 SDK应用程序的实例是由实例句柄(WinMain函数的参数hInstance)来标识的。而对MFC程序来说,通过产生一个应用程序类的对象来唯一标识应用程序的实例。每一个MFC程序有且仅有一个从应用程序类(CWinApp)派生的类。每一个MFC程序实例有且仅有一个该派生类的实例化对象,也就是theApp全局对象。该对象就表示了应用程序本身。
还记得子类构造函数的执行过程?当一个子类在构造之前会先调用其父类的构造函数。因此theApp对象的构造函数CMfcApp 在调用之前,会调用其父类CWinApp的构造函数,从而就把我们程序自己创建的类与Microsoft 提供的基类关联起来了。CWinApp的构造函数完成程序运行时的一些初始化工作。
下面让我们看看CWinApp类构造函数的定义。像前面搜索“WinMain”函数那样,找到Microsoft提供的CWinApp类定义的源文件:appcore.cpp,并在编辑环境中打开,其中CWinApp构造函数的代码如下。
CWinApp::CWinApp(LPCTSTR lpszAppName)
{if (lpszAppName != NULL)m_pszAppName=_tcsdup(lpszAppName);elsem_pszAppName =NULL;// initialize CWinThread stateAFX_MODULE_STATE* pModulestate =_AFX_CMDTARGET_GETSTATE();ENSURE (pModulestate);AFX_MODULE_THREAD_STATE* pThreadstate =pModulestate->m_thread;ENSURE(pThreadState);ASSERT(AfxGetThread()== NULL);pThreadstate->m_pCurrentwinThread=this;ASSERT (AfxGetThread() == this);m_hThread = ::GetCurrentThread();m_nThreadID=::GetCurrentThreadId();// initialize CWinApp stateASSERT(afxCurrentWinApp == NULL);pModuleState->m_pCurrentWinApp this;ASSERT (AfxGetApp ()== this);// in non-running state until WinMainm_hInstance= NULL;m_hLangResourceDLL= NULL;m_pszHelpFilePath= NULL;m_pszProfileName= NULL;m_pszRegistryKey= NULL;m_pszExeName= NULL;m_pszAppID= NULL;m_pRecentFileList= NULL;m_pDocManager= NULL;m_atomApp= m_atomSystemTopic= NULL;m_lpCmdLine= NULL;m_pCmdInfo= NULL;m_pDataRecoveryHandler= NULL;// initialize wait cursor statem_nWaitCursorCount =0;m_hcurWaitCursorRestore= NULL;// initialize current printer statem_hDevMode= NULL;m_hDevNames= NULL;m_nNumPreviewPages =0;// initialize DAO statem_lpfnDaoTerm= NULL;// other initializationm_bHelpMode= FALSE;m_eHelpType= afxwinHelp;m_nSafetyPoolsize= 512;m_dwRestartManagerSupportFlags =0;m_nAutosaveInterval = 5 * 60 * 1000;m_bTaskbarInteractionEnabled= TRUE;// Detect the kind of OS:OSVERSIONINFO osvi;osvi.dwosversionInfoSize= sizeof (OSVERSIONINFO);#pragma warning( disable 4996)::GetVersionEx(&osvi);#pragma warning( default 4996)m_bIsWindows7 =(osvi. dwMajorVersion ==6) && (osvi. dwMinorVersion >=1)|| (osvi. dwMajorVersion >6);// Taskbar initialization:m_bComInitialized= FALSE;m_pTaskbarList= NULL;m_pTaskbarList3= NULL;m_bTaskBarInterfacesAvailable= TRUE;
}
上述CWinApp的构造函数中有这样两行代码:
pThreadState->m_pCurrentWinThread= this;pModuleState->m_pCurrentWinApp= this;
m_pCurrentWinThread 对象的类型是 CWinThread,该类是 CWinApp 的父类。
根据C++继承性原理,这个this对象代表的是子类 CMfcApp的对象,即theApp。同时,可以发现CWinApp的构造函数有一个LPCTSTR类型的形参:lpszAppName。但是我们程序中CMfcApp的构造函数是没有参数的。如果基类的构造函数带有一个形参,那么子类构造函数需要显式地调用基类带参数的构造函数。那么,为什么我们程序中的 CMfcapp构造函数没有这么做呢?
如果某个函数的参数有默认值,那么在调用该函数时可以传递该参数的值,也可以不传递直接使用默认值即可。
class CWinApp : public CWinThread
{DECLARE DYNAMIC (CWinApp)public://Constructorexplicit CWinApp(LPCTSTR lpszAppName=NULL);.....
可以看到,CWinApp构造函数的形参确实有一个默认值(NULL)。这样,在调用CWinApp类的构造函数时,就不用显式地去传递这个参数的值。
作者简介:荔园微风,1981年生,高级工程师,浙大工学硕士,软件工程项目主管,做过程序员、软件设计师、系统架构师,早期的Windows程序员,Visual Studio忠实用户,C/C++使用者,是一位在计算机界学习、拼搏、奋斗了25年的老将,经历了UNIX时代、桌面WIN32时代、Web应用时代、云计算时代、手机安卓时代、大数据时代、ICT时代、AI深度学习时代、智能机器时代,我不知道未来还会有什么时代,只记得这一路走来,充满着艰辛与收获,愿同大家一起走下去,充满希望的走下去。
相关文章:
Visual Studio 2022的MFC框架——theApp全局对象
我是荔园微风,作为一名在IT界整整25年的老兵,今天我们来重新审视一下Visual Studio 2022下开发工具的MFC框架知识。 MFC中的WinMain函数是如何与MFC程序中的各个类组织在一起的呢?MFC程序中的类是如何与WinMain函数关联起来的呢?…...
SpringBoot Cache
一、基本概念 Spring Cache 是一个框架,实现了基于注解的缓存功能,只需要简单地加一个注解,就能实现缓存功能。 Spring Cache 提供了一层抽象,底层可以切换不同的缓存实现,例如: • EHCache • Caffeine …...
vue 简单实验 自定义组件 component
1.代码 <script src"https://unpkg.com/vuenext" rel"external nofollow" ></script> <div id"components-demo"><button-counter></button-counter> </div> <script> // 创建一个Vue 应用 const ap…...
C++ 改善程序的具体做法 学习笔记
1、尽量用const enum inline替换#define 因为#define是做预处理操作,编译器从未看见该常量,编译器刚开始编译,它就被预处理器移走了,而#define的本质就是做替换,它可能从来未进入记号表 解决方法是用常量替换宏 语言…...
Unity 之 GameObject.Find()在场景中查找指定名称的游戏对象
文章目录 GameObject.Find 是 Unity 中的一个函数,用于在场景中查找指定名称的游戏对象。这个函数的主要作用是根据游戏对象的名称来查找并返回一个引用,使您能够在代码中操作该对象。以下是有关 GameObject.Find 的详细介绍: 函数签名&…...
flink on yarn with kerberos 边缘提交
flink on yarn 带kerberos 远程提交 实现 flink kerberos 配置 先使用ugi进行一次认证正常提交 import com.google.common.io.Files; import lombok.extern.slf4j.Slf4j; import org.apache.commons.io.FileUtils; import org.apache.flink.client.cli.CliFrontend; import o…...
NodeJS的简介以及下载和安装
本章节会带大家下载并安装NodeJs 以及简单的入门,配有超详细的图片,一步步带大家进行下载与安装 NodeJs简介关于前端与后端Node是什么?为什么要学习NodeNodeJS的优点: NodeJS的下载与安装NodeJS的下载: NodeJS的快速入…...
量化面试-概率题
文章目录 一、题目1.糖果罐(绿皮书79页)2 折木棍(绿皮书89页)3 第一张ACE(绿皮书95页)4 n个均匀分布之和(绿皮书95页) 二、答案1. 糖果罐2 折木棍3 第一张ACE4 n个均匀分布之和 一、…...
【spark】java类在spark中的传递,scala object在spark中的传递
记录一个比较典型的问题,先讲一下背景,有这么一个用java写的类 public class JavaClass0 implements Serializable {private static String name;public static JavaClass0 getName(String str) {if (name null) {namestr;}return name;}... }然后在sp…...
php 文字生成图片保存到本地
你可以使用PHP的GD库来生成图片并保存到本地。首先,你需要确保你的PHP环境已经安装了GD库。然后,你可以使用GD库的函数来创建一个画布,并在上面绘制文字。最后,使用imagepng或imagejpeg函数将画布保存为PNG或JPEG格式的图片文件。…...
面试手撕—二叉搜索树及其后序遍历
一、引言 在面试地平线的时候,聊到了二叉搜索树,让手撕二叉搜索树,以下是要求 1、用类模板实现二叉搜索树 2、写一个函数,实现给一个vector数组,转换成二叉搜索树 3、写出二叉搜索树的后序遍历 二、代码实现 #inc…...
Java数据结构面试题以及答案
本专栏记录Java后端开发相关的面试题,欢迎大家阅读专栏的其他文章。 目录 1.B树和B树的区别?B树和B树的优点分别是? 2.排序算法的种类和复杂度 3.HashMap和Hashtable的原理、区别、应用场景 4.ConcurrentHashMap的原理、应用场景 5.Arra…...
Java——它要求用户输入一个整数(实际上是一个字符串),然后计算该整数的平方值,并将结果输出。
这是一个Java程序,它要求用户输入一个整数(实际上是一个字符串),然后计算该整数的平方值,并将结果输出。程序的基本流程如下: 首先,声明并初始化变量data和result,它们的初始值都为…...
【科研论文配图绘制】task6直方图绘制
【科研论文配图绘制】task6直方图绘制 task6 主要掌握直方图的绘制技巧,了解直方图含义,清楚统计指标的添加方式 1.直方图 直方图是一种用于表示数据分布和离散情况的统计图形,它的外观和柱形图相近,但它所 表达的含义和柱形图…...
Leetcode刷题:395. 至少有 K 个重复字符的最长子串、823. 带因子的二叉树
Leetcode刷题:395. 至少有 K 个重复字符的最长子串、823. 带因子的二叉树 1. 395. 至少有 K 个重复字符的最长子串算法思路参考代码和运行结果 2. 823. 带因子的二叉树算法思路参考代码和运行结果 1. 395. 至少有 K 个重复字符的最长子串 题目难度:中等 标签&#…...
java八股文面试[多线程]——Synchronized的底层实现原理
笔试:画出Synchronized 线程状态流转实现原理图 synchronized关键字解决的是多个线程之间访问资源的同步性,synchronized 翻译为中文的意思是同步,也称之为”同步锁“。 synchronized的作用是保证在同一时刻, 被修饰的代码块或方…...
C#,《小白学程序》第三课:类、类数组与排序
类class把数值与功能巧妙的进行了结合,是编程技术的主要进步。 下面的程序你可以确立 分数 与 姓名 之间关系,并排序。 1 文本格式 /// <summary> /// 同学信息类 /// </summary> public class Classmate { /// <summary> /…...
史上最全AP、mAP详解与代码实现
文章目录 前言一、mAP原理1、mAP概念2、准确率3、精确率4、召回率5、AP: Average Precision 二、mAP0.5与mAP0.5:0.951、mAP0.52、mAP0.5:0.95 三、mAP代码实现1、真实标签json文件格式2、模型预测标签json文件格式3、mAP代码实现4、mAP结果显示 四、模型集成mAP代码1、模型mai…...
百数应用中心——生产制造管理解决方案解决行业难题
传统生产制造业面临着许多挑战,其中一些主要问题包括效率低下、交期压力大、需求预测不准确、生产模式复杂、异常响应慢、库存高和计划脱节等。这些问题不仅影响了生产效率和质量,也导致了不必要的成本和客户满意度下降。 生产制造管理应用对于企业的生产…...
《存储IO路径》专题:IO虚拟化初探
大家好,欢迎来到今天的科技小课堂。今天我们要聊聊的是一项非常有趣且实用的技术——I/O虚拟化(Input/Output Virtualization,简称IOV)。想象一下,如果把物理硬件资源比作一道丰盛的大餐,那么IOV就是那位神…...
生成xcframework
打包 XCFramework 的方法 XCFramework 是苹果推出的一种多平台二进制分发格式,可以包含多个架构和平台的代码。打包 XCFramework 通常用于分发库或框架。 使用 Xcode 命令行工具打包 通过 xcodebuild 命令可以打包 XCFramework。确保项目已经配置好需要支持的平台…...
【WiFi帧结构】
文章目录 帧结构MAC头部管理帧 帧结构 Wi-Fi的帧分为三部分组成:MAC头部frame bodyFCS,其中MAC是固定格式的,frame body是可变长度。 MAC头部有frame control,duration,address1,address2,addre…...
Oracle查询表空间大小
1 查询数据库中所有的表空间以及表空间所占空间的大小 SELECTtablespace_name,sum( bytes ) / 1024 / 1024 FROMdba_data_files GROUP BYtablespace_name; 2 Oracle查询表空间大小及每个表所占空间的大小 SELECTtablespace_name,file_id,file_name,round( bytes / ( 1024 …...
ESP32读取DHT11温湿度数据
芯片:ESP32 环境:Arduino 一、安装DHT11传感器库 红框的库,别安装错了 二、代码 注意,DATA口要连接在D15上 #include "DHT.h" // 包含DHT库#define DHTPIN 15 // 定义DHT11数据引脚连接到ESP32的GPIO15 #define D…...
Java 加密常用的各种算法及其选择
在数字化时代,数据安全至关重要,Java 作为广泛应用的编程语言,提供了丰富的加密算法来保障数据的保密性、完整性和真实性。了解这些常用加密算法及其适用场景,有助于开发者在不同的业务需求中做出正确的选择。 一、对称加密算法…...
Android Bitmap治理全解析:从加载优化到泄漏防控的全生命周期管理
引言 Bitmap(位图)是Android应用内存占用的“头号杀手”。一张1080P(1920x1080)的图片以ARGB_8888格式加载时,内存占用高达8MB(192010804字节)。据统计,超过60%的应用OOM崩溃与Bitm…...
tree 树组件大数据卡顿问题优化
问题背景 项目中有用到树组件用来做文件目录,但是由于这个树组件的节点越来越多,导致页面在滚动这个树组件的时候浏览器就很容易卡死。这种问题基本上都是因为dom节点太多,导致的浏览器卡顿,这里很明显就需要用到虚拟列表的技术&…...
鸿蒙DevEco Studio HarmonyOS 5跑酷小游戏实现指南
1. 项目概述 本跑酷小游戏基于鸿蒙HarmonyOS 5开发,使用DevEco Studio作为开发工具,采用Java语言实现,包含角色控制、障碍物生成和分数计算系统。 2. 项目结构 /src/main/java/com/example/runner/├── MainAbilitySlice.java // 主界…...
【分享】推荐一些办公小工具
1、PDF 在线转换 https://smallpdf.com/cn/pdf-tools 推荐理由:大部分的转换软件需要收费,要么功能不齐全,而开会员又用不了几次浪费钱,借用别人的又不安全。 这个网站它不需要登录或下载安装。而且提供的免费功能就能满足日常…...
前端高频面试题2:浏览器/计算机网络
本专栏相关链接 前端高频面试题1:HTML/CSS 前端高频面试题2:浏览器/计算机网络 前端高频面试题3:JavaScript 1.什么是强缓存、协商缓存? 强缓存: 当浏览器请求资源时,首先检查本地缓存是否命中。如果命…...
