MFC 程序执行流程

目录

MFC 程序启动

MFC 入口函数

程序执行流程总结


在Win32课程中WinMain由程序员自己实现,那么流程是程序员安排,但到了MFC中,由于MFC库实现WinMain,也就意味着MFC负责安排程序的流程。

MFC 程序启动

程序的启动,构造theApp对象,调用父类CWinApp的构造函数。

  • 将theApp对象的地址保存到线程状态信息中
  • 将theApp对象的地址保存到模块状态信息中
  • 进入WinMain函数,调用AfxWinMain函数

在应用类下断点,并且打开函数调用堆栈

摁下F11,CMyWinApp(){} 构造函数调用,会先调用 CMyWinApp 类的父类 CWinApp 类构造函数,在调用爷爷类 CThread

爷爷类 CThread 只是做一些初始化的操作,不做关注,直接看 CWinApp 的构造函数

这两个都是类,都定义在全局:

  1. AFX_MODULE_STATE                      当前程序模块状态信息
  2. AFX_MODULE_THREAD_STATE     当前程序线程状态信息
//获取当前程序模块状态信息AFX_MODULE_STATE* pModuleState = AfxGetModuleState();//获取当前程序线程状态信息AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;// 将 &theApp 保存到 当前程序线程状态信息 的一个成员中pThreadState->m_pCurrentWinThread = this;

一个断言(assertion),用于在代码中进行调试和错误检测。在这行代码中,它检查AfxGetThread()返回的线程指针是否等于当前的线程指针this,如果不相等,则会触发断言失败,打印相关的错误信息并中断程序的执行。

ASSERT(AfxGetThread() == NULL);
pThreadState->m_pCurrentWinThread = this;
ASSERT(AfxGetThread() == this);

 AfxGetThread()  函数 返回的为&theApp

CWinThread* AFXAPI AfxGetThread()
{// check for current thread in module thread state  AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();CWinThread* pThread = pState->m_pCurrentWinThread;return pThread;
}

这两行代码看不明白?

可以在前面补上this->,发现 this 是 CMyWinApp 的对象,这两个成员变量应该是继承父类来的

获取当前线程的伪句柄,获取当前线程的ID

	m_hThread = ::GetCurrentThread();m_nThreadID = ::GetCurrentThreadId();

所以 AfxGetApp() 返回&theApp

	ASSERT(afxCurrentWinApp == NULL); // only one CWinApp object pleasepModuleState->m_pCurrentWinApp = this;ASSERT(AfxGetApp() == this);

总结:

  1. CWinApp,封装了应用程序、线程等信息
  2. CMyWinApp继承自CWinApp 负责程序的运行
  3. CMyWinApp theApp;  程序就开始执行,并且调用构造函数,依次为祖宗类到本类,只需要关注父类 CWinApp 构造函数即可
  4. CWinApp::CWinApp(LPCTSTR lpszAppName),在调用时会自动传递参数并且为空,主要逻辑未带如下
CWinApp::CWinApp(LPCTSTR lpszAppName)
{m_pszAppName = NULL; // 应用程序的名称 赋值为空/* 下面是初始化当前的执行线程,也就是主线程了  */AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();     // 获取当前程序模块状态信息AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread; // 当前程序线程状态信息,这个类也是当前程序模块信息类的成员类                                    pThreadState->m_pCurrentWinThread = this;                       // 把&theapp赋值给当前程序线程信息的成员编程m_hThread = ::GetCurrentThread();                               // 获取当前线程的伪句柄m_nThreadID = ::GetCurrentThreadId();                           // 获取当前线程的id/* 初始化应用类状态 */pModuleState->m_pCurrentWinApp = this;                          // 把&theapp赋值给当前程序线程信息的成员编程/*剩下的就是初始化一些其他的成员变量*/
}

其他调用到的一些函数:

  • AfxGetModuleState() :调用此函数获取一个指针,指向AFX_MODULE_STATE
  • AfxGetThread():调用此函数获取一个指针,指向表示当前执行线程的 CWinThread 对象,&theapp
  • GetCurrentThread():获取当前线程伪句柄
  • AfxGetApp():调用此函数获取一个指针,指向AFX_MODULE_STATE,&theapp

