Visual Studio 2022的MFC框架——AfxWinMain全局对象和InitInstance函数

我是荔园微风,作为一名在IT界整整25年的老兵,今天我们来重新审视一下Visual Studio 2022下开发工具的MFC框架知识。 

在看这篇帖子前,请先看我的另一篇帖子《Visual Studio 2022的MFC框架——应用程序向导》。

当程序调用了CWinApp类的构造函数,并执行了CMfcApp类的构造函数,且产生了theApp 对象之后,接下来就进入 WinMain 函数。根据前面我写的一些MFC帖子所示的代码,可以发现WinMain 函数实际上是通过调用 AfxWinMain函数来完成它的功能的。

Afx前缀的函数代表应用程序框架Application Framework函数。应用程序框架实际上是一套辅助我们生成应用程序的框架模型。该模型把多个类进行了一个有机的集成,可以根据该模型提供的方案来设计我们自己的应用程序。在MFC中,以Afx为前缀的函数都是全局函数,可以在程序的任何地方调用它们。

我们可以采取同样的方式查找定义AfxWinMain 函数的源文件,在搜索到的文件中双击winmain.cpp,并在其中找到 AfxWinMain函数的定义代码。

int AFXAPI AfxWinMain (HINSTANCE hInstance, HINSTANCE hPrevInstance,
        _In_ LPTSTR lpCmdLine, int nCmdShow)
{
  ASSERT (hPrevInstance == NULL);

  int nReturnCode =-1;

  CWinThread* pThread = AfxGetThread();
  CWinApp* pApp AfxGetApp();

  // AFX internal initialization
  if ( !AfxWinInit (hInstance, hPrevInstance, lpCmdLine, nCmdShow))
      goto InitFailure;

  // App global initializations (rare)
  if (pApp != NULL && !pApp->InitApplication())
     goto InitFailure;

   // Perform specific initializations
  if (!pThread->InitInstance())
   {
     if (pThread->m_pMainWnd != NULL)
      {
        TRACE(traceAppMsg, 0, "Warning\n");
        pThread->m_pMainWnd->Destroywindow();
      }
    nReturnCode = pThread->ExitInstance();
    goto InitFailure;
 
  nReturnCode = pThread->Run();

  InitFailure:
  #ifdef _DEBUG
     // Check for missing AfxLockTempMap calls
     if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
      {
        TRACE(traceAppMsg, 0, "Warning\n", 
       AfxGetModuleThreadState ()->m_nTempMapLock);
      }
     AfxLockTempMaps ();
     AfxUnlockTempMaps (-1);
  #endif

  AfxWinTerm();
  return nReturnCode;
  }

在上面所示的代码中,AfxWinMain首先调用AfxGetThread函数获得一个CWinThread类型的指针,接着调用 AfxGetApp函数获得一个CWinApp类型的指针。从MFC类库组织结构图中可以知道CWinApp派生于CWinThread。

下面是AfxGetThread函数的源代码,位于thrdcore.cpp文件中。

CWinThread* AFXAPI AfxGetThread()
{
  // check for current thread in module thread state

   AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState ();
   CWinThread* pThread = pState->m_pCurrentWinThread;
  return pThread;
}

从上面所示代码中可以发现, AfxGetThread函数返回的就是在 CWinApp构造函数中保存的 this指针。对Mfc程序来说,这个this指针实际上指向的是CMfcApp的全局对象:theApp。

AfxGetApp是一个全局函数,定义于 afxwin1.inl中:

_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp()

   { return afxCurrentWinApp;}

而afxCurrentWinApp的定义位于 afxwin.h文件中,代码如下:

#define afxCurrentWinApp    AfxGetModuleState()->m_pCurrentWinApp

结合查看之前关于MFC帖子中所示的CWinApp构造函数代码,就可以知道 AfxGetApp函数返回的是在 CWinApp 构造函数中保存的 this 指针。对Mfc程序来说, 这个 this 指针实际上指向的是 CMfcApp的对象: theApp。也就是说,对Mfc程序来说, pThread和 pApp所指向的都是CMfcApp类的对象,即theApp全对象。

再来说说InitInstance函数,再回到上面所示的 AfxWinMain函数,可以看到在接下来的代码中,pThread和 pApp调用了三个函数,这三个函数就完成了Win32程序所需要的几个步骤:设计窗口类、注册窗口类、创建窗口、显示窗口、更新窗口、消息循环,以及窗口过程函数。pApp首先调用 InitApplication函数,该函数完成MFC内部管理方面的工作。

接着,调用pThread的 InitInstance 函数。在Mfc程序中,可以发现从 CWinApp派生的应用程序类CMfcApp也有一个InitInstance函数,其声明代码如下所示。

virtual BOOL InitInstance();

从其定义可以知道,InitInstance函数是一个虚函数。根据类的多态性原理,可以知道AfxWinMain函数在这里调用的实际上是子类  CMfcApp 的 InitInstance函数。CMfcApp类的 InitInstance函数定义代码如下所示。

BOOL CMfcApp::InitInstance()
{
  INITCOMMONCONTROLSEX InitCtrls;
  InitCtrls. dwsize sizeof (InitCtrls);

  //将它设置为包括所有要在应用程序中使用的
  //公共控件类

  InitCtrls. dwICC = ICC_WIN95_CLASSES;
  InitCommonControlsEx(&InitCtrls);

  CWinApp::InitInstance();

  //初始化OLE库
  if (!AfxOleInit())
  {
     AfxMessageBox (IDP_OLE_INIT_FAILED);
      return FALSE;
    }

  AfxEnableControlContainer();

  EnableTaskbarInteraction (FALSE);

  SetRegistryKey(_T("应用程序向导生成的本地应用程序")
    LoadStdProfileSettings(4); //加载标准 INI 文件选项(包括 MRU)

    //注册应用程序的文档模板。  文档模板
  //将用作文档、框架窗口和视图之间的连接

  CSingleDocTemplate* pDocTemplate;

  pDocTemplate = new CSingleDocTemplate(
  IDR_MAINFRAME,
    RUNTIME_CLASS (CMfcDoc),
    RUNTIME_CLASS (CMainFrame),  //主SDI框架窗口
    RUNTIME_CLASS (CMfcView));
  if ( !pDocTemplate)
     return FALSE;
  AddDocTemplate (pDocTemplate);

  //分析标准shel1命令、DDE、打开文件操作的命令行
    CCommandLineInfo cmdInfo;
  ParseCommandLine (cmdInfo);

 
  if ( !ProcessShellCommand (cmdInfo))
     return FALSE;
  
   //唯一的一个窗口已初始化,因此显示它并对其进行更新
    m_pMainWnd->ShowWindow(SW_SHOW);
  m_pMainWnd->Updatewindow ();
  return TRUE;
}

这几篇内容涉及的内容有点难,如果大家看不懂,要结合其他VC的资料综合看。

作者简介:荔园微风,1981年生,高级工程师,浙大工学硕士,软件工程项目主管,做过程序员、软件设计师、系统架构师,早期的Windows程序员,Visual Studio忠实用户,C/C++使用者,是一位在计算机界学习、拼搏、奋斗了25年的老将,经历了UNIX时代、桌面WIN32时代、Web应用时代、云计算时代、手机安卓时代、大数据时代、ICT时代、AI深度学习时代、智能机器时代,我不知道未来还会有什么时代,只记得这一路走来,充满着艰辛与收获,愿同大家一起走下去,充满希望的走下去。

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

goroutine的一点东西

前面的两篇,从相对比较简单的锁的内容入手(也是干货满满),开始了go的系列。这篇开始,进入更核心的内容。我们知道,go应该是第一门在语言层面支持协程的编程语言(可能是我孤陋寡闻),goroutine也完全算的上是go的门面。g…

VBA技术资料MF50:VBA_在Excel中突出显示前3个值

【分享成果,随喜正能量】人受到尊重,不是因为权钱,而是他骨子里透出的,正直与善良。。 我给VBA的定义:VBA是个人小型自动化处理的有效工具。利用好了,可以大大提高自己的工作效率,而且可以提高…

记录--怎么实现一个3d翻书效果

这里给大家分享我在网上总结出来的一些知识,希望对大家有所帮助 本篇主要讨论以下两种翻书动画的实现: 第一种是整页翻转的效果: 这种整页翻转的效果主要是做rotateY的动画,并结合一些CSS的3d属性实现。 第二种折线翻转的效果&…

ACM模式数组构建二叉树Go语言实现

目的 想输入一个数组,然后构造二叉树 例如数组为[6, 2, 8, 0, 4, 7, 9, -1, -1, 3, 5] 对应的二叉树为: 参考资料 ACM模式数组构建二叉树 重点:如果父节点的数组下标是i,那么它的左孩子下标就是i*21,右孩子下标就是…

生产环境部署与协同开发 Git

目录 一、前言——Git概述 1.1 Git是什么 1.2 为什么要使用Git 什么是版本控制系统 1.3 Git和SVN对比 SVN集中式 Git分布式 1.4 Git工作流程 四个工作区域 工作流程 1.5 Git下载安装 1.6 环境配置 设置用户信息 查看配置信息 二、git基础 2.1 本地初始化仓库 ​编辑…

opencv 进阶20-随机森林示例

OpenCV中的随机森林是一种强大的机器学习算法,旨在解决分类和回归问题。随机森林使用多个决策树来进行预测,每个决策树都是由随机选择的样本和特征组成的。在分类问题中,随机森林通过投票来确定最终的类别;在回归问题中&#xff0…

AE2018 安装过程

双击打开安装包,大概等五分钟后。 出现下边安装界面。 安装成功。 可以将图标发送到桌面快捷方式。

MySQL内容及原理记录

原理篇 架构、索引、事务、锁、日志、性能调优 高可用 读写分离、分库分表、分布式ID、高可用、分布式数据库、分布式事务、分布式锁 架构 1 执行一条 SQL 查询语句,期间发生了什么? (1)连接器:客户端通过连接器…

sql server 备份到网络共享

场景:sql server服务器A将数据库备份文件备份到服务器B 1)服务器B创建共享目录 这里我将 D:\ProDbBak 共享,并且Everyone完全控制 2)sql server服务器A能够访问服务器B共享目录,并且能完全控制 3)修改服务…

