vc++深入跟蹤MFC程序的執行流程


在MFC程序設計的學習過程中最令人感到難受,甚至於有時會動搖學習者信心的就是一種對於程序的一切細節都沒有控制權的感覺。這種感覺來源於學習者不知道一個MFC程序是如何運行起來的(即一個MFC程序的執行流程)和MFC程序的設計思想和機制,即使是寫過Windows程序的學習者,也會感到非常迷惘並且無從下手。而這種感覺的出現會使大家認為自己離開了書本上的例子就無法設計編制程序。下面我就來說一說一個MFC具體是如何被執行的。在閱讀本文之前,你要有一定的Windows程序設計基礎,知道Windows程序的運行流程,如不清楚,可先看看我寫的這篇文章——解說一個簡單的Win32程序。
 
一、單文檔項目特點簡述
以一個在VS2010中建立的一個單文檔MFC程序來例子,深入跟蹤MFC執行流程。工程名為MFCSDI,工程建立步驟不在這樣詳述。
 
當一個SDI(單文檔)程序建立之后,我們會看到程序為我們生成了四個類:CAboutDlg、CChildView、CMainFrame、CMFCSDIApp。在它們的頭文件中可以看到CCAboutDlg是從CDialogEx類派生出來的,用來顯示一個對話框窗口,該對話框用來顯示與此程序相關的版本信息。CMainFrame由類CFrameWndEx派生而來,用為表示一個程序的框架。CHildView類由類CWnd派生而來,用於單文檔程序的顯示。CMFCSDIApp類由類CWinAppEx派生而來,用於表示一個MFC程序,在每一個MFC程序中都有一個C+工程名+App的類(本例子中為CMFCSDIApp),它定義了一個全局對象theApp,它是一個應用程序對象,它就代表着這個程序。一個MFC程序有且只有一個這樣的從CWinAppEx派生出來的類,也有且僅有一個從從CWinAppEx派生出來的類(如這里的CMFCSDIApp)所實例化的對象。
 
由此可見,這個MFC單文檔程序並不像之前所說的Win32程序那樣有一條清晰的主線。一個Windows程序從WinMain函數開始,經過注冊窗口類、創建窗口、顯示和刷新窗口才使得該程序的窗口界面為用戶可見,然后建立進行消息循環,用戶對此界面所作的任何操作都會被Windows作為消息傳遞給程序的窗口函數,並由窗口函數對消息進行分類處理,這些工作都是被 WinMain函數獨自包辦的。但在MFC程序中WinMain函數的地位被CWinApp類取代了,它所負責的全部初始化工作和對消息解釋及分派都有 CWinApp類的內部函數來完成,但是WinMain仍然存在,並且扮演着駕馭CWinApp的角色。但我們在生成的所有文件的代碼中,也找不到WinMain函數。而且這幾個類之間是通過什么聯系起來,組成一個Windows程序的呢?
 
二、在WinMain執行前初始化的全局變量theApp
前面說過,theApp是一個應用程序對象,它就代表着這個程序。一個MFC程序有且只有一個這樣的從CWinAppEx派生出來的類,也有且僅有一個從從CWinAppEx派生出來的類(如這里的CMFCSDIApp)所實例化的對象。因為它是一個全局變量,根據C++的特點,它可以在WinMain函數執行前進行自己的初始化。
 
所以,要構造theApp對象就要調用其構造函數,由於CMFCSDIApp的基類為CWinAppEx,CWinAppEx的基類為CWinApp,由於要構造子類,就要先構造父類,即要構造theApp對象就要先調用CWinApp的構造函數來構造父類,CWinApp的構造函數對theApp的一些參數作初始化。
 
三、調用WinMain函數
構造完theApp這個全局對象后,就進入WinMian函數,它的代碼在mfc代碼所在目錄下的appmodul.cpp文件中,這個函數名為_tWinMain,咋一看與我們在Win32所用的WinMain函數的名字不一樣,其實_tWinMain是一個宏,到它的定義處一看,就知道它代表的正是WinMain,它的寫法與我們在Win32程序中的WinMain函數是一樣的。這個_tWinMain會調用一個函數AfxWinMain,這個函數在文件winmain.cpp中定義,而這個函數會有一條語句pThread->InitInstance(),pThread是一個窗口線程的指針,它的值由函數AfxGetThread()所得,根據多態性的原理,pThread會獲得一個指向子類的指針,所以它會調用CMFCSDIApp類的成員函數CMFCSDIApp::InitInstance(),這個函數會初始化一些程序運行所需要的資源。
 