综上所述,应用程序类的构造函数主要做一些初始化的操作
 

 

MFC 入口函数

进入入口函数WinMain

  • 获取应用程序类对象theApp的地址
  • 利用theApp地址调用InitApplication,初始化当前应用程序的数据
  • 利用theApp地址调用InitInstance函数初始化程序,在函数中我们创建窗口并显示。
  • 利用theApp地址调用CWinApp的Run函数进行消息循环
  • 如果没有消息,利用theApp地址调用OnIdle虚函数实现空闲处理
  • 程序退出利用theApp地址调用ExitInstance虚函数实现退出前的善后处理工作

给函数 InitInstance 下断点

查看调用堆栈

补充一点:应用程序句柄和窗口句柄的关系?

应用程序句柄(HINSTANCE)用于标识一个特定的应用程序实例,它通常在应用程序启动时由操作系统分配。应用程序句柄主要用于访问应用程序的资源,例如图标、位图、对话框模板等。

窗口句柄(HWND)用于标识一个窗口,它是在窗口被创建时由操作系统分配的。窗口句柄允许应用程序与窗口进行交互,比如显示、隐藏、关闭、重绘等操作。

在关系上,应用程序句柄和窗口句柄可以是相互独立的。一个应用程序实例可以拥有多个窗口,每个窗口都有自己的窗口句柄,但它们共享相同的应用程序句柄。因此,应用程序句柄通常用于全局资源的管理,而窗口句柄用于特定窗口的操作。

查看 wWinMain 函数,函数功能主要实现是交给 AfxWinMain 实现的

下面是 AfxWinMain的伪代码

这两代码都是获取&theApp

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

AfxWinInit 函数与初始化MFC有关

	if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))goto InitFailure;

利用theApp对象调用应用程序类成员虚函数 初始化

	if (pApp != NULL && !pApp->InitApplication())goto InitFailure;

利用theApp对象调用应用程序类成员虚函数 创建并显示窗口

这就和我们在 CMyWinApp 中重写的虚函数对上了,后面的是未初始化成功的处理

    if (!pThread->InitInstance()){if (pThread->m_pMainWnd != NULL){TRACE(traceAppMsg, 0, "Warning: Destroying non-NULL m_pMainWnd\n");pThread->m_pMainWnd->DestroyWindow();}nReturnCode = pThread->ExitInstance();goto InitFailure;}

利用theApp对象调用应用程序类成员虚函数 消息循环

nReturnCode = pThread->Run();

接下来进入 Run() 函数看看

m_pMainWnd 是 CWinThread 类的一个成员变量,是指向线程的主窗口对象的指针

也就是说 if 语句块是处理错误的,return 处调用父类的Run函数

