MFC (微軟)

作為一個應用程式的開發框架,必須滿足各方面的功能需求。

應用程式啟動

編輯

基於MFC開發的應用程式在啟動時,Windows作業系統:

首先呼叫WinMain函數(位於appmodul.cpp中,封裝到mfc80.dll(VS2005版)),WinMain函數內呼叫了AfxWinMain函數。

AfxWinMain函數(位於WinMain.cpp中)呼叫了

該應用程式自訂的App類(這個類衍生於CWinApp的,CWinApp又是衍生於CWinThread,因此代表了應用程式的主線程)的InitInstance函數,該函數註冊並建立窗口(通過AppUI2.cpp中的ProcessShellCommmand函數),然後ShowWindow、UpdateWindow;

CWinThread的InitInstance函數;

CWinThread的Run函數(位於thrdcore.cpp中)。該函數內部是Windows的訊息循環。 當應用程式收到WM_QUIT訊息後,CWinThread::Run函數返回,緊接着CWinThread::ExitInstance被呼叫,該函數可被覆蓋。程式至此退出執行。 訊息循環是一個for(;;)的無窮迴圈,該無窮迴圈內部包含了一個do...while的循環結構。while循環條件是呼叫PeekMessage函數的返回值,如果當前UI線程訊息佇列為空就返回到外層的無窮迴圈;while循環體內做兩件事:

PumpMessage()。實際呼叫AfxInternalPumpMessage函數實現其功能:GetMessage()、AfxPreTranslateMessage()、TranslateMessage()、DispatchMessage().即:從UI線程訊息佇列移除一條訊息、遍歷該訊息的CWnd類直到該窗口的各級別父窗口的CWnd類以提供預處理該訊息的機會、如果該訊息是按鍵訊息則翻譯為WM_CHAR訊息、把該訊息給相應的窗口函數。

IsIdleMessage():實際呼叫了AfxInternalIsIdleMessage函數,對於WM_PAINT、WM_SYSTIMER、以及游標位置沒有變化的WM_MOUSEMOVE或WM_NCMOUSEMOVE,為Idle Message。

各個窗口函數(WndProc)內部首先取得對應當前窗口控制代碼的CWnd類的指標,然後呼叫AfxCallWndProc函數。

應用程式結束

編輯

如果是點擊了IDOK按鈕,預設是呼叫OnOK(),然後是OnDestory(),最後是PostNcDestroy()

如果點擊IDCANCEL按鈕,預設呼叫OnCancel(),然後是OnDestory(),最後是PostNcDestroy()

如果點擊右上角的關閉按鈕:先OnClose(),然後是OnCancel(),再然後是OnDestory() ,最後是PostNcDestroy()

訊息循環與訊息對映

編輯

參見:Windows訊息循環

MFC Class hierarchy for Windows message process flow

訊息分類

編輯

分類

訊息

對映宏

訊息處理常式原型

註釋

系統訊息

標準Windows訊息

MFC使用專用的相關的宏。如ON_WM_CREATE()

MFC使用專用的訊息處理成員函數

一般地由窗口對象來處理這類訊息

命令訊息 WM_COMMAND

ON_COMMAND(id, memberFxn)

void OnXXX ()

通過識別碼ID來區分來自哪個選單項、工具列按鈕或者加速鍵等

ON_COMMAND_RANGE(id, idLast, memberFxn)

void OnXXX (UINT id)

批次處理一定範圍內的標示符ID

ON_UPDATE_COMMAND_UI(id, idLast, memberFxn)

void OxXXX(CCmdUI* pCmdUI)

程式空閒時發的介面更新訊息的宏

ON_UPDATE_COMMAND_UI_RANGE(id, idLast, memberFxn)

void OxXXX(CCmdUI* pCmdUI)

ON_CONTROL(id, idLast, memberFxn)

void OnXXX ()

父窗口響應控制項傳送的訊息

ON_CONTROL_RANGE(id, idLast, memberFxn)

void OnXXX (UINT id)

ON_CONTROL_REFLECT(id, idLast, memberFxn)

void OnXXX ()

子控制項響應父窗口反射回來的通知訊息

通知訊息WM_NOTIFY

ON_NOTIFY(wNotifyCode,id,memberFxn)

afx_msg void memberFxn(NMHDR * pNotifyStruct, LRESULT* result)

wParam為控制項ID,lParam指向NMHDR結構體,結構體的code域值為控制項通知碼用來表示控制項上的動作,如NM_CLICK.一般地由父窗口對象來處理這類訊息。

ON_NOTIFY_RANGE( wNotifyCode, id, idLast, memberFxn )