Kotlin学习之密封类

Kotlin中的密封类: kotlin中的密封类,用关键词Sealed修饰,且还有一个规定:Sealed类的子类应该是Sealed类的嵌套类,或者应该在与Sealed类相同的文件中声明。 当我们想定义一个有相同父类,但是有不同子类的时候&#xf…

C语言每日一练------Day(10)

本专栏为c语言练习专栏,适合刚刚学完c语言的初学者。本专栏每天会不定时更新,通过每天练习,进一步对c语言的重难点知识进行更深入的学习。 今日练习题关键字:自除数 除自身以外数组的乘积 💓博主csdn个人主页&#xff…

K8s简介之什么是K8s

目录 1.概述 2.什么是容器引擎? 3.什么是容器 4.什么是容器编排? 5.容器编排工具 6.到底什么是K8s? 7.为什么市场推荐K8s 8.K8s架构 9.K8s组件 Pods API 服务器 调度器 控制器管理器 Etcd 节点 Kubelet Kube代理 Kubectl 1.概述 Kub…

Mac“其他文件”存放着什么?“其他文件”的清理方法

很多Mac用户在清理磁盘空间时发现,内存占用比例比较大的除了有iCloud云盘、应用程序、影片、音频、照片等项目之外,还有一个“其他文件”的项目磁盘占用比也非常大,想要清理却无从下手。那么Mac“其他文件”里存放的是什么文件?我…

