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执行的流程所使用的虚函数