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.hqwc.cn/news/284144.html

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

相关文章

热烈祝贺龙泰环保加入2024济南生物发酵展

江苏龙泰环保设备制造有限公司(LTEP),是一家致力于工业废气技术的研发、设计、制造、安装调试于一体的综合性科技型企业, 公司自成立以来,坚持高标准、严要求,塑造了一支经验丰富、技术精湛的知识化专业队伍;在坚持自主…

【算法题】 TLV解析 Ⅱ (js)

从第三个字节开始因此 const msg "0F04ABABABAB"; const msg1 "0F04ABABABAB10001FF"; function solution(msg, tags) {const tagObj {};for (let i 0; i 3 < msg.length; ) {const tag parseInt(msg.slice(i, i 2), 16);const len parseInt(m…

MySQL - 创建表的三种方法详解及练习

目录 &#x1f959;1. 基础创建 &#x1f9c0;实例1 &#x1f959;2. 带约束创建 &#x1f9c0;实例2 &#x1f959;3. 复制创建 &#x1f9c0;实例3&#xff1a; &#x1f9c0;实例4&#xff1a; &#x1f9c0;实例5&#xff1a; ​ &#x1f9c0;实例6&#xff1a; &am…

社科院与新加坡新跃社科联合培养博士—我想我的人生变得精彩

既然人生的幕布已拉开&#xff0c;就一定要积极的演出&#xff0c;既然脚步已经跨出&#xff0c;风吹坎坷也不能退步&#xff0c;既然我已经把希望播在这里&#xff0c;就一定要坚持到胜利的谢幕&#xff0c;人生没有什么是为了别人做的&#xff0c;工作不是为了老板&#xff0…

手把手入门MO | 如何通过通过 FineBI 实现 MatrixOne 的可视化报表

1. 概述 FineBI 是新一代大数据分析工具&#xff0c;它有助于企业的业务人员深入了解和充分利用他们的数据。在 FineBI 中&#xff0c;用户可以轻松地制作多样化的数据可视化信息&#xff0c;自由分析和探索数据。FineBI 具有多种数据连接功能&#xff0c;可用于创建各种复杂的…

Android通过listview实现输入框自定义提示栏(代替AutoCompleteTextView自动完成文本框)

效果图 背景 本人因为一些需求初次接触android&#xff0c;需要实现一个类似android自带的AutoCompleteTextView&#xff08;自动完成文本框&#xff09;&#xff0c;但和其不同的是通过后端接口直接筛选数据&#xff08;自己的分词处理规则&#xff09;&#xff0c;然后返回前…

Android-高效加载大图

Android 高效加载大图 前言读取位图尺寸和类型将按比例缩小的版本加载到内存中 前言 图片有各种形状和大小。在很多情况下&#xff0c;它们的大小超过了典型应用界面的要求。例如&#xff0c;系统“图库”应用会显示使用 Android 设备的相机拍摄的照片&#xff0c;这些照片的分…

如何实现公网访问本地内网搭建的WBO白板远程协作办公【内网穿透】

最近&#xff0c;我发现了一个超级强大的人工智能学习网站。它以通俗易懂的方式呈现复杂的概念&#xff0c;而且内容风趣幽默。我觉得它对大家可能会有所帮助&#xff0c;所以我在此分享。点击这里跳转到网站。 文章目录 前言1. 部署WBO白板2. 本地访问WBO白板3. Linux 安装cp…

《代码随想录》--二叉树(一)

《代码随想录》--二叉树 第一部分 1、二叉树的递归遍历2、二叉树的迭代遍历3、统一风格的迭代遍历代码4、二叉树的层序遍历226.翻转二叉树 1、二叉树的递归遍历 前序遍历 中序遍历 后序遍历 代码 前序遍历 class Solution {public List<Integer> preorderTraversal(T…

redis:五、缓存持久化(RDB和AOF)的开启和配置、面试回答模板

持久化 方案 redis中自身存在两种方案&#xff0c;分别叫RDB和AOF&#xff0c;来保障数据的持久化。其中前者默认开启&#xff0c;后者默认关闭。 redis是基于内存的&#xff0c;redis持久化的意思就是将redis数据&#xff0c;即内存数据写入磁盘等持久化存储设备当中。 RDB…

数字化医疗新篇章:构建智能医保支付购药系统

在迎接数字化医疗时代的挑战和机遇中&#xff0c;智能医保支付购药系统的建设显得尤为重要。本文将深入介绍如何通过先进的技术实现&#xff0c;构建一套智能、高效的医保支付购药系统&#xff0c;为全面建设健康中国贡献力量。 1. 引言 随着医疗科技的飞速发展&#xff0c;…

【网络安全技术】传输层安全——SSL/TLS

一、TLS位置及架构 TLS建立在传输层TCP/UDP之上&#xff0c;应用层之下。 所以这可以解决一个问题&#xff0c;那就是为什么抓不到HTTP和SMTP包&#xff0c;因为这两个在TLS之上&#xff0c;消息封上应用层的头&#xff0c;下到TLS层&#xff0c;TLS层对上层消息整个做了加密&…