【HSPCIE仿真】输入网表文件(5)基本仿真输出

仿真输出 1. 概述1.1 输出变量1.2 输出分析类型 2. 显示仿真结果2.1 .print语句基本语法示例 2.2 .probe 语句基本语法示例 2.3 子电路的输出2.4 打印控制选项.option probe.option post.option list.option ingold 2.5 .model_info打印模型参数 3. 仿真输出参数的选择3.1 直流…

SQL语法与DDL语句的使用

文章目录 前言一、SQL通用语法二、DDL语句1、DDL功能介绍2、DDL语句对数据库操作(1)查询所有数据库(2)查询当前数据库(3)创建数据库(4)删除数据库(5)切换数据…

qt第一天

#include "widget.h" #include "ui_widget.h" #include "QDebug" Widget::Widget(QWidget *parent): QWidget(parent), ui(new Ui::Widget) {ui->setupUi(this);this->resize(QSize(800,600)); //使用匿名对象,调用重…

无涯教程-Android - Broadcast Receivers

Broadcast Receivers 仅响应来自其他应用程序或系统本身的广播消息,这些消息有时称为events或intents。例如,应用程序还可以启动广播,以使其他应用程序知道某些数据已下载到设备并可供他们使用,因此广播接收器将拦截此通信并启动适…

数据结构(Java实现)-ArrayList与顺序表

什么是List List是一个接口,继承自Collection。 List的使用 List是个接口,并不能直接用来实例化。 如果要使用,必须去实例化List的实现类。在集合框架中,ArrayList和LinkedList都实现了List接口。 线性表 线性表(lin…

SQL server数据库-定制查询-指定查询列/行、结果排序和Like模糊查询

本篇讲述进阶查询方法,如有语句不明确,可跳转本文专栏学习基础语法 1、指定列查询 特点 只会显示你输入的列的数据,会根据你输入的顺序进行显示,可以自定义查询显示时的列名 (1)只会显示你输入的列的数…