MFC六大核心機制——初始化
本人也是新手初學MFC(從Win32過渡過來),總結一下我的理解分享給大家,希望能產生共鳴。
要求:會點C++,知道基本關鍵字和類;至少能大概看明白一個簡單的Win32應用程式。
先來手敲一個最簡單的MFC程式:
#include <afxwin.h> class CMyApp :public CWinApp { public: virtual BOOL InitInstance() { m_pMainWnd = new CFrameWnd(); ((CFrameWnd*)m_pMainWnd)->Create(NULL, _T("主視窗")); m_pMainWnd->ShowWindow(m_nCmdShow); m_pMainWnd->UpdateWindow(); return TRUE; } }; CMyApp theApp;
不可思議,這個就是一個基於MFC的Windows視窗程式。看看我們都幹了啥:從CWinApp派生了一個類,然後重寫了他的InitInstance函式。裡面new了一個CFrameWnd的物件賦值到m_pMainWnd。至於ShowWindow和UpdateWindow,如果我們有開發過基於SDK的Win32程式,應該都能理解。
不難推斷出,CWinApp和CFrameWnd是由MFC幫我們預定義好的類。Create,ShowWindow和UpdateWindow是CFrameWnd提供的成員函式,和等效的WindowsAPI幾乎差不多。這也是就是MFC,對WindowsAPI的封裝。
看到這裡,我們不禁要問:
1-WinMain函式哪裡去了?
2-是誰呼叫了我寫的InitInstance函式,它又怎麼知道應該呼叫theApp物件的InitInstance函式?
3-雖然能看到Create,ShowWindow之類的,但是好像沒有註冊視窗類?
4-還有訊息迴圈、視窗過程呢?
一個Windows程式中,這些都是必不可少的。但是MFC中,這些程式碼哪裡去了?
MFC中這些部分也是不可少的,只不過,MFC都已經幫我們寫了,就像做填空題一樣,每次都要乾的繁瑣的事情,MFC框架幫我們幹了,精要的地方,留給我們DIY。
上面留下的問題,來一個一個解決:
1-WinMain在MFC原始碼目錄appmodul.cpp檔案中,叫_tWinMain(這是一個用於自適應MBCS和Unicode的巨集)。
extern "C" int WINAPI _tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance, _In_ LPTSTR lpCmdLine, int nCmdShow) #pragma warning(suppress: 4985) { // call shared/exported WinMain return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); }
只是簡單的呼叫AfxWinMain(這裡我也不明白為什麼還要整出來一個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;
}
這裡看到了一個似曾相識的函式InitInstance,這就算是回答了第二個問題的前面半部分。那MFC是如何獲取到我們定義的theApp物件的呢?回頭繼續看上面的程式碼:CWinThread* pThread = AfxGetThread();,跟進這個函式:
CWinThread* AFXAPI AfxGetThread()
{
// check for current thread in module thread state
AFX_MODULE_THREAD_STATE* pState = AfxGetModuleThreadState();
CWinThread* pThread = pState->m_pCurrentWinThread;
return pThread;
}
再跟:
AFX_MODULE_THREAD_STATE* AFXAPI AfxGetModuleThreadState()
{
AFX_MODULE_THREAD_STATE* pResult=AfxGetModuleState()->m_thread.GetData();
ENSURE(pResult != NULL);
return pResult;
}
繼續跟:
AFX_MODULE_STATE* AFXAPI AfxGetModuleState()
{
_AFX_THREAD_STATE* pState = _afxThreadState;
ENSURE(pState);
AFX_MODULE_STATE* pResult;
if (pState->m_pModuleState != NULL)
{
// thread state's module state serves as override
pResult = pState->m_pModuleState;
}
else
{
// otherwise, use global app state
pResult = _afxBaseModuleState.GetData();
}
ENSURE(pResult != NULL);
return pResult;
}
到這裡,好像已經沒法跟了,不管是_afxthreadState還是_afxBaseModuleState貌似都是兩個全域性變量了。
想一想,這兩個全域性變數是何時被賦值的?我們一路從WinMain走來,沒發現賦值的地方(中途有呼叫了幾個函式,我可以先告訴大家,也並沒有在那些函式中賦值)。沒錯,是在WinMain函式之前賦值的,在CWinApp的建構函式,把theApp物件的指標儲存為全域性變量了,注意我們從CWinApp派生了CMyApp類。這也對應了CWinApp的特點:一個基於MFC的應用程式,必須有且只能有一個基於CWinApp的物件!所以即使是一個最簡單的MFC應用,也需要定義一個派生自CWinApp的類的物件。