学习MFC首先要知道的--程序执行顺序
MFC的程序執行順序
很多剛學MFC的人都會被MFC給弄的暈頭轉向。以前傳統的C語言中的main()不見了,window sdk api 中的WinMain()函數也不見了,到底用MFC編寫的程序是如何開始運行的呢?到底MFC有沒有遵從最基本的C++的標準呢?到底MFC的代碼運行的順序又是怎么樣的呢?那么多個文件,那么多函數,到底哪一個先運行,哪一個后運行,哪一個調用哪一個,哪一個又被哪一個調用(你看暈了吧?那么多“哪一個”^_^)?這里這么復雜,到底最真是的是怎么一回事呢?我開始學習的時候,也是一頭霧水,什么都不明白,但是為了能先學習一些其他的,我囫圇吞棗的看了過去,先學習了CDIALOG和CVIEW的一些用法,并能編出了一個很簡單的程序。
前幾天,上網的時候,看到好多人都在看《孫鑫vc++講座》視頻教程,好像大家反應還不錯,于是,我就去找個地方下載下來看了。剛好今天看到了MFC的運行機制,里面講到了MFC的運行順序。孫同學在視頻中是利用實例,利用斷點,然后不斷的進行調試運行,以實事(實事勝于雄辯啊!!!)告訴我們,MFC是如何開始運行的。下面,我就根據我看到的教程,和網上一些前輩整理出來的材料在整理:
重點:MFC運行機制 執行順序 各個函數用途以及調用順序
孫同學在視頻中反復說明的是:MFC的程序和C語言的程序,從執行原理上說,是完全一致的。
抓住這一點,那么對于理解MFC程序的運行機制也就相對于簡單了。
C中的main函數就相當于MFC中的WinMain函數。
感興趣的可以利用VC的斷點設置自己跟蹤下面講述的各個函數,就明白它的執行順序了。
一、C語言程序執行步驟
在C語言中,大約的步驟如下:
1, 全局變量內存分配 例子如下:
#include <iostream.h> int a=88; main(){ cout<<a<<endl; }如果我們在main前設置斷點,我們就會發現,在進入main之前,a就已經存在了。也就是說像a這樣的全局變量在進入main函數前已經創建,并初始化。
2, 進入main函數
二、MFC程序的運行步驟(主要是初始化)
打開一個MFC APPWizard(exe)工程,跟蹤其執行步驟,可以發現,是以下順序:
CXXApp中的全局變量定義(在WinMain()函數之前定義的全局變量)
CXXApp theApp;
調用CXXApp構造函數(當然,創建一個類,它首先會調用自己的構造函數,這時WinMain()還沒有運行呢,呵呵奇怪吧?跟上面例子的變量a,其實是差不多的。)
CXXApp ::CXXApp(){}
進入Winmain函數(_tWinMain為宏,值為WinMain)(這個函數不是我們自己寫的,而且是隱藏在一個比較隱蔽的文件里面,D:/Program Files/Microsoft Visual Studio/VC98/MFC/src/WINMAIN.CPP里面。)
_tWinMain(){} (如果你查看它的定義,#define _tWinMain WinMain,其實兩者是一樣的)
完成初始化工作:包括窗口類注冊、窗口產生、顯示和更新
pThread->InitInstance()
(由于InitInstance是虛函數,所以這次調用的是派生類的InitInstance()函數,也就是你能在theApp里面看到的那一個函數)
對于MFC程序,MainFrame,View,ToolBar,Controlbar等都是窗口,所以下面的窗口注冊與創建、顯示等要反復調用多次,一次對應一個窗口
注冊窗口類
AfxEndDeferRegisterClass()(相當于SDK里面的RegisterClass()函數)
創建窗口
CMainFrame::PreCreateWindow()//反復調用一次是給我們修改窗口屬性的機會
CFrameWnd::Create()
消息循環
PumpMessage()
補充1:
在MFC中,由于涉及到(窗口)類定義,所以定義全局變量的時候,需要進行更多的步驟。
全局變量涉及到類定義(類似于C中的類型定義)的話,那么需要遵循以下步驟(以MFC的窗口類為例,這是在SDK 里面經常用到的,有用api編寫過函數的,應該都知道)
- 設計一個窗口類
- 注冊窗口類
- 創建窗口
- 顯示及更新窗口
- 消息循環
補充2:本課涉及到MFC函數的源文件位置
根目錄
找到您安裝VC98下MFC的位置,比如我的機子上為:D:/Program Files/Microsoft Visual Studio/VC98/MFC。下面提供的就是相對路徑了。在安裝目錄下找到MFC文件夾下的SRC文件夾,SRC下是MFC源代碼。
1,尋找WinMain人口:
路徑:MFC|SRC|APPMODUL.CPP:
_tWinMain(HINSTANCE hInstance, HINSTANCE hPrevInstance,LPTSTR lpCmdLine, int nCmdShow) {// call shared/exported WinMainreturn AfxWinMain(hInstance, hPrevInstance, lpCmdLine, nCmdShow); }注意:(#define _tWinMain WinMain)
2,對于全局對象或全局變量來說,在程序運行即WINMAIN函數加載的時候,已經為全局對象或全局變量分配了內存和賦初值。(理解先運行構造函數CTEAPP:CTEAPP()再運行WinMain的關鍵)
所以:CTEApp theApp;->CTEApp ::CTEApp(){}->_tWinMain(){}
說明:每一個MFC程序,有且只有一個從WinApp類派生的類(應用程序類),也只有一個從應用程序類所事例化的對象,表示應用程序本身。在WIN32程序當中,表示應用程序是通過WINMAIN入口函數來表示的(通過一個應用程序的一個事例號這一個標識來表示的)。在基于MFC應用程序中,是通過產生一個應用程序對象,用它來唯一的表示了應用程序。
3,通過構造應用程序對象過程中調用基類CWinApp的構造函數,在CWinApp的構造函數中對程序包括運行時一些初始化工作完成了。
CWinApp構造函數:MFC|SRC|APPCORE.CPP
(在CWinApp類定義中, CWinApp(LPCTSTR lpszAppName = NULL); )
注意:CWinApp()函數中:
(this指向的是派生類CTEApp對象,即theApp)
調試:
CTEApp theApp;(->CTEApp ::CTEApp() ->
CWinApp::CWinApp() (先調用基類初始化函數)->
CTEApp ::CTEApp() ->
_tWinMain(){}(紅色箭頭表示依次運行的順序)
4,_tWinMain函數中通過調用AfxWinMain()函數來完成它要完成的功能。(Afx*前綴代表這是應用程序框架函數,是一些全局函數,應用程序框架是一套輔助生成應用程序的框架模型,把一些類做一些有機的集成,我們可根據這些類函數來設計自己的應用程序)。
AfxWinMain()函數路徑:MFC|SRC|WINMAIN.CPP:
在AfxWinMain()函數中:
說明:pApp存儲的是指向WinApp派生類對象(theApp)的指針。
//_AFXWIN_INLINE CWinApp* AFXAPI AfxGetApp() // { return afxCurrentWinApp; }調用pThread->InitInstance()
說明:pThread也指向theApp,由于基類中virtual BOOL InitApplication()定義為虛函數,所以調用pThread->InitInstance()時候,調用的是派生類CTEApp的InitInstance()函數。
nReturnCode = pThread->Run();
說明:pThread->Run()完成了消息循環。
5,注冊窗口類:AfxEndDeferRegisterClass();
AfxEndDeferRegisterClass()函數所在文件:MFC|SRC|APPCORE.CPP
說明:設計窗口類:在MFC中事先設計好了幾種缺省的窗口類,根據不同的應用程序的選擇,調用AfxEndDeferRegisterClass()函數注冊所選擇的窗口類。
調試:
//注冊所選擇的窗口類(出于文檔管理,注冊提前,正常的應在PreCreateWindow中進行注冊)
//之后進入創建窗口階段(以下再不做調試)
6,PreCreateWindow()://主要是注冊窗口類,提供這個函數主要是允許程序對窗口的參數進行多次的修改,而且這個函數也是反復調用的。
BOOL CMainFrame::PreCreateWindow(CREATESTRUCT& cs) {if( !CFrameWnd::PreCreateWindow(cs) )return FALSE;return TRUE; }說明:
CFrameWnd::PreCreateWindow()函數所在文件:MFC|SRC|WINFRM.CPP BOOL CFrameWnd::PreCreateWindow(CREATESTRUCT& cs) { if (cs.lpszClass == NULL) {VERIFY(AfxDeferRegisterClass(AFX_WNDFRAMEORVIEW_REG));//判斷AFX_WNDFRAMEORVIEW_REG型號窗口類是否注冊,如果沒有注冊則注冊cs.lpszClass = _afxWndFrameOrView; // COLOR_WINDOW background//把注冊后的窗口類名賦給cs.lpszClass }if ((cs.style & FWS_ADDTOTITLE) && afxData.bWin4)cs.style |= FWS_PREFIXTITLE;if (afxData.bWin4)cs.dwExStyle |= WS_EX_CLIENTEDGE;return TRUE; }其中:
virtual BOOL PreCreateWindow(CREATESTRUCT& cs);//PreCreateWindow()是個虛函數,如果子類有則調用子類的。這是虛函數的特性。
7,創建窗口:
Create()函數路徑:MFC|SRC|WINFRM.CPP:
說明:CreateWindowEx()函數與CREATESTRUCT結構體參數的對應關系,使我們在創建窗口之前通過可PreCreateWindow(cs)修改cs結構體成員來修改所要的窗口外觀。PreCreateWindow(cs))//是虛函數,如果子類有調用子類的。
HWND CreateWindowEx(DWORD dwExStyle, LPCTSTR lpClassName, LPCTSTR lpWindowName,DWORD dwStyle, int x, int y, int nWidth, int nHeight, HWND hWndParent, HMENU hMenu, HINSTANCE hInstance, LPVOID lpParam ); typedef struct tagCREATESTRUCT { // csLPVOID lpCreateParams;HINSTANCE hInstance;HMENU hMenu;HWND hwndParent;int cy;int cx;int y;int x;LONG style;LPCTSTR lpszName;LPCTSTR lpszClass;DWORD dwExStyle; } CREATESTRUCT;8,顯示和更新窗口:
CTEApp類,TEApp.cpp中
9,消息循環:
int AFXAPI AfxWinMain() { ... // Perform specific initializations if (!pThread->InitInstance()){...} //完成窗口初始化工作,完成窗口的注冊,完成窗口的創建,顯示和更新。 nReturnCode = pThread->Run(); //繼承基類Run()方法,調用CWinThread::Run()來完成消息循環 ... }CWinThread::Run()方法路徑:MFC|SRC|THRDCORE.CPP int CWinThread::Run() { ...// phase2: pump messages while availabledo//消息循環{// pump message, but quit on WM_QUITif (!PumpMessage())//取消息并處理return ExitInstance();...} while (::PeekMessage(&m_msgCur, NULL, NULL, NULL, PM_NOREMOVE)); ... }說明:
BOOL PeekMessage(,,,,)函數說明
The PeekMessage function checks a thread message queue for a message and places the message (if any) in the specified structure.
If a message is available, the return value is nonzero.
If no messages are available, the return value is zero.
9,文檔與視結構:
可以認為View類窗口是CMainFram類窗口的子窗口。
DOCument類是文檔類。
DOC-VIEW結構將數據本身與它的顯示分離開。
文檔類:數據的存儲,加載
視類:數據的顯示,修改
10,文檔類,視類,框架類的有機結合:
在CTEApp類CTEApp::InitInstance()函數中通過文檔模板將文檔類,視類,框架類的有機組織一起。
總結
以上是生活随笔為你收集整理的学习MFC首先要知道的--程序执行顺序的全部內容,希望文章能夠幫你解決所遇到的問題。
- 上一篇: WinXP下 扫雷程序逆向分析 --扫雷
- 下一篇: CALL注入--扫雷辅助(二)