我们在Win32中知道一个Windows窗口程序需要在主函数中经过注册窗口、创建窗口、显示窗口、消息循环、窗口的消息处理函数进行消息处理五个部分进行实现的。但是在MFC生成的的函数中我们没有见到主函数,但我们却能够创建窗口、处理消息。那么,它是如何执行的呢。接下类让我们揭盖MFC的神秘面纱,了解MFC的启动过程。
要想了解win32的执行过程和消息机制请观看这篇文章windows窗口创建流程及window消息机制详解
为了能够更好的了解MFC的执行过程,我们将使用Win32创建项目,然后修改为MFC的项目。因为win32的项目和MFC的项目取决于能否使用MFC库
项目创建过程
创建win32项目,在项目属性–>高级,将MFC的使用改成使用MFC库
添加CPP文件,包含#include
书写代码
(1)定义自己的框架类,派生自CFrameWnd类。
(2) 定义自己的应用程序类,派生自CWinApp类,并定义构造函数以及重写InitInstance虚函数,在程序中创建并显示窗口。
代码如下:
CMyWinApp::CMyWinApp()
{
}
BOOL CMyWinApp: : InitInstance ()
{
CMDIFrameWnd* pFrame = new CMDIFrameWnd(); //创建框架类
pFrame->Create(NULL,_T("MFCBase")); //创建窗口
m_pMainWnd = pFrame; //将框架类对象保存到theApp的属性中
pFrame->ShowWindow(SW_SHOW); //显示窗口
pFrame->UpdateWindow();
return TRUE;
}
(3) 创建全局变量 CMyWinApp的对象
MFC的入口函数和win32窗口程序相同,都是从WinMain入口。但是MFC库已经实现了WinMain函数,所以在程序中不需要实现。
因此,在Win32中WinMain由程序员自己实现,那么流程是程序员安排的,但到了MFC中,由于MFC库实现了WinMain,也就意味着MFC负责安排程序的流程。
通过程序断点调试并通过伪代码的方式,获取MFC的执行流程。
在MFC中存在三个全局变量分别为 AFX_MODULE_STATE pModuleState 当前程序模块状态信息 、AFX_MODULE_THREAD_STATE pModuleThread当前模块线程状态信息、 AFX_THREAD_STATE pThreadState 当前线程状态信息
//theApp基类构造函数
CWinApp::CWinApp()
{
/* 该行代码,获取全局变量的地址
第一个全局变量: AFX_MODULE_STATE pModuleState 当前程序模块状态信息
*/
//_AFX_CMDTARGET_GETSTATE();是一个宏,指代着AfxGetModutate();
AFX_MODULE_STATE* pModuleState = _AFX_CMDTARGET_GETSTATE();
/*
获取第二个全局变量 AFX_MODULE_THREAD_STATE* pThreadState 当前程序线程状态信息
*/
AFX_MODULE_THREAD_STATE* pThreadState = pModuleState->m_thread;
ASSERT(AfxGetThread() == NULL);//判断当前线程中的m_pCurrentWinThread是否为NULL
/*
m_pCurrentWinThread为空
将theApp的保存到全局变量pThreadState的成员变量m_pCurrentWinThread中
此时moduleState、 pThreadState、theApp的存储关系
pModuleState->m_thread(pThreadState)->m_pCurrentWinThread(theApp)
*/
pThreadState->m_pCurrentWinTread = this; //该this指代theApp;
//AfxGetThread内部执行过程见下面
ASSERT(AfxGetThread() == this); //判断是否赋值成功
//这两个是theApp的属性
this -> m_hThread = ::GetCurrentThread();
this -> m_nThreadID = ::GetCurrentThread();
/*
此时pModuleState、 pThreadState、theApp的存储关系
pModuleState->m_pCurrentWinApp(theApp)
pModuleState->m_thread(pThreadState)->m_pCurrentWinThread(theApp)
*/
pModuleState -> m_pCurrentWinAPP = this;
ASSERT(AfxGetAPP() == this);
//AfxGetApp的内部执行过程见下面
}
AfxGetThread内部执行过程
AfxGetThread()
{
//获取全局变量 AFX_MODULE_THREAD_STATE* pThreadState 当前程序线程状态信息
AFX_MODULE_THREAD_STATE* pState = AfxGetModutateThreadState();
CWinThread* pThread = pState -> m_pCurrentWinThread; //这是theApp;
return pThread; //返回的是theApp的地址
}
AfxGetApp的内部执行过程
//AfxGetApp的内部执行过程见下面
AfxGetAPP()
{
//afxCurrentWinApp是一个宏指代着AfxGetModutate()-> m_pCurrentWinApp; //返回theApp
return afxCurrentWinApp;
}
总结:当程序启动时,构造theApp对象,调用父类CWInApp的构造函数。
主要功能是将theApp对象保存到两个MFC的全局变量中。
AFX_MODULE_STATE moduleState 当前程序模块状态信息
AFX_MODULE_THREAD_STATE* pThreadState 当前程序模块线程状态信息
补充: 综上所知,全局变量AFX_MODULE_STATE pModuleState 和 AFX_MODULE_THREAD_STATE* pModuleThread存在一一对应的关系,即pModuleThread->m_moduleState = pModuleState 、 pModuleState->m_thread = pModuleThread
通过调用堆栈获取InitInstance()的调用过程,找到MFC的main函数,MFC的main函数是_tWinMain(),它调用了AfxWinMain
AfxWinMain的执行过程
int AFXAPI AfxWinMain(HINSTANCE hInstance,HINSTANCE hPrevInstance,
_In_ LPTSTR lpCmdLine, int nCmdShow)
{
//首先获取了theApp pThread和pApp都是全局变量theApp
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
/*
由pApp->InitApplication()可知,InitApplication()是theApp的成员函数,且InitApplication()
是虚函数,我们没有对InitApplication()进行重写,因此它会执行父类的InitApplication()进行初始化
*/
if (pApp != NULL && !pApp->InitApplication()) goto InitFailure;
/*
接下来又对实例进行初始化,InitInstance()是theApp的成员函数,并且我们进行了重写,
因此执行我们的代码,在InitInstance()中,我们创建了Frame的对象并通过Frame创建了窗口,
并将窗口进行显示。最后我们还将Frame对象保存到theApp的成员属性m_pMainWnd中
*/
if (!pThread->InitInstance())
{
。。。。。。
}
//进行消息循环 该Run函数用了CWinThread::Run()函数 ,Run函数的执行在下面
nReturnCode = pThread->Run();
}
CWinThread::Run()内部执行过程
int CWinThread::Run()
{
//获取MFC中的第三个全局变量 该变量中存放着消息
AFX_THREAD_STATE* pState = AfxGetThreadState();
BOOL bIdle = TRUE;
for(;;)//死循环
{
//当消息队列中没有消息时进入循环
while(bIdle && !::PeekMessage(&(pState->msgCur),NULL,NULL,NULL,PM_NOREMOVE))
{
//OnIdle()用来进行空闲处理 它是theApp的成员虚函数,可以进行重写
if(this->OnIdle(this -> lIdleCount++)) this->bIdle = FALSE;
}
do
{
//当PumpMessage()返回false时就可以退出死循环,就是当GetMessage获取到WM_QUIT时才会退出
if(!PumpMessage()) return ExitInstance(); //程序结束之前进行一些处理
// PumpMessage()内部直接调用了全局函数AfxInternalPumMessage();
// AfxInternalPumMessage();的内部执行见下面
}while(::PeekMessage(&(pState->msgCur),NULL,NULL,NULL,PM_NOREMOVE));//有消息时不断循环
}
}
AfxInternalPumMessage();的内部执行
BOOL AfxInternalPumMessage()
{
// GetMessage获得WM_QUIT消息时才会进入
if(!::GetMessage(&(pState->msgCur),NULL,NULL,NULL))
{
return FALSE;
}
if(pState->m_msgCur.message != WM_KICKIDLE && !AfxPreTranslateMessae(&(pState->m_msgCur)))
{
//消息翻译
::TranslateMessage(&(pState->m_msgCur));
//消息分发
::DispatchMessage(&(pState->m_msgCur));
}
return true;
}
总结:进入入口函数WinMain
首先获取应用程序类对象theApp,
利用theApp调用InitApplication,初始化当前应用程序的数据
利用theApp调用InitInstance初始化程序,在函数中我们创建窗口并显示
利用theApp调用CWinApp的Run函数进行消息循环
如果没用消息,利用theApp调用OnIdle虚函数进行空闲处理
程序退出利用theApp地址调用ExitInstance虚函数实现退出前的善后处理工作
通过以上可以知道theApp控制着程序的流程
通过以上分析theApp作为MFC的全局变量,它控制的程序执行的流程,通过调用Win32的API来实现窗口创建的流程,但是在本节我们只看到了消息循环的实现。其实注册窗口、创建窗口、显示窗口以及窗口的消息处理函数都在theApp的成员函数中InitInstance中的pFrame->Create(NULL,_T(“MFCBase”));实现,它的具体实现,请关注我的下一篇文章MFC的窗口创建机制。