从上一节的Win32程序与MFC程序的对比中可以看到,MFC程序中可运行代码只有MyApp::InitInstance()和MyFrame::MyFrame(),可以想到MFC程序一定会调用此两个函数,如果你手中有一个好的集成开发环境(如果没有,那你最好去安装一个^_^),可以在这两个函数中设置断点,然后运行程序,程序首先会在InitInstance函数中中断,此时查看程序调用堆栈,可以看到如下图所示的函数调用顺序:
嗯!我们看到堆栈中有Win32程序入口函数WinMain,说明MFC已经为我们定义了WinMain函数,MFC程序也是从WinMain函数开始运行。
然后进入函数WinMain发现其只是调用了AfxWinMain函数,其代码如下:
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
__in LPTSTR lpCmdLine, int nCmdShow)
{
ASSERT(hPrevInstance == NULL);
int nReturnCode = -1;
CWinThread* pThread = AfxGetThread();
CWinApp* pApp = AfxGetApp();
// AFX internal initialization
if (!AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow))
goto InitFailure;
// App global initializations (rare)
if (pApp != NULL && !pApp->InitApplication())
goto InitFailure;
// Perform specific initializations
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;
}
nReturnCode = pThread->Run();
InitFailure:
#ifdef _DEBUG
// Check for missing AfxLockTempMap calls
if (AfxGetModuleThreadState()->m_nTempMapLock != 0)
{
TRACE(traceAppMsg, 0, "Warning: Temp map lock count non-zero (%ld).\n",
AfxGetModuleThreadState()->m_nTempMapLock);
}
AfxLockTempMaps();
AfxUnlockTempMaps(-1);
#endif
AfxWinTerm();
return nReturnCode;
}
主要的函数调用过程如下:
AfxWinInit(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
pApp->InitApplication();
pThread->InitInstance();
pThread->Run();
AfxWinTerm();
这里先不对每个调用进行说明,只用记住这里有这些过程调用。
这部分里面就有pThread->InitInstance()调用,可以知道pThread一定是指向MyApp对象的(CWinThread是CWinApp的基类,CWinApp是MyApp的基类)。
这里顺便说明下MFC的类层次结构:
CObject
|-CCmdTarget
|-CWinThread
| |-CWinApp
|
|-CWnd
| |-CFrameWnd
| |-Cview
| |-CDialog
|
|-CDocument
|
|-CDocTemplate
|-CSingleDocTemplate
|-CMultiDocTemplate
在InitInstance中我们创建了一个MyFrame对象赋值给m_pMainWindow指针,之后显示更新窗口,我们有理由猜测注册窗口类别和创建Windows窗口对象的过程是在创建MyFrame对象时完成的,下面深入MyFrame的创建过程。
还是在集成环境中执行进入MyFrame的创建过程,最终可以看到如下图所示的调用堆栈:
可以看到依次调用CFrameWnd::Create,CWnd::CreateEx,CFrameWnd::PreCreateWindow,AfxEndDefRegisterClass,_AfxRegisterWithIcon函数,_AfxRegisterWithIcon代码如下:
AFX_STATIC BOOL AFXAPI _AfxRegisterWithIcon(WNDCLASS* pWndCls,
LPCTSTR lpszClassName, UINT nIDIcon)
{
pWndCls->lpszClassName = lpszClassName;
HINSTANCE hInst = AfxFindResourceHandle(
ATL_MAKEINTRESOURCE(nIDIcon), ATL_RT_GROUP_ICON);
if ((pWndCls->hIcon = ::LoadIcon(hInst, ATL_MAKEINTRESOURCE(nIDIcon))) == NULL)
{
// use default icon
pWndCls->hIcon = ::LoadIcon(NULL, IDI_APPLICATION);
}
return AfxRegisterClass(pWndCls);
}
上面的代码中就有AfxRegisterClass函数调用,此函数又调用了::AfxCtxRegisterClass,这个函数是用宏定义的,调用了RegisterClass函数注册窗口。
定义的宏如下:AFX_ISOLATIONAWARE_STATICLINK_FUNC(ATOM ,RegisterClassW,(const WNDCLASSW*lpWndClass),(lpWndClass),0)
书中写的是在AfxRegisterClass函数中直接调用了::RegisterClass,和现在分析的并不一样,可能是版本更新后的调整。
现在注册窗口类的代码已经找到,下面是创建Windows对象的过程。
通过上面的调用堆栈进入CWnd::CreateEx函数,在调用CFrameWnd::PreCreateWindow函数后有::AfxCtxCreateWindowEx调用,此函数和上面::AfxCtxRegisterClass一样是通过宏定义的,实际上就是调用了::CreateWindowEx,这样创建窗口的代码也找到了,接着看下窗口函数的指定。
在注册窗口前有调用AfxEndDeferRegisterClass,代码如下:
BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister)
{
// mask off all classes that are already registered
AFX_MODULE_STATE* pModuleState = AfxGetModuleState();
fToRegister &= ~pModuleState->m_fRegisteredClasses;
if (fToRegister == 0)
return TRUE;
LONG fRegisteredClasses = 0;
// common initialization
WNDCLASS wndcls;
memset(&wndcls, 0, sizeof(WNDCLASS)); // start with NULL defaults
wndcls.lpfnWndProc = DefWindowProc;
wndcls.hInstance = AfxGetInstanceHandle();
wndcls.hCursor = afxData.hcurArrow;
...
}
嗯?上面用的窗口函数竟然是DefWindowProc!这其中的玄机等学习MFC的消息处理时再来细说。
现在我们再MFC程序中也找的WinMain入口函数,在窗口类中设置了窗口处理函数,注册窗口类RegisterClass函数,创建窗口CreateWindowEx函数,显示窗口函数ShowWindow以及更新窗口函数UpdateWindow,在第一节的问题中只剩下消息循环的启动了。
我们回到AfxWinMain函数中,在pThread->InitInstance后可以看到调用了pThread->Run函数,深入此函数,看见调用堆栈如下:
栈顶函数AfxInternalPumpMessage代码如下:
BOOL AFXAPI AfxInternalPumpMessage()
{
_AFX_THREAD_STATE *pState = AfxGetThreadState();
if (!::GetMessage(&(pState->m_msgCur), NULL, NULL, NULL))
{
// Note: prevents calling message loop things in 'ExitInstance'
// will never be decremented
return FALSE;
}
// process this message
if (pState->m_msgCur.message != WM_KICKIDLE && !AfxPreTranslateMessage(&(pState->m_msgCur)))
{
::TranslateMessage(&(pState->m_msgCur));
::DispatchMessage(&(pState->m_msgCur));
}
return TRUE;
}
可以看到上面的代码中就有Win32程序进行消息提取的调用过程,循环在CWinThread::Run中进行。
对我们MFC的程序执行过程作一个总结:
从WinMain开始,调用AfxWinMain,调用重载的MyApp::InitInstance,创建MyFrame对象,调用CFrameWnd::Create,调用CWnd::CreateEx,调用CFrameWnd::PreCreateWindow,调用::RegisterClass注册窗口,调用::CreateWindowEx创建窗口,调用CWinApp::Run,调用CWinThread::Run(循环),调用::GetMessage获取消息进行处理。以上除了全局函数外,其他函数都可以在MyFrame和MyApp进行重写从而修改程序的执行。
至此,我们已经在MFC的代码中找到了Win32程序的各个必要的组成部分,但是对消息处理部分还不甚明了,接着来详细分析下MFC的消息处理过程。