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,一经查实,立即删除!

相关文章

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;所以本章最后通过…

18个非技术面试题

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

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…

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

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

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.信号 在一定情况下被发射的事件&…

【TB作品】51单片机,语音出租车计价器

西交大题目 1.语音出租车计价器 一、功能要求: 1.具有可模拟出租车车轮转速传感器的硬件设计,可计量出租车所走的公 里数。 2.显示和语音播报里程、价格和等待红灯或堵车的计时价格: 3.具有等待计时功能 4.具有实时年月日显示和切换功能。 5.操作简单、界面友好。 二、设计建议…

08-工厂方法

意图 定义一个用于创建对象的接口&#xff0c;让子类决定实例化哪一个类 类图 适用性 在下列情况可以使用工厂方法模式&#xff1a; 当一个类不知道它所必须创建的对象的类的时候。当一个类希望由它的子类来指定它所创建的对象的时候。当类将创建对象的职责委托给多个帮助子…

ELK(八)—Metricbeat部署

目录 介绍修改配置文件启动 Modulenginx开启状态查询配置Nginx module查看是否配置成功 介绍 Metricbeat 是一个轻量级的开源度量数据收集器&#xff0c;用于监控系统和服务。它由 Elastic 公司开发&#xff0c;并作为 Elastic Stack&#xff08;Elasticsearch、Logstash、Kiba…

【Linux】在vim中批量注释与批量取消注释

在vim编辑器中&#xff0c;批量注释和取消注释的操作可以通过进入V-BLOCK模式、选择要注释或取消注释的内容、输入注释符号或选中已有的注释符号和按键完成。这些操作可以大大提高代码或文本的编写和修改效率&#xff0c;是vim编辑器中常用的操作之一。 1.在vim中批量注释的步…

[elementPlus] teleported 在 ElSubMenu中的用途

如图 一个菜单对应的路由结构如上图 如果做适配窄屏幕 如果在 <ElSubMenu :index"route.path" >中不加入 teleported 就会出现问题 加上就OK了 <ElSubMenu :index"route.path" teleported>

力扣200. 岛屿数量(java DFS解法)

Problem: 200. 岛屿数量 文章目录 题目描述思路解题方法复杂度Code 题目描述 思路 该问题可以归纳为一类遍历二维矩阵的题目&#xff0c;此类中的一部分题目可以利用DFS来解决&#xff0c;具体到本题目&#xff1a; 1.我们首先要针对于二维数组上的每一个点&#xff0c;尝试展…

单片机期末复习

前言 发现很多人都写了单片机原理及接口技术课后习题的答案&#xff0c;但是也就只写了答案而已&#xff0c;可能是他们觉得太简单的缘故吧&#xff0c;我这里对此进行一下我近段时间复习的总结&#xff0c;本篇博客只展示选择题、填空题和判断题的答案&#xff0c;仅供参考&a…

饥荒Mod 开发(十一):修改物品堆叠

饥荒Mod 开发(十)&#xff1a;制作一把AOE武器 饥荒Mod 开发(十二)&#xff1a;一键制作 饥荒中物品栏有限&#xff0c;要拾取的物品有很多&#xff0c;经常装不下要忍痛丢掉各种东西&#xff0c;即使可以将物品放在仓库但是使用不方便&#xff0c;所以可以将物品的堆叠个数设…