天天看點

【筆記】《深入淺出MFC》第6章 MFC程式的生死因果

一、頭檔案說明

STDAFX.H

這個檔案用來作為Precompile header file,其内隻是載入其他的MFC頭檔案。應用程式通常會準備自己的頭STDAFX.H。

AFXWIN.H

每一個Windows MFC程式都必須載入它,因為它以及它所載入的檔案聲明了所有的MFC類。

在WINDEF.H中有CALLBACK的定義

#define CALLBACK _stdcall     //是一種函數調用習慣

在AFXWIN.H中有afx_msg的定義

#define afx_msg        //故意安排一個空位置,也許以後版本會用到。

所有MFC頭檔案均置于\MSVC\MFC\INCLUDE中。這些檔案在編譯過程中耗費大量時間,是以有必要設定Precompiled header。一個應用程式在常需要不斷地編譯。Windows程式載入的.H檔案非常巨大但内容不變,編譯器浪費在上面的時間非常多。Precompiled header就是将.H檔案第一次編譯後的結果存儲起來,第二次再編譯時就可以直接從磁盤中取出來用。

二、MFC程式的來龍去脈

CWinApp代表程式本體。CFrameWnd代表一個主架構視窗。必須以這兩個類為基礎,派生自己的類,并改寫其中一部分成員函數。

全局對象theAPP,就是application object。每一個應用程式都應該改寫CWinApp::InitInstance()函數。

MFC把有着相當固定行為的WinMain内部操作封裝在CWinApp中,把有着相當固定行為的WinProc内部操作封裝在CFrameWnd中。

傳統SDK程式WinMain完成的工作,現在由CWinApp的三個函數完成。

Virtual BOOL InitApplication();

Virtual BOOL InitInstance();

Virtual int Run();

CWinApp繼承CWinThread了成員變量m_pMainWnd,代表主視窗。CFrameWnd主要用來掌握一個視窗,它是用來取代SDK程式中的視窗函數的地位。

我們并未寫WinMain程式代碼,這是MFC早已準備好并由連結器直接加到應用程式代碼中的。_tWinMain函數的_t是為了支援UniCode而準備的一個宏。

【筆記】《深入淺出MFC》第6章 MFC程式的生死因果

下面是AfxWinMain代碼。

【筆記】《深入淺出MFC》第6章 MFC程式的生死因果
【筆記】《深入淺出MFC》第6章 MFC程式的生死因果

将以上代碼整理一下就得到下面這段代碼。

【筆記】《深入淺出MFC》第6章 MFC程式的生死因果

AfxGetApp 是一個全局函數,它取得CMyWinApp 對象指針。AfxWinInit 是繼CWinApp 構造式之後的第一個動作。AfxWinInit 之後的動作是pApp->InitApplication。

【筆記】《深入淺出MFC》第6章 MFC程式的生死因果

以上代碼這些動作都是MFC 為了内部管理而做的。繼InitApplication 之後, AfxWinMain 調用pApp->InitInstance。

【筆記】《深入淺出MFC》第6章 MFC程式的生死因果

一般而言,CMyWinApp隻改寫CWinApp中的InitInstance,通常它不改寫InitApplication和Run。

注意:應用程式一定要改寫虛拟函數InitInstance,因為它在CWinApp 中隻是個空函數,沒有任何内建(預設)動作。

CMyWinApp::InitInstance 一開始new 了一個CMyFrameWnd 對象,準備用作主框視窗的C++ 對象。CFrameWnd::Create 在産生視窗之前,會先引發視窗類别的注冊動作。

【筆記】《深入淺出MFC》第6章 MFC程式的生死因果

下面是CreateEx代碼。

【筆記】《深入淺出MFC》第6章 MFC程式的生死因果
【筆記】《深入淺出MFC》第6章 MFC程式的生死因果

調用的PreCreateWindow 是虛拟函數, CWnd 和CFrameWnd 之中都有定義。由于this 指針所指對象的緣故,這裡應該調用的是CFrameWnd::PreCreateWindow。

