摘要:本文章主要以MFC程序的執(zhí)行流程、執(zhí)行順序等執(zhí)行過(guò)程的剖析做出的結(jié)論,下面一起來(lái)看看原文的具體介紹。
一、MFC介紹
微軟基礎(chǔ)類庫(kù)(英語(yǔ):Microsoft Foundation Classes,簡(jiǎn)稱MFC)是微軟公司提供的一個(gè)類庫(kù)(class libraries),以C++類的形式封裝了Windows API,并且包含一個(gè)應(yīng)用程序框架,以減少應(yīng)用程序開(kāi)發(fā)人員的工作量。其中包含大量Windows句柄封裝類和很多Windows的內(nèi)建控件和組件的封裝類。
二、MFC程序執(zhí)行過(guò)程剖析
1)我們知道在WIN32API程序當(dāng)中,程序的入口為WinMain函數(shù),在這個(gè)函數(shù)當(dāng)中我們完成注冊(cè)窗口類,創(chuàng)建窗口,進(jìn)入消息循環(huán),最后由操作系統(tǒng)根據(jù)發(fā)送到程序窗口的消息調(diào)用程序的窗口函數(shù)。而在MFC程序當(dāng)中我們不在能找到類似WinMain這樣的程序入口,取而代之的是一系列派生類的聲明和定義以及一個(gè)沖CWinApp類派生而來(lái)的類的全局對(duì)象。CWinApp類被稱之為應(yīng)用程序?qū)ο螅谝粋€(gè)MFC程序當(dāng)中只允許有一個(gè)應(yīng)用程序?qū)ο蟆S捎贑WinApp的派生對(duì)象是全局的,因此這個(gè)對(duì)象的構(gòu)造函數(shù)會(huì)在所有的其他代碼運(yùn)行之前被調(diào)用,而由于CWinApp類當(dāng)中包含了HWND、HINSTANCE等句柄的存在,其構(gòu)造函數(shù)就執(zhí)行了對(duì)這些成員數(shù)據(jù)的初始化操作,這里的所謂初始化僅僅是把所有的句柄對(duì)象賦值為NULL。
2)在調(diào)用完CWinApp的構(gòu)造函數(shù)以后由連接器向程序內(nèi)自動(dòng)鏈接的AfxWinMain函數(shù)將被調(diào)用,而這個(gè)函數(shù)可以被看作MFC程序的入口函數(shù)。在這個(gè)函數(shù)當(dāng)中調(diào)用全局AfxGetApp()函數(shù)獲得應(yīng)用程序?qū)ο螅@時(shí)將調(diào)用AfxInit全局函數(shù),這個(gè)函數(shù)的功能是使用操作系統(tǒng)傳遞給AfxWinMain函數(shù)的參數(shù)初始化應(yīng)用程序?qū)ο螽?dāng)中的相關(guān)句柄數(shù)據(jù)成員。
3)之后AfxWinMain函數(shù)調(diào)用CWinApp::InitApplication成員函數(shù),這個(gè)成員函數(shù)用來(lái)初始化應(yīng)用程序?qū)ο螽?dāng)中的關(guān)于文檔部分的內(nèi)容。
4)隨后調(diào)用CWinApp::InitInstance成員函數(shù),在這個(gè)成員函數(shù)當(dāng)中,使用new操作在堆上聲明一個(gè)框架窗口對(duì)象,由此導(dǎo)致框架窗口對(duì)象的構(gòu)造函數(shù)被調(diào)用,在框架窗口構(gòu)造函數(shù)當(dāng)中調(diào)用Create函數(shù)來(lái)創(chuàng)建窗口,而調(diào)用的Create函數(shù)一般將WNDCLASS參數(shù)設(shè)置成NULL,這樣就由MFC內(nèi)部調(diào)用PreCreateWindow函數(shù),在這個(gè)函數(shù)當(dāng)中由MFC注冊(cè)幾個(gè)默認(rèn)的WNDCLASS供框架窗口的Create使用。這時(shí)程序控制權(quán)交還給CWinApp::InitInstance成員函數(shù)內(nèi)部,由這個(gè)函數(shù)調(diào)用CWnd::ShowWindow顯示窗口并且調(diào)用CWnd::UpdateWindow向窗口發(fā)送WM_PAINT消息。調(diào)用完CWinApp::InitInstance成員函數(shù)后由AfxWinMain函數(shù)調(diào)用CWinApp::Run成員函數(shù),并由這個(gè)函數(shù)來(lái)創(chuàng)建和處理消息循環(huán),并且在沒(méi)有消息的時(shí)候處理OnIdle空閑處理。至此整個(gè)程序的創(chuàng)建過(guò)程完成。
5)在程序的運(yùn)行過(guò)程當(dāng)中,由操作系統(tǒng)源源不斷的發(fā)送消息給應(yīng)用程序,并且由CWinApp::Run當(dāng)中的消息循環(huán)處理并且分發(fā)給相關(guān)的窗口對(duì)象的DefWindowProc成員函數(shù),并由這個(gè)成員函數(shù)查詢窗口對(duì)象的消息映射表,如果查到對(duì)應(yīng)項(xiàng),則由登記在消息映射表當(dāng)中的類成員函數(shù)處理,否則則按照Message Route當(dāng)中的順序象父層類發(fā)送。
6)在消息運(yùn)行結(jié)束,用戶按下關(guān)閉按鈕后,操作系統(tǒng)向程序發(fā)送WM_CLOSE消息,默認(rèn)狀況下程序調(diào)用DestoryWindow并且發(fā)送WM_DESTORY消息,應(yīng)用程序接受到這個(gè)消息以后的默認(rèn)操作是調(diào)用PostQuitMessage函數(shù),由這個(gè)函數(shù)發(fā)送WM_QUIT消息。當(dāng)程序?qū)ο蠼邮艿絎M_QUIT消息后消息循環(huán)結(jié)束,由AfxWinMain函數(shù)調(diào)用AfxTerm函數(shù)清理程序使用過(guò)的資源并且結(jié)束整個(gè)程序。
三、MFC程序的執(zhí)行順序
MFC只是對(duì)WIN32的API進(jìn)行了封裝,所以MFC的本質(zhì)還是WIN32程序。有了這層封裝,我們看不到WIN32的WinMain函數(shù),也就不清楚MFC程序的啟動(dòng)過(guò)程。雖然我們沒(méi)有看到WinMain函數(shù),但不代表沒(méi)有WinMain函數(shù),這個(gè)函數(shù)位于*\VC\atlmfc\src\mfc目錄的appmodul .cpp文件中有一個(gè)_tWinMain函數(shù), _tWinMain函數(shù)調(diào)用了WinMain.CPP文件中的AfxWinMain函數(shù)。
tWinMain函數(shù)實(shí)現(xiàn):
extern “C” int WINAPI
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
__in LPTSTR lpCmdLine, int nCmdShow)
{
// call shared/exported WinMain
return AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow);
}1234567
AfxWinMain函數(shù)實(shí)現(xiàn):
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))
// 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;
}123456789101112131415161718192021222324252627282930313233343536373839404142434445
_tWinMain和WinMain的聲明是一致的,但是_tWinMain不是最先執(zhí)行的,因?yàn)檎麄€(gè)程序一開(kāi)始是初始化全局變量,這里的全局變量有WinApp類型的theApp,初始化theApp就是執(zhí)行CWinApp的構(gòu)造函數(shù)。在這個(gè)AfxWinMain函數(shù)里面調(diào)用了pThread-》InitInstance()函數(shù),InitInstance函數(shù)是CWinApp類(CWinApp繼承于CWinThread)的虛函數(shù),所以這里就調(diào)用派生類的InitInstance函數(shù),用戶一般在這個(gè)函數(shù)里面創(chuàng)建對(duì)話框,單文檔,多文檔界面。執(zhí)行完InitInstance函數(shù)后,就會(huì)執(zhí)行CWinApp類的run函數(shù)(*\VC\atlmfc\src\mfc\thrdcore.cpp),這個(gè)函數(shù)一個(gè)死循環(huán),不斷地從的消息隊(duì)列中讀取消息。代碼如下:
int CWinThread::Run()
{
ASSERT_VALID(this);
_AFX_THREAD_STATE* pState = AfxGetThreadState();
// for tracking the idle time state
BOOL bIdle = TRUE;
LONG lIdleCount = 0;
// acquire and dispatch messages until a WM_QUIT message is received.
for (;;)
{
// phase1: check to see if we can do idle work
while (bIdle &&
!::PeekMessage(&(pState-》m_msgCur), NULL, NULL, NULL, PM_NOREMOVE))
{
// call OnIdle while in bIdle state
if (!OnIdle(lIdleCount++))
bIdle = FALSE; // assume “no idle” state
}
// phase2: pump messages while available
do
{
// pump message, but quit on WM_QUIT
if (!PumpMessage())
return ExitInstance();
// reset “no idle” state after pumping “normal” message
//if (IsIdleMessage(&m_msgCur))
if (IsIdleMessage(&(pState-》m_msgCur)))
{
bIdle = TRUE;
lIdleCount = 0;
}
} while (::PeekMessage(&(pState-》m_msgCur), NULL, NULL, NULL, PM_NOREMOVE));
}
}123456789101112131415161718192021222324252627282930313233343536373839
小結(jié)
MFC程序的執(zhí)行順序和Win32的執(zhí)行順序完全一樣:初始化全局變量(theApp),執(zhí)行WinMain函數(shù)(AfxWinMain),創(chuàng)建及注冊(cè)窗口(在InitInstance函數(shù)中創(chuàng)建對(duì)話框,單文檔,多文檔窗口),消息循環(huán)(Run函數(shù))
四、MFC程序的執(zhí)行流程
對(duì)于理解MFC程序執(zhí)行流程,我覺(jué)得理解new 、虛擬函數(shù)調(diào)用、構(gòu)造和析構(gòu)函數(shù)調(diào)用(http://blog.csdn.net/misskissc/article/details/8549254)次序、類指標(biāo)結(jié)論幾個(gè)地方對(duì)此很有幫助。剛好在學(xué)習(xí)MFC程序的過(guò)程中也正好去學(xué)習(xí)了這幾個(gè)方面(或者說(shuō)是先去查詢式的學(xué)習(xí)了這幾個(gè)方面之后覺(jué)得對(duì)理解MFC程序執(zhí)行流程有幫助)。
1.new
new和malloc函數(shù)都可以為變量申請(qǐng)一段某種類型的動(dòng)態(tài)存儲(chǔ)空間,new在申請(qǐng)動(dòng)態(tài)空間外還會(huì)引發(fā)申請(qǐng)空間對(duì)象構(gòu)造函數(shù)的運(yùn)行。當(dāng)然,有new就需要在需要的地方有delete,這是一種必備的習(xí)慣。
2.虛擬函數(shù)的調(diào)用
使用派生類對(duì)象訪問(wèn)類的成員函數(shù)時(shí),若此函數(shù)為非虛擬函數(shù)時(shí),訪問(wèn)的函數(shù)都是基類成員函數(shù)的地址;若訪問(wèn)的成員函數(shù)為虛擬函數(shù)(前面加了virtual關(guān)鍵字)時(shí),若在派生類中沒(méi)有申明(修改)則調(diào)用的是基類(或者某派生類(當(dāng)此派生類改變了此虛擬函數(shù)時(shí)))中的函數(shù)地址,若在派生類修改了虛擬函數(shù),則調(diào)用的就是派生類的成員函數(shù)。
3.構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用次序
構(gòu)造函數(shù)和析構(gòu)函數(shù)的調(diào)用次序從一定程度上反應(yīng)出MFC程序執(zhí)行流程。建構(gòu)式(運(yùn)行應(yīng)用程序類的對(duì)象的構(gòu)造函數(shù))是早于程序運(yùn)行入口點(diǎn)的,即應(yīng)用程序類對(duì)象的構(gòu)造函數(shù)的運(yùn)行要比AfxWinMain函數(shù)的運(yùn)行要早。當(dāng)然,析構(gòu)函數(shù)是在相應(yīng)情況下(delete發(fā)生、局部運(yùn)行完畢、程序運(yùn)行結(jié)束時(shí))發(fā)生。而且構(gòu)造函數(shù)的運(yùn)行遵循一定的方式(構(gòu)造函數(shù)析構(gòu)函數(shù)調(diào)用次序),析構(gòu)函數(shù)的運(yùn)行與構(gòu)造函數(shù)的運(yùn)行相反。
4.類指標(biāo)相關(guān)結(jié)論
如果以一個(gè)基礎(chǔ)類別之指標(biāo)指向衍生類別之物件,那么該指標(biāo)只能呼叫基礎(chǔ)類所定義的函式。如果一個(gè)衍生類指標(biāo)指向一個(gè)基礎(chǔ)類別物件,必須先做明確的轉(zhuǎn)型動(dòng)作(對(duì)基礎(chǔ)類之物件取地址&對(duì)派生類指標(biāo)賦值)。這種做法很危險(xiǎn)。如果基礎(chǔ)類別和衍生類別都定義了相同名之成員函式,那么透過(guò)物件指標(biāo)呼叫成員函式時(shí),到底呼叫到哪一個(gè)函式,必須視該指標(biāo)原始性別而定,而不是視指標(biāo)實(shí)際所指之物件的性別而定。
五、簡(jiǎn)析MFC程序執(zhí)行流程
撇開(kāi)所有的頭文件中對(duì)類聲明、派生的代碼,我們到應(yīng)用程序類源代碼文件(CMy*.cpp)定義應(yīng)用程序類對(duì)象的地方,見(jiàn)圖1
圖1.《深入淺出 MFC》一書(shū)中的MFC程序執(zhí)行流程分析圖
MFC程序執(zhí)行流程跟標(biāo)號(hào)增大的方向一致:
當(dāng)定義應(yīng)用程序類對(duì)象時(shí),在程序進(jìn)入AfxWinMain()函數(shù)前,應(yīng)用程序類對(duì)象的構(gòu)造函數(shù)先運(yùn)行,構(gòu)造應(yīng)用程序類對(duì)象。
進(jìn)入程序運(yùn)行的入口點(diǎn)AfxWinMain()函數(shù)中,AfxGetApp()函數(shù)返回指向當(dāng)前應(yīng)用程序的單一的CWinApp對(duì)象的指針。根據(jù)類指標(biāo)相關(guān)結(jié)論可知,除了被修改的虛擬函式外,用指針pApp訪問(wèn)的函數(shù)都是訪問(wèn)的CWinApp中的成員函式。所以用pApp-》InitApplication() [及Run] 調(diào)用的函式為CWindApp::InitApplication() [及CWindApp::Run()] ;因?yàn)镮nitIntance()函數(shù)被改變,因此而pApp-》InitIntance()調(diào)用的卻是派生類中(假設(shè)為CMyWindApp)的函式(CMyWindApp::InitIntance()),而且InitIntance()函數(shù)內(nèi)的內(nèi)容會(huì)被自動(dòng)執(zhí)行,一般這里的代碼為跟生成窗口有關(guān)。
這里是關(guān)于初始化的函數(shù),作為初學(xué)者,這里可以先跳過(guò)。容日后究。
這就是在調(diào)用派生類CMyWindApp的成員函數(shù),雖然這個(gè)調(diào)用的語(yǔ)句我們看不到。這里涉及虛擬函數(shù)妙用的內(nèi)容哦,派生類中被修改的虛擬函數(shù)將會(huì)被調(diào)用。
(5-8語(yǔ)句)這是在對(duì)虛擬函數(shù)InitIntance()進(jìn)行改寫(xiě),在改寫(xiě)的過(guò)程中很可能依據(jù)函數(shù)的調(diào)用方式(虛擬函數(shù),非虛擬函數(shù)的調(diào)用方式)再調(diào)用其它的函數(shù)。其實(shí)函數(shù)的調(diào)用占MFC應(yīng)用程序執(zhí)行流程的很大一部分,對(duì)于函數(shù)的調(diào)用,這里添加了一些規(guī)則,如此就構(gòu)成了整個(gè)的運(yùn)行。
(9語(yǔ)句)pApp-》Run()就是檢測(cè)是否有消息發(fā)生的主消息循環(huán)事件函數(shù),當(dāng)有任何規(guī)定的事件發(fā)生它都會(huì)檢測(cè)到,并通過(guò)一定方式調(diào)用到相應(yīng)的函數(shù),具體內(nèi)容可以后深究。
這個(gè)過(guò)程只是在定義應(yīng)用程序類對(duì)象時(shí)MFC內(nèi)部機(jī)制的簡(jiǎn)單剖析,其實(shí)在接收到用戶信息、調(diào)用用響應(yīng)的事件函數(shù)(DECLARE_DYNAMIC/IMPLEMENT_DYNAMIC巨集)等這些過(guò)程都是很有研究?jī)r(jià)值的,對(duì)咱初學(xué)者來(lái)說(shuō)就先了解一個(gè)大概,最開(kāi)始就要把這些原理弄得很清楚還是很費(fèi)功夫的!如此,就找到了MFC程序入口及消息循環(huán)的基本原理了,至于MFC程序如何響應(yīng)事件及另外的一些重要性質(zhì)就讓我們?nèi)蘸笸诰虬伞?/p>
評(píng)論