MFC 程序执行流程

news2024/11/27 1:41:42

目录

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 please
	pModuleState->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 state
			if (!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 _DEBUG
		TRACE(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 decremented
		return 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.coloradmin.cn/o/1319112.html

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

相关文章

【JAVA】CyclicBarrier源码解析以及示例

文章目录 前言CyclicBarrier源码解析以及示例主要成员变量核心方法 应用场景任务分解与合并应用示例 并行计算应用示例 游戏开发应用示例输出结果 数据加载应用示例 并发工具的协同应用示例 CyclicBarrier和CountDownLatch的区别循环性:计数器的变化:用途…

神经网络可以计算任何函数的可视化证明

神经网络可以计算任何函数的可视化证明 对于神经网络,一个显著的事实就是它可以计算任何函数。 如下:不管该函数如何,总有神经网络能够对任何可能的输入x,输出值f(x) 即使函数有很多输入和输出&#xff0…

【每日一题】【12.17】746.使用最小花费爬楼梯

🔥博客主页: A_SHOWY🎥系列专栏:力扣刷题总结录 数据结构 云计算 数字图像处理 力扣每日一题_ 1.题目链接 746. 使用最小花费爬楼梯https://leetcode.cn/problems/min-cost-climbing-stairs/ 2.题目详情 今天的每日一题又…

leetcode每日一题打卡

leetcode每日一题 746.使用最小花费爬楼梯162.寻找峰值 从2023年12月17日开始打卡~持续更新 746.使用最小花费爬楼梯 2023/12/17 代码 解法一 class Solution {public int minCostClimbingStairs(int[] cost) {int n cost.length;int[] dp new int[n1];dp[0] 0;dp[1] 0;…

RocketMQ系统性学习-SpringCloud Alibaba集成RocketMQ以及批量发送消息、消息过滤实战

文章目录 批量发送消息消息过滤 批量发送消息 批量发送消息可以减少网络的 IO 开销,让多个消息通过 1 次网络开销就可以发送,提升数据发送的吞吐量 虽然批量发送消息可以减少网络 IO 开销,但是一次也不能发送太多消息 批量消息直接将多个消…

结构型设计模式(二)装饰器模式 适配器模式

装饰器模式 Decorator 1、什么是装饰器模式 装饰器模式允许通过将对象放入特殊的包装对象中来为原始对象添加新的行为。这种模式是一种结构型模式,因为它通过改变结构来改变被装饰对象的行为。它涉及到一组装饰器类,这些类用来包装具体组件。 2、为什…

将mjpg格式数转化成opencv Mat格式

该博客可以解决如下两个问题: 1、将mjpg格式数据转化成opencv Mat格式 2、v4l2_buffer 格式获取的mjpg格式数据转换成Mat格式。 要将 MJPEG 格式的数据转换为 OpenCV 的 Mat 格式,您可以使用 imdecode 函数。imdecode 函数可以将图像数据解码为 Mat 对象…

【MySQL】数据库和表的操作

数据库和表的操作 一、数据库的操作1. 创建数据库2. 字符集和校验规则(1)查看系统默认字符集以及校验规则(2)查看数据库支持的字符集(3)查看数据库支持的字符集校验规则(4)校验规则对…

B01、JVM与Java体系结构-01

字节码与多语言混合编程 字节码概述: 我们平时说的java字节码,指的是用java语言编译成的字节码。准确的说任何能在jvm平台上执行的字节码格式都是一样的。所以应该统称为:jvm字节码。不同的编译器,可以编译出相同的字节码文件&…

05 动态渲染数据

概述 实际上动态渲染数据&#xff0c;在《使用CDN开发Vue3项目》中就已经学习过了&#xff0c;核心代码如下&#xff1a; <div id"vue-app">{{text}}</div> <script src"https://cdn.staticfile.org/vue/3.0.5/vue.global.js"></sc…

jsp属性访问控制管理系统Myeclipse开发mysql数据库web结构java编程计算机网页项目

背景&#xff1a;为了解决共享数据授权访问的管理问题&#xff0c;出现了早期的自主访问控制&#xff08;Discretionary Access Control&#xff0c;DAC&#xff09;和强制访问控制&#xff08;Mandatory Access Control&#xff0c;MAC&#xff09;&#xff0c;随着计算机和技…

STM32F103RCT6开发板M3单片机教程06--定时器中断

前言 除非特别说明&#xff0c;本章节描述的模块应用于整个STM32F103xx微控制器系列&#xff0c;因为我们使用是STM32F103RCT6开发板是mini最小系统板。本教程使用是&#xff08;光明谷SUN_STM32mini开发板&#xff09; STM32F10X定时器(Timer)基础 首先了解一下是STM32F10X…

Unity中URP下的菲涅尔效果实现(URP下的法线和视线向量怎么获取)

文章目录 前言一、实现思路二、实现原理我们可以由下图直观的感受到 N 与 L夹角越小&#xff0c;点积越接近&#xff08;白色&#xff09;1。越趋近90&#xff0c;点积越接近0&#xff08;黑色&#xff09; 三、实现URP下的菲涅尔效果1、我们新建一个Shader&#xff0c;修改为最…

机器学习笔记 - 时间序列分析基础概念解释

一、简述 时间序列分析是一种统计方法,可检查定期收集的数据点以揭示潜在的模式。该技术与各个行业高度相关,因为它可以根据历史数据做出决策和预测。通过了解过去并预测未来,时间序列分析在金融、医疗保健、能源、供应链管理、天气预报、营销等领域发挥着至关重要的作用。 …

关于mysql存储过程中N/A和null的使用注意事项

oracle和mysql的存储过程大同小异&#xff0c;但是一些细节还是需要留意的。最近发现mysql的N/A和null在存储过程中容易忽略的一点&#xff0c;这会导致我们的存储过程提前结束。今天突然想起来了就记录一下。   mysql的N/A和null区别网上也说得很详细了&#xff0c;我就不赘…

频谱论文:面向频谱地图构建的频谱态势生成技术研究

#频谱# [1]李竟铭.面向频谱地图构建的频谱态势生成技术研究.2019.南京航空航天大学,MA thesis.doi:10.27239/d.cnki.gnhhu.2019.000556. &#xff08;南京航空航天大学&#xff09; 频谱地图是对无线电环境的抽象表达&#xff0c;它可以直观、多维度地展现频谱态势信息&…

部署智能合约以及 javascript 调用合约函数(Web3项目二实战之三)

在上一篇 智能合约是Web3项目的核心要务(Web3项目二实战之二) ,我们已然为项目编写了智能合约,在攥写完智能合约后,该项目将完成了一大部分,剩下无非就是用户界面交互的内容。 然而,在码完了智能合约代码后,起着承前启后关键性的便是,前端界面与智能合约的交互。 智能…

scroll-behavior属性使用方法

定义和用法&#xff1a; scroll-behavior 属性规定当用户单击可滚动框中的链接时&#xff0c;是否平滑地&#xff08;具动画效果&#xff09;滚动位置&#xff0c;而不是直线跳转。 <style>element{/* 核心代码 */scroll-behavior: smooth;} </style> 属性值&am…

gitlab ci pages

参考文章 gitlab pages是什么 一个可以利用gitlab的域名和项目部署自己静态网站的机制 开启 到gitlab的如下页面 通过gitlab.ci部署项目的静态网站 # build ruby 1/3: # stage: build # script: # - echo "ruby1"# build ruby 2/3: # stage: build …

翻译: 如何分析你的工作是否被AI替代 比如程序员、律师 Additional job analysis examples

我发现对于许多职业角色&#xff0c;人们心中都有一个标志性的任务&#xff0c;这个任务独特地定义了那个职业角色。例如&#xff0c;计算机程序员编写代码。医生可能会看病人。律师去法庭上争论案件。我认为当人们考虑人工智能的机会时&#xff0c;通常会本能地问&#xff0c;…