【筆記】《深入淺出MFC》第6章 MFC程式的生死因果

利用AfxDeferRegisterClass宏注冊視窗類。不同類别的PreCreateWindow 成員函數都是在視窗産生之前一刻被調用,準備用來注冊視窗類别。

CMyFrameWnd::CMyFrameWnd 結束後, 視窗已經誕生出來;程式流程又回到CMyWinApp::InitInstance ,于是調用ShowWindow 函數令視窗顯示出來,并調用UpdateWindow 函數令Hello 程式送出WM_PAINT 消息。

視窗類别注冊好了,視窗誕生并顯示出來了, UpdateWindow 被調用,使得消息隊列中出現了一個WM_PAINT 消息,等待被處理。現在,執行pApp->Run。

【筆記】《深入淺出MFC》第6章 MFC程式的生死因果
【筆記】《深入淺出MFC》第6章 MFC程式的生死因果
【筆記】《深入淺出MFC》第6章 MFC程式的生死因果
【筆記】《深入淺出MFC》第6章 MFC程式的生死因果

Message Map 機制是為了提供更友善的程式接口(例如宏或表格),讓程式員很友善就可以建立起消息與處理例程的對應關系。

MFC 提供給應用程式使用的「很友善的接口」是兩組宏。以Hello 的主視窗為例,第一個動作是在HELLO.H 的CMyFrameWnd 加上DECLARE_MESSAGE_MAP:

【筆記】《深入淺出MFC》第6章 MFC程式的生死因果

第二個動作是在HELLO.CPP 的任何位置(當然不能在函數之内)使用宏如下:

【筆記】《深入淺出MFC》第6章 MFC程式的生死因果

MFC 把消息主要分為三大類, Message Map 機制中對于消息與函數間的對映關系也明定以下三種:

1、标準Windows 消息(WM_xxx)的對映規則:

【筆記】《深入淺出MFC》第6章 MFC程式的生死因果

2、指令消息( WM_COMMAND)的一般性對映規則是:ON_COMMAND(<id>,<memberFxn>)

例如:

ON_COMMAND(IDM_ABOUT, OnAbout)

ON_COMMAND(IDM_FILENEW, OnFileNew)

ON_COMMAND(IDM_FILEOPEN, OnFileOpen)

ON_COMMAND(IDM_FILESAVE, OnFileSave)

3、Notification 消息(由控制元件産生,例如BN_xxx)的對映機制的宏分為好幾種(因為控制元件本就分為好幾種),以下各舉一例做代表:

【筆記】《深入淺出MFC》第6章 MFC程式的生死因果

各個消息處理函數均應以afx_msg void 為函數類型。如果某個消息在Message Map 中找不到對映記錄,它會往基礎類别流竄,這個消息流竄動作稱為Message Routing。如果一直竄到最基礎的類别仍找不到對映的處理例程,由預設函數來處理。

【總結】

【筆記】《深入淺出MFC》第6章 MFC程式的生死因果
【筆記】《深入淺出MFC》第6章 MFC程式的生死因果
【筆記】《深入淺出MFC》第6章 MFC程式的生死因果
【筆記】《深入淺出MFC》第6章 MFC程式的生死因果

凡是由你設計而卻由Windows 系統調用的函數,統稱為callback 函數。這些函數都有一定的類型,以配合Windows的調用動作。

callback 函數是給Windows 調用的, Windows 并不經由任何對象調用這個函數,也就沒有傳遞this 指針給callback 函數,于是導至堆棧中有一個随機變量會成為this 指針,而其結果當然是程式的崩潰了。

要把某個函數用作callback 函數,兩個方法可以做到這一點:

(1)不要使用類的成員函數(也就是說,要使用全局函數)做為callback 函數。

(2)使用static 成員函數。也就是在函數前面加上static 修飾詞。

MFC