可手工添加(class wizard不支援),處理控制項id連續的一批控制項的同一通知碼訊息。

ON_NOTIFY_EX

允許通知訊息在多處被處理

ON_NOTIFY_EX_RANGE

ON_NOTIFY_REFLECT

afx_msg void memberFxn(NMHDR * pNotifyStruct, LRESULT* result)

複雜子控制項響應父窗口反射回來的通知訊息

自訂訊息

窗口類內部自訂訊息WM_USER到WM_APP-1

ON_MESSAGE(訊息名,memberFxn)

afx_msg LRESULT OnMyMessageXXX(WPARAM wParam, LPARAM lParam)

範例

程式內部自訂訊息WM_APP到0xBFFF

ON_THREAD_MESSAGE(WM_THREADMSG,OnThreadMessage)

afx_msg void OnThreadMessage(WPARAM wParam,LPARAM lParam);

範例

ON_REGISTERED_THREAD_MESSAGE

afx_msg void OnMyRegisterdThreadMsg(WPARAM, LPARAM);

RegisterWindowMessage()使用一個字串來登記一個自訂的訊息ID

應用程式之間自訂訊息0xC000到0xFFFF

ON_REGISTERED_MESSAGE

LRESULT OnMyMessageXXX(WPARAM wParam, LPARAM lParam)

RegisterWindowMessage()使用一個字串來登記一個自訂的訊息ID,便於跨行程應用

控制項的通知訊息與反射訊息

編輯

表單上的控制項,應當向父表單通報控制項發生的各種事件,如被點擊、繪製、內容改變等等,稱為通知訊息(notification message )。在 Windows 3.x的16位元程式設計時代,控制項向父表單傳送WM_COMMAND訊息,由父表單的代碼負責實現這些事件。其中wParam的低16位元是 control ID,高16位元是notification code (例如BN_CLICKED);lParam是控制項控制代碼。因此,再無可能傳遞其它資訊給父表單。為此,為傳遞具有特別內容的控制項事件,Windows 3.x定義了一批特殊的通知訊息(notification messages):

WM_PARENTNOTIFY:子窗口的某些重大事件發生時通知父窗口,包括建立、銷毀、滑鼠各鍵按下等事件

子窗口的捲動情況的通知訊息

WM_VSCROLL

WM_HSCROLL

子窗口的繪製通知訊息

WM_DRAWITEM,

WM_MEASUREITEM,

WM_COMPAREITEM,

WM_DELETEITEM,

WM_CHARTOITEM,

WM_VKEYTOITEM

WM_CTLCOLOR:設置按鈕、編輯框、ListBox、Static、捲軸控制項與MessageBox、DialogBox的前景色、背景色、背景模式、字型,並返回一把Brush,用於控制項背景繪製。

早於4.0版本的MFC,在控制項類提供了虛擬函式處理這些通知訊息,這一辦法已經被下述的「訊息反射」取代(但仍然向下相容繼續支援)。

隨着Windows 95開始了32位元程式時代,伴之而來的是Win32 API 與 MFC 4.0。 Win32增加了很多複雜的控制項,需要使用更多的通知訊息傳遞很多複雜的數據給父表單。Win32 API僅僅增加了一個訊息WM_NOTIFY,就實現了這些功能。lParam參數開頭是NMHDR數據結構,其後是與該通知類型相關的特定數據結構。

typedef struct tagNMHDR {

HWND hwndFrom;

UINT idFrom;

UINT code;

} NMHDR;

CWnd::OnNotify函數處理通知訊息。它的預設實現是檢查訊息對映表(message map)尋找通知的處理器函數並呼叫。一般說來,不必覆蓋OnNotify;而應該寫一個處理器函數並增加為該窗口類的訊息對映表條目。

ON_NOTIFY(wNotifyCode, id, memberFxn)

成員函數應該寫為:

afx_msg void memberFxn(NMHDR* pNotifyStruct, LRESULT* result);

