1. 程式人生 > >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)工程,跟蹤其執行步驟,可以發現,是以下順序:

1)CXXApp中的全域性變數定義(在WinMain()函式之前定義的全域性變數)

CXXApp theApp;

2)呼叫CXXApp建構函式(當然,建立一個類,它首先會呼叫自己的建構函式,這時WinMain()還沒有執行呢,呵呵奇怪吧?跟上面例子的變數a,其實是差不多的。)

CXXApp ::CXXApp(){}

3)進入Winmain函式(_tWinMain為巨集,值為WinMain)(這個函式不是我們自己寫的,而且是隱藏在一個比較隱蔽的檔案裡面,D:/Program Files/Microsoft Visual Studio/VC98/MFC/src/WINMAIN.CPP裡面。)

_tWinMain(){} (如果你檢視它的定義,#define _tWinMain      WinMain,其實兩者是一樣的)

4)完成初始化工作:包括視窗類註冊、視窗產生、顯示和更新

pThread->InitInstance() (由於InitInstance是虛擬函式,所以這次呼叫的是派生類的InitInstance()函式,也就是你能在theApp裡面看到的那一個函式)

對於MFC程式,MainFrame,View,ToolBar,Controlbar等都是視窗,所以下面的視窗註冊與建立、顯示等要反覆呼叫多次,一次對應一個視窗

(1) 註冊視窗類

AfxEndDeferRegisterClass()(相當於SDK裡面的RegisterClass()函式)

(2)建立視窗

CMainFrame::PreCreateWindow()//反覆呼叫一次是給我們修改視窗屬性的機會

CFrameWnd::Create()

(3)        訊息迴圈

PumpMessage()

補充1

在MFC中,由於涉及到(視窗)類定義,所以定義全域性變數的時候,需要進行更多的步驟。

全域性變數涉及到類定義(類似於C中的型別定義)的話,那麼需要遵循以下步驟(以MFC的視窗類為例,這是在SDK 裡面經常用到的,有用api編寫過函式的,應該都知道)

1)   設計一個視窗類

2)   註冊視窗類

3)   建立視窗

4)   顯示及更新視窗

5)   訊息迴圈  

補充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 WinMain
return 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){...}//帶引數,而CTEApp建構函式沒有顯式向父類傳參,難道CWinApp()有預設引數 見下:
(在CWinApp類定義中, CWinApp(LPCTSTR lpszAppName = NULL); )
注意:CWinApp()函式中:
pThreadState->m_pCurrentWinThread = this;
pModuleState->m_pCurrentWinApp = this
this指向的是派生類CTEApp物件,即theApp
除錯:CTEApp theApp;(  ->    CTEApp ::CTEApp())->    CWinApp::CWinApp() (先呼叫基類初始化函式)   ->       CTEApp ::CTEApp()     ->      _tWinMain(){}(紅色箭頭表示依次執行的順序)

4,_tWinMain函式中通過呼叫AfxWinMain()函式來完成它要完成的功能。(Afx*字首代表這是應用程式框架函式,是一些全域性函式,應用程式框架是一套輔助生成應用程式的框架模型,把一些類做一些有機的整合,我們可根據這些類函式來設計自己的應用程式)。
AfxWinMain()函式路徑:MFC|SRC|WINMAIN.CPP:
在AfxWinMain()函式中:
CWinApp* pApp = AfxGetApp();
說明: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
BOOL AFXAPI AfxEndDeferRegisterClass(LONG fToRegister){...}
說明:設計視窗類:在MFC中事先設計好了幾種預設的視窗類,根據不同的應用程式的選擇,呼叫AfxEndDeferRegisterClass()函式註冊所選擇的視窗類。
除錯:CWinApp::CWinApp();->CTEApp theApp;(->  CTEApp ::CTEApp())-> CWinApp::CWinApp()  ->  CTEApp ::CTEApp()  ->   _tWinMain(){}//進入程式  
->   AfxWinMain();  ->   pApp->InitApplication();->    pThread->InitInstance()//父類InitInstance虛擬函式;->CTEApp::InitInstance()//子類實現函式; ->  AfxEndDeferRegisterClass(LONG fToRegister)//註冊所選擇的視窗類(出於文件管理,註冊提前,正常的應在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()是個虛擬函式,如果子類有則呼叫子類的。這是虛擬函式的特性。
#define VERIFY(f)           ASSERT(f)
#define AfxDeferRegisterClass(fClass) AfxEndDeferRegisterClass(fClass)
define AFX_WNDFRAMEORVIEW_REG           0x00008
const TCHAR _afxWndFrameOrView[] = AFX_WNDFRAMEORVIEW;//WINCORE.CPP檔案中,定義為全域性陣列。
//#define AFX_WNDFRAMEORVIEW   AFX_WNDCLASS("FrameOrView")

7,建立視窗:
Create()函式路徑:MFC|SRC|WINFRM.CPP:
CFrameWnd::Create(...){
...
CreateEx(...);//從父類繼承來的,呼叫CWnd::CreateEx().
...
}

