前面已經知道,在一個windows程式中,winmain函數作為程式的入口函數,一個程式的是:設計視窗(給視窗設定相應的屬性)——>註冊視窗——>建立視窗——>顯示並且更新視窗——>訊息迴圈;
但是當建立一個MFC程式時候,並沒有看到winmain函數之類的東西,並且之前的程式架構與MFC也大有不同。現在,先來看看MFC中的程式架構以及程式運行機制吧。
由於MFC是對其SDK的封裝,所以程式運行邏輯方式應該是一樣的。
在MFC程式中,同樣還是有winmain函數參與,但是這個WinMain函數是在程式編譯串連時,由連結器將該函數連結到程式的。知道WinMain的存在後,那麼WinMain又是怎麼跟其他類組織在一起的呢?
(1)CxxxApp中,有一個theApp對象的全域對象,該對象就表示了應用程式本身。 對於全域對象theApp,它是在程式進入WinMain函數之前為其分配的空間,同時,定義其時候會調用它的建構函式。在程式中看到CxxxApp是CWinApp的子類,當調用CxxxApp的建構函式之前,會調用其父類的建構函式。程式的初始化在CWinApp建構函式完成。
(2)AfxWinMain函數:WinMain函數實際上是通過調用AfxWinMain函數來完成其功能的。AfxWinMain原始碼在MFC原始碼的WINMAIN.CPP檔案中。AfxWinMain首先通過調用AfxGetThread函數獲得一個CWinThread類型的指標,接著調用AfxGetApp函數獲得一個CWinApp類型的指標。由於CWinApp繼承自CWinThread,AfxGetThread函數實際上返回的是AfxGetApp函數的結果,因此上面所說的AfxGetThread函數獲得一個CWinThread類型的指標,接著調用AfxGetApp函數獲得一個CWinApp類型的指標所得到的兩個指標實際上是一致的。都指向theApp全域對象。
(3)InitInstance函數:接下來pThread和pApp調用了三個函數(在AfxWinMain所在的檔案WINMAIN.CPP中可以看到)。分別是pApp->InitApplication();
pThread->InitInstance(); pThread->Run();三個函數。這三個函數完成了程式所需要的幾個步驟:設計視窗類別,註冊視窗類別,顯示視窗類別,訊息迴圈,以及視窗過程函數。(通過進一步的跟進每個函數可以發現這三個函數最終需要調用跟win32中程式完成這些功能的函數的影子!)
設計和註冊視窗:MFC已經為我們預定義了一些預設的標準視窗類別,而註冊視窗類別是由AfxEndDeferRegisterClass函數完成。(AfxEndDeferRegisterClass函數首先獲得視窗類別的資訊,如果該視窗類別已經註冊,直接返回一個真值,否則,調用RegisterClass函數註冊該視窗類別。其與win32SDK中所使用的函數是一樣的啊!)
建立視窗:視窗的建立是由CWnd類中的CreateEx函數實現的,在MFC中,CFrameWnd類的Create函數內部調用了上述的CreateEx函數,而前者又是由CrameWnd中的LoadFrame函數調用!!!在CreateEx函數的實現過程中,調用了PreCreateWindow,而後者是一個虛函數,故此調用是調用子類的PreCreateWindow函數,之所以這樣是在視窗產生之前讓程式員能夠有機會修改視窗的外觀。參數類型是一個CREATESTRUCT結構體。
顯示與更新視窗:CxxxApp類中有一個m_pMainWnd的成員變數,是一個CWnd類型的指標,儲存了應用程式視窗的指標。通過它調用與SDK中一樣的ShowWindow與UpdateWindow函數完成任務。
訊息迴圈:前面已經提到過pThread->Run();這個函數就是實現了訊息迴圈。在這個函數中的for迴圈中,執行了我們認識的TransLateMessage與DisPatchMessage兩個函數;
視窗過程:在AfxEndDeferRegisterClass函數的來源程式中,其中有一行言謝眼熟的代碼:wndcls.lpfnwndProc = DefWindowProc;當然MFC程式並不是把所有的訊息都交給DefWindowProc視窗過程來處理。MFC中使用的是訊息映射機制!
一個MFC訊息響應函數在程式中有三處相關的資訊:函數原型,函數實現,用來關聯訊息和訊息響應函數的宏。(分別分布在標頭檔及源檔案中)
標頭檔中在兩個AFX_MSG 注釋宏之間是訊息響應函數的原型的聲明。源檔案中有兩處:(1)AFX_MSG_MAP注釋宏之間的訊息映射宏,通過這個宏把訊息與訊息響應函數關聯起來;(2)源檔案中訊息響應函數的實現代碼。
MFC中的訊息映射機制的實現方法:在每個能接收和處理訊息的類中,定義一個訊息和訊息的處理函數靜態對照表,即訊息映射表。在訊息映射表中,訊息與對應的訊息處理函數指標成對出現。某個類能處理的所有的訊息及其對應的訊息處理函數的地址都列在這個類所對應的靜態表中。當有訊息需要處理時,程式只要搜尋該訊息靜態表,查看錶中是否函數該訊息,就可以知道該內能夠處理此訊息。如果能,則依照靜態表找到對應的訊息處理函數。否則,將訊息傳給其父類。