当前位置: 首页 > article >正文

MFC程序设计(三)MFC程序启动

从前面的文章我们发现,一个MFC程序没有入口程序却可以运行,这与我们熟知的c++有所区别,现在我们开始探讨为什么MFC程序可以在没有入口程序的情况的可以运行的原因

入口函数

MFC把入口函数封装起来,存在MFC库中,由MFC库安排程序流程:创建窗口,显示窗口等等

执行流程

我们可以通过断点调试程序,这会让我们观察到程序是如何运行的

F9:下断点,F10:执行下一行代码,F11:执行下一个指令,F5:执行代码至下一个断电

现在我们将通过调试程序进行观察MFC是如何运行的,这是一个繁琐的过程。我们将以伪代码进行记录与程序运行有关的代码,期间会将一些宏直接替换为原先的的定义,这会更方便我们理解代码

在C++中,程序执行时,全局对象优先于入口函数执行,这是程序的起点,因此我们从全局对象看起

MFC库中有三个全局变量,存在于PE文件的TLS表中,以下接介绍其中两个

1.AFX_MODULE_STATE aaa;//当前程序模块状态信息

2.AFX_MODULE_THREAD_STATE bbb; //当前程序线程状态信息

注意:官网并未提及这些变量的名字,此处只做程序演示

通过F11,我们进入了该对象的父类构造函数

AFX_MODULE_STATE aaa;//当前程序模块状态信息
AFX_MODULE_THREAD_STATE bbb; //当前程序线程状态信息
CWinApp::CWinApp()//全局对象CMyWinApp theApp的父类构造函数
{
  AFX_MODULE_STATE* pModuleState = AfxGetModuleState();//获取全局变量&aaa
  AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;//获取全局变量&bbb,bbb是aaa的一个成员

  AfxGetThread() 
  {
     AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();//获取&bbb
     CWinThread* pThread = pState->m_pCurrentWinThread;
     return pThread;//返回的为&theApp
  }    
           
  pThreadState->m_pCurrentWinThread = this;//将&theApp保存到bbb的一个成员中
  
  //给theAPP成员赋值 
  m_hThread = ::GetCurrentThread();//获取线程句柄
  m_nThreadID = ::GetCurrentThreadId();//获取线程ID

  pModuleState->m_pCurrentWinApp = this;//将&theApp保存到aaa的一个成员中
   
  AfxGetApp()
  {
    return AfxGetModuleState()->m_pCurrentWinApp;//返回&theApp
  }
}

//ASSERT():断言,当()中判断语句错误时,程序中止

此时全局对象构造完毕,理论上开始进入入口函数。

在接下来的程序代码,InitInstance中发现了窗口的创建显示等等。我们在学习win32的时候了解到,窗口的创建显示等等都是在WinMain中调用的,所以可以推断出,InitInstance被WinMain调用了,其存在于WinMain内部,因此InitInstance才能够实现窗口的创建显示等等

我们通过调用堆栈可以查看函数与函数之间的调用关系,此处我们利用调用堆栈观察InitInstance被谁调用了 

通过调用堆栈,我们发现了WinMain,现在我们双击进入WinMain

WinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,_In_ LPTSTR lpCmdLine, int nCmdShow)//程序流程是不是theApp对象指导向哪走
{
	//win32
	return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow)
	{
		CWinThread* pThread = AfxGetThread()
	    CWinApp* pApp = AfxGetApp();//父类指针指向子类对象,这两代码都是获取&theApp
		AfxWinInit()//保存theAPP成员:程序的启动参数
		{
			pApp->m_hInstance = hInstance;
			pApp->m_lpCmdLine = lpCmdLine;
			pApp->m_nCmdShow = nCmdShow;
			pApp->SetCurrentHandles();
		}
		
		pApp->InitApplication()//利用theApp对象调用应用程序类成员虚函数:程序启动初始化

		if (!pThread->InitInstance())//利用theApp对象调用应用程序类成员虚函数创建并显示窗口。如果我们重写了InitInstance,那么此时代码回到最开始的页面,我们自己写的InitInstance
			
		pThread->Run();//利用theApp对象调用应用程序类成员虚函数:消息循环
		{
			return CWinThread::Run()
			{
				for (;;)//消息循环
				{
					while (!::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE))//查看消息队列里,当没有消息时
					{
						OnIdle(lIdleCount++);//利用theApp对象调用应用程序类成员虚函数:空闲处理
					}
					do
					{
					  if (GetMessage(&(pState->m_msgCur), NULL, NULL, NULL))//当接收到WM_QUIT退出消息
					  {
					    return ExitInstance();//程序结束前,利用theApp对象调用应用程序类成员虚函数: 善后处理。
					  }			
					  ::TranslateMessage(&(pState->m_msgCur));//翻译消息
					  ::DispatchMessage(&(pState->m_msgCur));//分发消息
					}
                    while (::PeekMessage(&(pState->m_msgCur), NULL, NULL, NULL, PM_NOREMOVE));//查看消息队列里,当有消息时
                }
			}
		}
	}
}

这就是MFC程序执行的完整流程

总结

这就是MFC执行的流程所使用的虚函数


http://www.kler.cn/a/516122.html

相关文章:

  • jenkins-k8s pod方式动态生成slave节点
  • Python的泛型(Generic)与协变(Covariant)
  • Cesium特效——城市白模的科技动效的各种效果
  • Mac安装Homebrew
  • CANoe Trace窗口
  • Spring MVC:设置响应
  • 动态路由协议基础知识
  • JavaScript系列(42)--路由系统实现详解
  • 2025.1.20——二、buuctf BUU UPLOAD COURSE 1 1 文件上传
  • 详解单片机学的是什么?(电子硬件)
  • Redis面试题每日20道【其一】
  • AI智慧社区--生成验证码
  • 【CPH系列】RFID标签读取模块,开发说明文档(包含重要内容和BUG)
  • K8S 启动探测、就绪探测、存活探测
  • 软考信安27~Windows操作系统安全相关
  • k8s服务StatefulSet部署模板
  • 如何用概率论解决真实问题?用随机变量去建模,最大的难题是相关关系
  • CAS简解
  • 怎么解决Visual Studio中两个cpp文件中相同函数名重定义问题
  • 【github 使用相关】提交pr和commit message Conventional Commits 规范 代码提交的描述该写什么?
  • 【C++】详细讲解继承(上)
  • 【网络协议】【http】【https】ECDHE-TLS1.2
  • 领域驱动设计(DDD)四 订单管理系统实践步骤
  • 【时时三省】(C语言基础)格式化输入输出函数
  • 2025.1.21——六、BUU XSS COURSE 1
  • P1115 最大子段和