四、注冊窗口
初始化一些所需的資源之后,就要對窗口類進行注冊。MFC會調用函數AfxEndDeferRegisterClass注冊窗口類,該函數的定義在文件wincore.cpp中。在Win32中時,我們需要設計一個窗口類,但是MFC已為我們設計好一個默認的窗口類,這里我們進行注冊就行。
 
五、產生窗口
注冊完窗口類之后,就會調用CMainFrm類的成員函數CMainFrm::PreCreateWindow來創建窗口,而這個函數會去調用它的父類的成員函數CFrameWnd::PreCreateWindow來創建窗口,在這個函數中可以對一些MFC設計好的默認的窗口類作一些修改,然后會調用AfxEndDeferRegisterClass函數進行窗口類的注冊。之后,就會調用CFrameWnd::Create函數進行窗口的創建,該函數的定義在文件winfrm.cpp中,而該在窗口創建過程中該函數又會調用CWnd::CreateEx函數來對窗口進行創建,CWnd::CreateEx定義在文件wincore.cpp中。窗口創建完成后,會調用ShowWindow函數和UpdateWindow函數顯示窗口,這兩個函數在函數CMFCSDIApp::InitInstance()中被調用(在文件MFCSDI.cpp中)。
 
在一個程序中可以見到PreCreateWindow會被調用很多次,這是因為我們產生一個程序時會注冊很多個窗口,如工具欄,按鈕等,每創建一個窗口都要調用AfxEndDeferRegisterClass函數來進行窗口類注冊,所以這兩個函數就被多次地調用了。
 
六、建立消息循環
回到一開始所說的AfxWinMain函數中,里面有一條語句nReturnCode = pThread->Run();其實這就是建立消息循環。在文件thrdcore.cpp中可以找到它的定義(CWinThread::Run),它會循環調用函數PumpMessage(同樣定義在文件thrdcore.cpp中),PumpMessage函數又會調用函數AfxInternalPumpMessage(在文件thrdcore.cpp中),它會調用函數GetMessage,它就等同於我們Win32程序中的函數GetMessage,然后調用函數::TranslateMessage,::DispatchMessage這與我們所寫的Win32程序是一致的。
 
七、窗口過程
MFC在窗口類注冊時就給它指定了一個默認的窗口過程,在函數AfxEndDeferRegisterClass(wincore.cpp)中有如下語句,wndcls.lpfnWndProc = DefWindowProc;把窗口過程指定為默認的窗口過程,而MFC則會通過消息映射轉換處理過程,使我們的程序能響應不同的消息。
 
八、窗口的銷毀
MFC程序的死亡相對於初生來說要簡單的多,主要是以下幾步:
  1.使用者通過點擊File/Close或程序窗口由上角的叉號發出WM_CLOSE消息。
  2.程序沒有設置WM_CLOSE處理程序,交給默認處理程序。
  3.默認處理函數對於WM_CLOSE的處理方式為調用::DestoryWindow,並因而發出WM_DESTORY消息。
  4.默認的WM_DESTORY處理方式為調用::PostQuitMessage,發出WM_QUIT。
  5.CWinApp::Run收到WM_QUIT后結束內部消息循環,並調用ExinInstance函數,它是CWinApp的一個虛擬函數,可以由用戶重載。
  6.最后回到AfxWinMain,執行AfxWinTerm,結束程序。
 
本文中,我用到的文件的路徑如下,寫出來給大家參考一下,大家可以找到這些函數,設置斷點,調試運行看一看程序的具體執行流程。
 
D:\ProgramFiles\Microsoft Visual Studio 10.0\VC\atlmfc\src\mfc\appmodul.cpp
D:\ProgramFiles\Microsoft Visual Studio 10.0\VC\atlmfc\src\mfc\winmain.cpp
D:\ProgramFiles\Microsoft Visual Studio 10.0\VC\atlmfc\src\mfc\winfrm.cpp
D:\ProgramFiles\Microsoft Visual Studio 10.0\VC\atlmfc\src\mfc\appcore.cpp
D:\ProgramFiles\Microsoft Visual Studio 10.0\VC\atlmfc\src\mfc\wincore.cpp
D:\ProgramFiles\Microsoft Visual Studio 10.0\VC\atlmfc\src\mfc\thrdcore.cpp

本文來源於編程啟航吧
原文地址:http://www.prm8.com/a/bianchengjingyan/vc/1346/


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM