程式設計學習筆記之MFC內部組織架構
MFC全稱是Microsoft Foundation Classes,意為微軟基礎類庫,是一個C++的類庫,裡面封裝了大量的windows API尤其是win32 API函式,因為我們在開發win32應用程式的時候,需要做很多重複的步驟,比如註冊視窗類、初始化視窗、創造視窗、更新視窗等,所以為了省卻不必要的麻煩,微軟將這些步驟打包封裝成一個框架,裡面有win32所必需的元件,這個“框架”就叫MFC。
MFC源於win32,要弄清它們之間的關係,我們首先要理解MFC object與windows object之間的區別
1):MFC object是由c++的CWnd或其派生類定義的,在程式執行時由類中的建構函式建立,然後在執行類中解構函式的時候銷燬。而windows object是window系統的一個內部資料結構例項,它由一個視窗控制代碼來引用,window系統為它分配系統資源,它是在MFC視窗建立之後建立的,一般由CWnd類中的Create函式所建立,得到的視窗控制代碼儲存在CWnd的m_hWnd成員變數中,它可以被一個程式或使用者的動作銷燬【呼叫相應的sdk系統函式】。
2):windows object是屬於低層的,而MFC object是高層的,後者封裝了前者大部分的功能,MFC object的使用者不需要直接應用windows object的控制代碼來使用win32 API,而是用對應的MFC object函式來代替。我們可以從MFC object那裡得到一個對應的windows object控制代碼,比如使用GetSafeHandle函式,也可以直接拿windows object裡的控制代碼來建立一個新的MFC object,如果使用MFC的FromHandle函式,得到的是一個臨時的MFC object物件,而如果使用的是Attach,則會得到一個永久性物件。
3):MFC object對系統的其它程序是不可見的,而windows object則是全域性性的,後者一旦建立,那麼它的控制代碼便可以被系統的其它程序所使用,比如,A程序如果想對B程序傳送訊息,便可以通過獲取B程序的視窗控制代碼來完成。
【宣告:以上內容“借鑑”自百科詞條,說是抄襲也不為過,笑。。。】
接下來我們談談如何建立一個簡單的MFC應用程式,本文以visual studio 2013為開發平臺,首先開啟vs2013,選擇檔案->新建->專案,接著選擇visual C++下的MFC應用程式,檔名稱我們取名叫test,點選下一步,在嚮導裡面我們選擇單文件,點選完成。至此,我們就生成了一個自己的MFC應用程式。按下F5會出現如此介面:
這個就是我們自己的MFC應用程式。
我們再來看一下它的組織架構:
我們可以看到,這裡面一共有5個類,其中test.cpp、testDoc.cpp與testView.cpp名字裡面的“test”都是根據我們生成MFC應用程式時所取的名字來命名的,而無論我們給專案取什麼名字,MainFrm.cpp和stdafx.cpp這兩個類的名字是不會變得!我們雙擊一下test.cpp類,進入它的定義,如圖:
我們發現,CtestApp是繼承自CWinApp的,並且在這裡【第54行】聲明瞭一個CtestApp型別的全域性變數theApp,這樣當我們按下F5執行的時候,程式會首先構造一個全域性變數theApp,然後因為構造子類例項會先執行父類的建構函式這個原理,程式再去執行CWinApp的建構函式【在CWinApp之前還繼承自CWinThread,然後CWinThread上面還有父類,因為關係太過複雜,這裡不再展開】,在這些都完成之後,然後才由MainCRTStartUp呼叫程式的主函式_tWinMain【這個_tWinMain是一個巨集定義,其本來面目就是WinMain】,再然後在這個主函式_tWinMain裡面,會呼叫一個AfxWinMain,AfxWinMain函式原始碼如下:
int AFXAPI AfxWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,
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)
{
TRACE0("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)
{
TRACE1("Warning: Temp map lock count non-zero (%ld).\n",
AfxGetModuleThreadState()->m_nTempMapLock);
}
AfxLockTempMaps();
AfxUnlockTempMaps(-1);
#endif
AfxWinTerm();
return nReturnCode;
}
注意第6行和第7行的程式碼,程式首先聲明瞭一個CWinThread型別的物件pThread,然後又聲明瞭一個CWinApp的物件pApp,並分別呼叫AfxGetThread與AfxGetApp函式為它們賦值【所有帶有字首Afx的函式,都表示這是一個全域性函式】,事實上,這兩個函式所返回的都是我們前面那個theApp物件的指標,即一個CtestApp型別的物件指標。瞭解這點之後,再來後面的程式碼就容易理解了,程式就是利用pThread與pApp這兩個物件,完成一個視窗類的設計、註冊、建立、顯示更新等步驟。先說註冊,在18行程式碼中,通過pThread->InitInstance()呼叫了CWinThread類中的方法InitInstance,其實這個InitInstance是一個虛擬函式,這就意味著會回到theApp的InitInstance中執行,在這裡面,它會利用一個叫AfxEndDeferRegisterClass()來設計註冊視窗類,MFC預先為我們設計了一些視窗類,然後在這裡根據我們的需要為我們註冊一下。接下來就是建立視窗了,那麼程式會在哪裡建立這個註冊好的視窗呢?答案是類CMainFrame中的PreCreateWindow(),再接著跳轉到父類CFrameWnd中PreCreateWindow方法中,利用裡面的Create呼叫CreateEx來建立視窗【用了兩個CreateEx函式互相呼叫,它們是過載的關係,即引數列表不同】。再接著就是顯示和更新視窗,分別通過theApp物件裡面的InitInstance函式呼叫來完成。最後的最後,就是我們的訊息迴圈,看第25行程式碼,通過呼叫pThread中的Run方法來實現對外部訊息的迴圈響應,因為pThread實際是一個指向theAPP物件的指標,而在theApp的實際型別是CtestApp,可是在這個型別的定義中,並沒有關於Run方法的定義,這是因為這個Run方法是若干個上層父類CWinThread中的虛擬函式,所以程式到這裡就回到了CWinThread類中的Run方法中,讓我們看一下Run方法的定義:
其中紅框內的函式呼叫,就是有關訊息路由等動作了。(未完待續)