1. 程式人生 > >MFC六大核心機制——初始化

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的類的物件。