CWnd::CreateEx()函式路徑:MFC|SRC|WINCORE.CPP
BOOL CWnd::CreateEx(...){
...
if (!PreCreateWindow(cs))//虛擬函式,如果子類有呼叫子類的。
{
   PostNcDestroy();
   return FALSE;
}
...
HWND hWnd = ::CreateWindowEx(cs.dwExStyle, cs.lpszClass,
   cs.lpszName, cs.style, cs.x, cs.y, cs.cx, cs.cy,
   cs.hwndParent, cs.hMenu, cs.hInstance, cs.lpCreateParams);

...
}
說明: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 { // cs 
     LPVOID     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中
m_pMainWnd->ShowWindow(SW_SHOW);//顯示視窗,m_pMainWnd指向框架視窗
m_pMainWnd->UpdateWindow();//更新視窗
說明:呼叫的順序
class CTEApp : public CWinApp{...}
class CWinApp : public CWinThread{...}
class CWinThread : public CCmdTarget
{
...
public:
CWnd* m_pMainWnd;
...
...
}

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 available
   do//訊息迴圈
   {
    // pump message, but quit on WM_QUIT
    if (!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.

/////////////////////////////////////////////////////////////
BOOL CWinThread::PumpMessage()
{
...
if (!::GetMessage(&m_msgCur, NULL, NULL, NULL))//取訊息
{...}
...
// process this message
if (m_msgCur.message != WM_KICKIDLE && !PreTranslateMessage(&m_msgCur))
{
   ::TranslateMessage(&m_msgCur);//進行訊息(如鍵盤訊息)轉換
   ::DispatchMessage(&m_msgCur);//分派訊息到視窗的回撥函式處理(實際上分派的訊息經過訊息對映,交由訊息響應函式進行處理。)
}
return TRUE;
}

9,文件與視結構:
可以認為View類視窗是CMainFram類視窗的子視窗。
DOCument類是文件類。
DOC-VIEW結構將資料本身與它的顯示分離開。
文件類:資料的儲存,載入
視類:資料的顯示,修改

10,文件類,視類,框架類的有機結合:
在CTEApp類CTEApp::InitInstance()函式中通過文件模板將文件類,視類,框架類的有機組織一起
...
CSingleDocTemplate* pDocTemplate;
pDocTemplate = new CSingleDocTemplate(
IDR_MAINFRAME,
RUNTIME_CLASS(CTEDoc),
RUNTIME_CLASS(CMainFrame),        // main SDI frame window
RUNTIME_CLASS(CTEView));
AddDocTemplate(pDocTemplate);//
增加到模板

摘自:http://blog.csdn.net/husongchao/article/details/5063786

相關推薦

MFC首先知道的--程式執行順序

很多剛學MFC的人都會被MFC給弄的暈頭轉向。以前傳統的C語言中的main()不見了,window sdk api 中的WinMain()函式也不見了,到底用MFC編寫的程式是如何開始執行的呢?到底MFC有沒有遵從最基本的C++的標準呢?到底MFC的程式碼執行的順序又是怎麼樣的呢?那麼多個檔案,那麼多函式,到

學Java開發,首先知道它能做什麽

開發工程師 修改 找到 家電 比較 動態網頁 app 網站 家裏   Java是什麽?可能不懂的人很難理解,那就從結果(也就是學了java到底能幹些什麽)來講解一下什麽是Java吧!   對於很多新手來說,剛開始接觸Java會很迷惘,不知道Java可以做什麽。   其實Ja

深入理解Java程式執行順序

下面將從一道阿里巴巴試題詳細分析Java程式執行順序。 阿里巴巴試題 public class Test { public static int k = 0; public static Test t1 = new Test("t1"); public static Test t

java程式執行順序理解

  public class StaticTest { public static int k = 0; public static StaticTest t1 = new StaticTest("t1"); public static StaticTest t2 =

真正瞭解WMS(倉庫管理系統),首先知道這些

WMS是Warehouse Management System的縮寫,翻譯即倉庫管理系統。通過WMS系統可以將入庫、出庫、調撥、盤點、批次管理、庫存預警等倉庫內的作業流程進行全方位的管理。應用WMS系統進行倉庫管理可以有效控制庫內的作業流程,對成本進行全面的掌控。同時WMS系統還可以同企業的其他管理

java中子類繼承父類程式執行順序問題

測試程式碼: package test; public class FatherTest { private String name; public FatherTest(){ System.out.println("--父類的無參建構函式--"); } public

Java:類檔案程式執行順序

只要按照這個步驟,遇到這一類問題就可以解決了。 1.類中所有屬性的預設值(一舉而成) 2. 父類靜態屬性初始化,靜態塊,靜態方法的宣告(按出現順序執行) 3. 子類靜態屬性初始化,靜態塊,靜態方法的宣告 (按出現順序執行) 4. 呼叫父類的構造方法, 首先父類的非靜態成員

深入瞭解類載入過程及Java程式執行順序

/** * @author chris * @date 2018/10/30 - 15:24 * 類載入執行順序: * 1.靜態屬性,靜態方法宣告,靜態塊。 * * 2.動態屬性,普通方法宣告,構造塊。 * * 3.構造方法。 * * 當new一個物件時,此時會呼叫構造方法,但是在呼叫構造方法之前,(除非被打

Go 程式執行順序

在一個 go 程式中通常包含:包、常量、變數、init()、main()等元素,如果同時存在多個包,包之間存在依賴關係,每個包中存在多個 init 函式,每個檔案中存在多個 init 函式,那麼問題來了,他們之間的執行順序是什麼樣的?通過本文我們來對它們之間的執行順序做盡可能詳盡的說明。如有不正之處,歡迎批評

python 程式執行順序

c++與c語言都有程式入口main函式 而python則是順序執行的指令碼語言,但是同樣的也有其自己的特定執行順序. 首先執行非函式定義和非類定義的無首行縮排的全域性變數定義 如有main,即: def main():    xxxxx if __name__ =

【轉】深入瞭解Java程式執行順序

Java中main方法,靜態,非靜態的執行順序詳解1 Java程式執行時,第一件事情就是試圖訪問main方法,因為main相等於程式的入口,如果沒有main方法,程式將無法啟動,main方法更是佔一個獨立的執行緒,找到main方法後,是不是就會執行mian方法塊裡的第一句話呢?答案是不一

Final和Static及程式執行順序

final:final可以用來修飾:類、屬性和方法用於修飾類:表示該類不可以被繼承用於修飾屬性:表示該屬性不可以被修改用於修飾方法:表示該方法不可以被重寫但是,final不可以用於修飾介面和抽象類 staticstatic可以用作修飾:類、屬性、方法、程式碼塊用於修飾類:普

java程式執行順序(main方法 靜態 非靜態 以及衍生出的部分定義)

Java程式執行時,第一件事情就是試圖訪問main方法,因為main相等於程式的入口,如果沒有main方法,程式將無法啟動,main方法更是佔一個獨立的執行緒。 下面是一段程式碼 幫助我們分析class A { public A() { System

UIViewController的生命週期及iOS程式執行順序

當一個檢視控制器被建立,並在螢幕上顯示的時候。 程式碼的執行順序 1、 alloc                                   建立物件,分配空間 2、init (initWithNibName) 初始化物件,初始化資料 3、loadView                    

執行緒池續:你必須知道執行緒池submit()實現原理之FutureTask!

![FutureTask思維導圖.png](https://img2020.cnblogs.com/other/799093/202006/799093-20200601101441232-1248184493.png) ### 前言 上一篇內容寫了`Java`中執行緒池的實現原理及原始碼分析,說好的是實

從矩陣乘法來看-O優化和ijk執行順序程式效能的影響

從矩陣乘法來看-O優化和ijk執行順序對程式效能的影響 根據計算矩陣乘積的c程式,主要想做想做兩件事情: 統計採用不同的優化選項編譯程式所用的時間,感受-O優化帶來的效能提升。 看看矩陣乘法中不同迴圈順序對程式效能的影響: 改變三重迴圈的順序,統

【高階網站製作】程式猿應該知道的六大SEO節點

  在高階網站製作時,我們就不應該盲目的去做一個自己喜歡的網站就行,我們還必須要做一個能夠讓搜尋引擎認可的網站,這樣在後期才能更好的做排名。然而,這些操作就要關係到SEO的問題上了,所以說在做網站建設的時候程式猿應該要知道的六大SEO節點!        一、連結統一   搜尋引擎

程式設計師,你首先得了解你的大腦!戒掉遊戲!

把大腦當做第三個人,他會不聽話,分泌多巴胺去玩遊戲。 讓你根本沒有心思去學習! 所以,瞭解和控制你的大腦是必須的! 幾年前,我在一次學術會議上碰到一位漂亮的女孩。交流之後,發現我們有著相似的興趣、相近的背景、隔得不遠。我們在會議期間聊的非常投機,會議之後也保持著聯絡,交往越來越多。彼時的我,

程式設計師穿西裝面試被當推銷趕出,想進阿里、騰訊知道的3件事

網際網路行業與很多行業最明顯的區別可能就在穿搭上了,拖鞋、大褲衩穿的這麼隨意去上班的大部分都是網際網路公司,而這些人中大部分都是程式設計師。很多剛畢業的學生第一次來面試都精心準備,西服領帶,在多數行業這是很正常的,但是如果去面試一家網際網路企業,穿的太正式的話就會比較尷尬了。 前幾天有個應

08-Python程式執行順序

大走向從上到下 1.分支 則進入不同的分支 場景舉例: 如果一個學生的分數大於60, 則判定為及格 否則就是不及格 2.迴圈 則會執行多次相同的程式碼 場景舉例: 計算1到100的累加和, 需要