int CWinApp::Run()
{if (m_pMainWnd == NULL && AfxOleGetUserCtrl()){// Not launched /Embedding or /Automation, but has no main window!TRACE(traceAppMsg, 0, "Warning: m_pMainWnd is NULL in CWinApp::Run - quitting application.\n");AfxPostQuitMessage(0);}return CWinThread::Run();
}

接下来就是真的消息循环的位置

突然看到这里有点懵,AfxGetThreadState() 函数,全局函数,调用此函数可获取指向表示当前正在执行的线程的 CWinThread 对象的指针。必须从所需的线程内调用。

_AFX_THREAD_STATE* pState = AfxGetThreadState();

检查线程消息队列中是否存在已发布的消息,

如果没有消息,做空闲处理,利用theApp对象调用应用程序类成员虚函数 空闲处理

		while (bIdle &&!::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE)){// call OnIdle while in bIdle stateif (!OnIdle(lIdleCount++))bIdle = FALSE; // assume "no idle" state}

PumpMessage实际上调用的是  AfxInternalPumpMessage() 函数

程序结束前,利用theApp对象调用应用程序类成员虚函数 善后处理。

if (!PumpMessage())return ExitInstance();

当窗口收到 WM_QUIT消息,就返回会FASLE,否则返回TRUE

	if (!::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL)){
#ifdef _DEBUGTRACE(traceAppMsg, 1, "CWinThread::PumpMessage - Received WM_QUIT.\n");pState->m_nDisablePumpCount++; // application must die
#endif// Note: prevents calling message loop things in 'ExitInstance'// will never be decrementedreturn FALSE;}
	if (pState->m_msgCur.message != WM_KICKIDLE && !AfxPreTranslateMessage(&(pState->m_msgCur))){::TranslateMessage(&(pState->m_msgCur));::DispatchMessage(&(pState->m_msgCur));}return TRUE;

程序执行流程总结

程序启动后,会调用构造函数初始化应用程序类的对象,以及其父类(主要)

  • 初始化 当前模块程序信息 以及 当前模块程序线程信息
  • 其他成员变量的初始化

调用 AfxWinMain 函数,执行程序流程

  • 初始化 MFC 以及 应用程序
  • 调用 InitInstance 完成窗口的注册创建显示
  • 调用爷爷类 CWinThread 的Run函数,进入消息循环

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

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

相关文章

Node.js初学习

目录 1、Node.js简介 2、npm是什么 3、node.js和vue是什么关系 1、Node.js简介 Introduction to Node.js | Node.js 根据官网的介绍:Node.js是一个开源的跨平台JavaScript运行时环境。Node.js在浏览器之外运行V8 JavaScript引擎,这是谷歌Chrome的核…

React 状态

大家好,欢迎来到 React 状态的课程。在这一课中,我们将学习如何在 React 中使用状态。 什么是状态? 状态是组件的数据。组件的状态可以通过 this.state 对象访问。 class ComponentName extends React.Component {constructor(props) {sup…

PCB设计规则中的经验公式_笔记

PCB设计规则中的经验公式 规则1 - 临界长度规则2 - 信号带宽与上升时间规则3- 时钟信号带宽规则4-信号传输速度规则5- 集肤 (效应) 深度规则6 - 50Ω传输线电容规则7 - 50Ω传输线电感规则8 - 回流路径电感规则9 - 地弹噪声规则10- 串行传输比特率与信号带宽规则11- PCB走线直流…

HIVE窗口函数

什么是窗口函数 hive中开窗函数通过over关键字声明;窗口函数,准确地说,函数在窗口中的应用;比如sum函数不仅可在group by后聚合,在可在窗口中应用; hive中groupby算子和开窗over,shuffle的逻辑…

面试 Java 算法高频题五问五答第一期

面试 Java 算法高频题五问五答第一期 作者:程序员小白条,个人博客 相信看了本文后,对你的面试是有一定帮助的! ⭐点赞⭐收藏⭐不迷路!⭐ 1)括号生成: 数字 n 代表生成括号的对数,请你设计一个…

OpenSergo Dubbo 微服务治理最佳实践

*作者:何家欢,阿里云 MSE 研发工程师 Why 微服务治理? 现代的微服务架构里,我们通过将系统分解成一系列的服务并通过远程过程调用联接在一起,在带来一些优势的同时也为我们带来了一些挑战。 如上图所示,可…

C语言之枚举类型

目录 枚举类型 枚举常量 枚举类型的特征 命名空间 本节我们来学习表示一定整数值的集合的枚举类型。 枚举类型 老样子&#xff0c;我们先用一段程序引出&#xff1a; /*显示所选动物的叫声*/ #include<stdio.h>enum animal {Dog, Cat, Monkey, Invalid}; /*显示狗叫…

Zotero攻略

给大家分享一下我对于Zotero的使用。 1、下载链接 Zotero | Your personal research assistant 进入后直接下载即可 2、一些好用的插件 &#xff08;1&#xff09;Zotero Connector 下载地址&#xff1a;Zotero | Connectors 超级好用&#xff01;不用一篇一篇下PDF了&am…

Redis设计与实现之事务

一、事务 Redis 通过 MULTI 、DISCARD 、EXEC 和 WATCH 四个命令来实现事务功能&#xff0c;本章首先讨 论使用 MULTI 、DISCARD 和 EXEC 三个命令实现的一般事务&#xff0c;然后再来讨论带有 WATCH 的事务的实现。 因为事务的安全性也非常重要&#xff0c;所以本章最后通过…

【JavaWeb】Request(学习笔记)

一、Request概述 1、request对象和response对象的原理 tomcat服务器会根据请求u1中的资源路径&#xff0c;创建对应的ServletDemo1对象tomcat 服务器&#xff0c;会创建request对象和response对象&#xff0c;request对象中封装请求消息数据tomcat将request和response两个对象…

18个非技术面试题

请你自我介绍一下你自己&#xff1f; 这道面试题是大家在以后面试过程中会常被问到的&#xff0c;那么我们被问到之后&#xff0c;该如果回答呢&#xff1f;是说姓名&#xff1f;年龄&#xff1f;还是其他什么&#xff1f; 最佳回答提示&#xff1a; 一般人回答这个问题往往会…

为什么参数上必须加@RequestBody?

PostMapping("/login")public Result<Map<String,Object>> login(RequestBody User user){return Result.success();} 在这个 PostMapping("/login") 的控制器方法中&#xff0c;使用 RequestBody 注解的目的是将请求体中的 JSON 数据映射到方…

函数柯里化 剖析

定义一个函数&#xff0c;以闭包的方式内部函数调用外部函数,并返回内部函数 function calc(n){ //args是获取的第一次参数的值let argsArray.prototype.slice.call(arguments)// 上面的args写法等同于下方的for循环// for (var i 0; i < arguments.length; i) {// A…

neuq-acm预备队训练week 9 P1119 灾后重建

解题思路 本题可以用最短路算法——Floyd AC代码 #include<bits/stdc.h> #define inf 1e9 using namespace std; const int N 2e2 50; int n, m, q, now 0, a, b, c, t[N], G[N][N];int main() {scanf("%d%d", &n, &m);for(int i 0;i<n;i)sc…

设计模式-GOF对各个模式的定义

以下内容是对设计模式之父GOF的著作《设计模式——可复用面向对象软件的基础》定义的摘抄 1 抽象工厂 意图 提供一个接口以创建一系列相关或相互依赖的对象&#xff0c;而无须指定它们具体的类。 适用性 在以下情况下使用抽象工厂模式&#xff1a; 一个系统要独立于它的产…

2023新时代中国模特大赛总决赛在京落幕

12月16日&#xff0c;备受瞩目的2023新时代中国模特大赛圆满落幕。本次大赛旨在挖掘和培养具有新时代特色的模特人才&#xff0c;推动中国时尚产业的创新发展。 作为中国时尚界的重要赛事&#xff0c;新时代中国模特大赛吸引了来自全国各地的优秀模特选手45名参加全国总决赛。在…

ReactHooks大全—useState

React Hooks是React 16.8版本引入的一种新的编程范式&#xff0c;它可以让我们在不使用class的情况下&#xff0c;使用state和其他React特性&#xff0c;。React Hooks的出现&#xff0c;不仅提高了函数组件的功能和复用性&#xff0c;也简化了组件的编写和维护&#xff0c;让我…

Spring 依赖查找知识点总结

前言 源码在我github的guide-spring仓库中&#xff0c;可以克隆下来 直接执行。 我们本文主要来介绍依赖查找的使用示例 依赖查找 什么是依赖查找 依赖查找并不是 Spring 框架特有的概念&#xff0c;它是一种在软件开发中获取依赖对象的方式。它通常用于获取运行时需要的服…

The Grid – Responsive WordPress Grid响应式网格插件

点击阅读The Grid – Responsive WordPress Grid响应式网格插件原文 The Grid – Responsive WordPress Grid响应式网格插件是一个高级 wordpress 网格插件&#xff0c;它允许您在完全可定制且响应迅速的网格系统中展示任何自定义帖子类型。 Grid WordPress 非常适合展示您的博…

QT信号、槽机制介绍与实现

时间记录&#xff1a;2023/12/17 1.介绍 QT中进行对象之间事件的处理机制为信号、槽机制&#xff0c;即一个对象状态改变时发出信号&#xff0c;然后与此信号进行绑定的对应槽便会被触发&#xff0c;类似于c/c里面的回调函数机制 2.信号 在一定情况下被发射的事件&…