MFC 4.0提供了一種特性「訊息反射」(message reflection),[19]允許控制項通知訊息既可以在父窗口中,也可以在控制項中被處理。可以對控制項建立一個衍生的控制項類,實現對從父窗口反射回來的指定類型訊息的處理。訊息反射是MFC而不是Win32的特性,因此父窗口的類必須是從CWnd衍生,從而父窗口在CWnd::OnNotify函數中處理控制項的WM_NOTIFY時,首先呼叫CWnd::ReflectLastMsg把訊息反射回控制項的CWnd::SendChildNotifyLastMsg函數去處理;ReflectLastMsg返回值就是在控制項的訊息對映中使用ON_NOTIFY_REFLECT_EX()聲明的反射訊息處理常式的返回值,可以通知父窗口該訊息是否已經被控制項處理。控制項的CWnd::SendChildNotifyLastMsg函數,首先獲得線程的最後一條message,然後呼叫傳送窗口的虛擬函式OnChildNotify函數。在子窗口處理反射回來的控制項訊息,第一種方法是多載控制項窗口的OnChildNotify虛擬函式;第二種辦法是由CWnd::OnChildNotify預設處理去呼叫CWnd::ReflectChildNotify函數,進入控制項子窗口的MFC訊息對映的標準處理(子窗口處理的訊息被譯成WM_REFLECT_BASE+WM_NOTIFY訊息)。對於WM_NOTIFY,僅當在控制項的訊息對映(message map)中,控制項沒有通過ON_NOTIFY_REFLECT()聲明的反射訊息處理常式,父窗口的相應的通知訊息處理常式才會被呼叫(在父窗口訊息對映中使用宏ON_NOTIFY聲明)。控制項中通過ON_NOTIFY_REFLECT_EX()聲明的反射訊息處理常式可以返回真或假,以決定父窗口是否繼續處理該通知訊息。WM_NOTIFY以外的其它通知訊息,父窗口在第一時間有機會處理它,控制項對它的處理排在第二位。反射訊息處理常式通常使用特定的名字,對應的訊息反射宏的名字是在訊息名字加上字首ON_,字尾_REFLECT。如WM_CTLCOLOR對應ON_WM_CTLCOLOR_REFLECT。但以下三種情況,反射訊息處理常式可以隨意自行起名,對應的訊息反射宏的名字分別為:

WM_COMMAND使用ON_CONTROL_REFLECT

WM_NOTIFY使用ON_NOTIFY_REFLECT

ON_UPDATE_COMMAND_UI使用ON_UPDATE_COMMAND_UI_REFLECT

訊息傳遞處理機制

編輯

MFC類體系中,Windows訊息傳遞處理機制是基於CCmdTarget類及其衍生類別的靜態成員函數GetThisMessageMap()內部定義的靜態數據成員:

成員類型為AFX_MSGMAP_ENTRY的陣列_messageEntries。在類的實現檔案中,在BEGIN_MESSAGE_MAP與END_MESSAGE_MAP之間的內容來初始化訊息對映入口項陣列。

資料類型為AFX_MSGMAP的變量messageMap。該結構包含兩項,分別是直接基礎類別GetThisMessageMap函數指標與本類的_messageEntries陣列首元素地址。

在標頭檔的類別定義中使用宏DECLARE_MESSAGE_MAP()來聲明靜態成員函數GetThisMessageMap與虛擬函式GetMessageMap

用戶所寫的類的Windows訊息處理常式(例如OnCommand)必須轉換為CCmdTarget::*的成員函數指標類型AFX_PMSG,儲存在該類的_messageEntries陣列中。

struct AFX_MSGMAP_ENTRY

{

UINT nMessage; // windows消息代号

UINT nCode; // WM_NOTIFY的控制代码

UINT nID; // WM_COMMAND下面的ID号,如果为其他的消息,则这个数字为0

UINT nLastID; //和前面的ID一起组成一个范围,用于发送一次消息,处理执行多次

UINT nSig; // 标志消息处理函数的类型

AFX_PMSG pfn; // 函数调用指针

};

typedef void (CCmdTarget::*AFX_PMSG)(void);

呼叫用戶類中該訊息處理常式時,根據該函數儲存在_messageEntries中的signature(一個無符號整型表示的函數的形參類型列表與返回值類型),把類型為void (CCmdTarget::*AFX_PMSG)(void)的成員函數指標強制轉為其它類型的CCmdTarget成員函數指標(例如void (AFX_MSG_CALL CWnd::*pfn_v_i_i)(int, int),目前在union MessageMapFunctions中列出了近百種CCmdTarget成員函數指標),然後呼叫轉換後的成員函數指標。這是基於Visual C++編譯器把單繼承的成員函數指標編譯為只儲存了函數的主記憶體起始地址,因此可以在同一個單繼承類中把一種類型的成員函數指標強制轉換為另一種成員函數指標,或者把單繼承衍生類別的成員函數指標強制轉換為基礎類別成員函數指標。這是打破了C++標準的違例辦法。例如,對於CWnd::OnCommand函數,轉換過程是:

BOOL (CWnd::*)(WPARAM, LPARAM lParam) => void (CWnd::*)() => void (CCmdTarget::*)()

挖掘机高难度作业:拆楼作业和破碎作业怎么搞~~
高附加值是什么意思?高附加值的产品有哪些?