前言:本站為你精心整理了MFC程序設(shè)計(jì)思考范文,希望能為你的創(chuàng)作提供參考價(jià)值,我們的客服老師可以幫助你提供個(gè)性化的參考范文,歡迎咨詢。
摘要:mfc以層次結(jié)構(gòu)組織起來,比較龐雜,尤其是它的消息映射機(jī)制,更是涉及到很多底層的東西。本文通過對(duì)整個(gè)消息映射機(jī)制進(jìn)行系統(tǒng)的分析,可以幫助程序開發(fā)人員更好地了解MFC,進(jìn)行可視化編程。
關(guān)鍵詞:消息驅(qū)動(dòng);消息映射;MFC程序設(shè)計(jì)
1引言
微軟公司提供的MFC基本類庫(MicrosoftFoundationClasses),是進(jìn)行可視化編程時(shí)使用最為流行的一個(gè)類庫。MFC封裝了大部分WindowsAPI函數(shù)和Windows控件,使得程序的開發(fā)變得簡單,極大的縮短了程序的開發(fā)周期。MFC獨(dú)創(chuàng)的Document/View框架結(jié)構(gòu),能夠?qū)⒐芾頂?shù)據(jù)的代碼和顯示數(shù)據(jù)的程序代碼分開,并且設(shè)計(jì)了一套方便的消息映射和命令傳遞機(jī)制,方便程序員的開發(fā)使用。其中消息映射機(jī)制本身比較龐大和復(fù)雜,對(duì)它的分析和了解無疑有助于我們寫出更為合理的高效的程序。這里我們分析一下MFC的消息映射機(jī)制,以了解MFC是如何對(duì)Windows的消息加以封裝,方便用戶的開發(fā)。
2SDK下的消息機(jī)制實(shí)現(xiàn)
首先,簡單回顧一下SDK下我們是如何進(jìn)行Windows的程序開發(fā)的。Windows程序的運(yùn)行是依靠外部發(fā)生的事件來驅(qū)動(dòng)的,事件由操作系統(tǒng)捕捉,以消息的形式進(jìn)入消息隊(duì)列,然后通過消息循環(huán)從隊(duì)列中不斷取出消息,送到對(duì)應(yīng)的窗口過程里處理。相對(duì)于DOS程序,Windows是以WinMain作為程序的入口點(diǎn),以下就是一個(gè)簡化的Win32程序的主體,通過while語句實(shí)現(xiàn)消息循環(huán):
WinMain(…)
{
MSGmsg;
RegisterClass(…);//注冊(cè)窗口類
CreateWindow(…);//創(chuàng)建窗口
ShowWindow(…);//顯示窗口
UpdateWindow(…);
While(GetMessage(&msg,…)){//消息循環(huán)
TranslateMessage(…);
DispatchMessage(…);
}
returnmsg.wParam;
}
其中,msg代表消息,程序是通過GetMessage函數(shù)從和某個(gè)線程相對(duì)應(yīng)的消息隊(duì)列里面把消息取出來并放到消息變量msg里面。然后TranslateMessage函數(shù)用來把鍵盤消息轉(zhuǎn)化并放到響應(yīng)的消息隊(duì)列里面,最后DispatchMessage函數(shù)把消息分發(fā)到相關(guān)的窗口過程去處理。窗口過程根據(jù)消息的類型對(duì)不同的消息進(jìn)行相關(guān)的處理。在SDK編程過程中,用戶需要在窗口過程中分析消息的類型及其參數(shù)的含義,然后做不同的處理,相對(duì)比較麻煩;而MFC把消息調(diào)用的過程給封裝起來,使用戶能夠通過ClassWizard方便的使用和處理Windows的各種消息。
3MFC中的消息映射機(jī)制
在MFC的框架結(jié)構(gòu)下,“消息映射”是通過巧妙的宏定義,形成一張消息映射表格來進(jìn)行的。這樣一旦消息發(fā)生,F(xiàn)ramework就可以根據(jù)消息映射表格來進(jìn)行消息映射和命令傳遞。
首先在需要進(jìn)行消息處理的類的頭文件(.H)里,都會(huì)含有DECLARE_MESSAGE_MAP()宏,聲明該類擁有消息映射表格:
classCscribbleDoc:publicCdocument
{
…
DECLARE_MESSAGE_MAP()
};
然后在類應(yīng)用程序文件(.CPP)實(shí)現(xiàn)這一表格
BEGIN_MESSAGE_MAP(CInheritClass,CBaseClass)
//{{AFX_MSG_MAP(CInheritClass)
ON_COMMAND(ID_EDIT_COPY,OnEditCopy)
………
//}}AFX_MSG_MAP
END_MESSAGE_MAP()
----這里主要進(jìn)行消息映射的實(shí)現(xiàn),把它和消息處理函數(shù)聯(lián)系在一起。其中出現(xiàn)三個(gè)宏,第一個(gè)宏是BEGIN_MESSAGE_MAP有兩個(gè)參數(shù),分別是擁有消息表格的類,及其父類。第二個(gè)宏是ON_COMMAND,指定命令消息的處理函數(shù)名稱。第三個(gè)宏是END_MESSAGE_MAP()作為結(jié)尾符號(hào)。中間的奇怪符號(hào)//}}和//{{,是ClassWizard產(chǎn)生的,對(duì)程序無影響。
觀察DECLARE_MESSAGE_MAP的定義:
#defineDECLARE_MESSAGE_MAP()
private:
staticconstAFX_MESSAGE_ENTRY_messageEntries[];
protected:
staticAFX_DATAconstAFX_MSGMAPmessageMap;
virtualconstAFX_MSGMAP*GetMessageMap()const;
里面又包含了MFC新定義的兩個(gè)數(shù)據(jù)結(jié)構(gòu),如下:
AFX_MSGMAP_ENTRY
structAFX_MSGMAP_ENTRY
{
UINTnMessage;//windowsmessage
UINTnCode;//controlcodeorWM_NOTIFYcode
UINTnID;//controlID(or0forwindowsmessages)
UINTnLastID;//usedforentriesspecifyingarangeofcontrolid''''s
UINTnSig;//signaturetype(action)orpointertomessage#
AFX_PMSGpfn;//routinetocall(orspecialvalue)
};
和AFX_MSGMAP
structAFX_MSGMAP
{
constAFX_MSGMAP*pBaseMap;
constAFX_MSGMAP_ENTRY*lpEntries;
};
其中AFX_MSGMAP_ENTRY結(jié)構(gòu)包含了一個(gè)消息的所有相關(guān)信息,而AFX_MSGMAP主要作用有兩個(gè),一是用來得到基類的消息映射入口地址。二是得到本身的消息映射入口地址。
實(shí)際上,MFC把所有的消息一條條填入到AFX_MSGMAP_ENTRY結(jié)構(gòu)中去,形成一個(gè)數(shù)組,該數(shù)組存放了所有的消息和與它們相關(guān)的參數(shù)。同時(shí)通過AFX_MSGMAP能得到該數(shù)組的首地址,同時(shí)得到基類的消息映射入口地址。當(dāng)本身對(duì)該消息不響應(yīng)的時(shí)候,就可以上溯到基類的消息映射表尋找對(duì)應(yīng)的消息響應(yīng)。
現(xiàn)在我們來分析MFC是如何讓窗口過程來處理消息的,實(shí)際上所有MFC的窗口類都通過鉤子函數(shù)_AfxCbtFilterHook截獲消息,并且在鉤子函數(shù)_AfxCbtFilterHook中把窗口過程設(shè)定為AfxWndProc。原來的窗口過程保存在成員變量m_pfnSuper中。
在MFC框架下,一般一個(gè)消息的處理過程是這樣的。
(1)函數(shù)AfxWndProc接收Windows操作系統(tǒng)發(fā)送的消息。
(2)函數(shù)AfxWndProc調(diào)用函數(shù)AfxCallWndProc進(jìn)行消息處理,這里一個(gè)進(jìn)步是把對(duì)句柄的操作轉(zhuǎn)換成對(duì)CWnd對(duì)象的操作。
(3)函數(shù)AfxCallWndProc調(diào)用CWnd類的方法WindowProc進(jìn)行消息處理。
(4)WindowProc調(diào)用OnWndMsg進(jìn)行正式的消息處理,即把消息派送到相關(guān)的方法中去處理。在CWnd類中都保存了一個(gè)AFX_MSGMAP的結(jié)構(gòu),而在AFX_MSGMAP結(jié)構(gòu)中保存有所有我們用ClassWizard生成的消息的數(shù)組的入口,我們把傳給OnWndMsg的message和數(shù)組中的所有的message進(jìn)行比較,找到匹配的那一個(gè)消息。實(shí)際上系統(tǒng)是通過函數(shù)AfxFindMessageEntry來實(shí)現(xiàn)的。找到了那個(gè)message,實(shí)際上我們就得到一個(gè)AFX_MSGMAP_ENTRY結(jié)構(gòu),而我們?cè)谏厦嬉呀?jīng)提到AFX_MSGMAP_ENTRY保存了和該消息相關(guān)的所有信息,其中主要是消息的動(dòng)作標(biāo)識(shí)和相關(guān)的執(zhí)行函數(shù)。然后我們就可以根據(jù)消息的動(dòng)作標(biāo)識(shí)調(diào)用相關(guān)的執(zhí)行函數(shù),而這個(gè)執(zhí)行函數(shù)實(shí)際上就是通過ClassWizard在類實(shí)現(xiàn)中定義的一個(gè)方法。這樣就把消息的處理轉(zhuǎn)化到類中的一個(gè)方法的實(shí)現(xiàn)上。
(5)如果OnWndMsg方法沒有對(duì)消息進(jìn)行處理的話,就調(diào)用DefWindowProc對(duì)消息進(jìn)行處理。這是實(shí)際上是調(diào)用原來的窗口過程進(jìn)行缺省的消息處理。所以如果正常的消息處理的話,MFC窗口類是完全脫離了原來的窗口過程,用自己的一套體系結(jié)構(gòu)實(shí)現(xiàn)消息的映射和處理。即先調(diào)用MFC窗口類掛上去的窗口過程,再調(diào)用原先的窗口過程。用戶面對(duì)的消息參數(shù)將不再是固定的wParam和lParam,而是和消息類型具體相關(guān)的參數(shù)。比如和消息WM_LButtonDown相對(duì)應(yīng)的方法OnLButtonDown的兩個(gè)參數(shù)是nFlags和point。nFlags表示在按下鼠標(biāo)左鍵的時(shí)候是否有其他虛擬鍵按下,point更簡單,就是表示鼠標(biāo)的位置。同時(shí)MFC窗口類消息傳遞中還提供了兩個(gè)函數(shù),分別為WalkPreTranslateTree和PreTranslateMessage。我們知道利用MFC框架生成的程序,都是從CWinApp開始執(zhí)行的,而CWinapp實(shí)際繼承了CWinThread類。在CWinThread的運(yùn)行過程中會(huì)調(diào)用窗口類中的WalkPreTranslateTree方法。而WalkPreTranslateTree方法實(shí)際上就是從當(dāng)前窗口開始查找愿意進(jìn)行消息翻譯的類,直到找到窗口沒有父類為止。在WalkPreTranslateTree方法中調(diào)用了PreTranslateMessage方法。實(shí)際上PreTranslateMessage最大的好處是我們?cè)谙⑻幚砬翱梢栽谶@個(gè)方法里面先做一些事情。舉一個(gè)簡單的例子,比如我們希望在一個(gè)CEdit對(duì)象里,把所有的輸入的字母都以大寫的形式出現(xiàn)。我們只需要在PreTranslateMessage方法中判斷message是否為WM_CHAR,如果是的話,把wParam(表示鍵值)由小寫字母的值該為大寫字母的值就實(shí)現(xiàn)了這個(gè)功能。
4小結(jié)
MFC通過巧妙的宏定義把消息調(diào)用的過程給封裝起來,使用戶能夠通過ClassWizard方便的使用和處理Windows的各種消息。通過對(duì)MFC消息映射機(jī)制的分析,不僅能夠使我們更好的使用MFC類庫,同時(shí),對(duì)于我們自己設(shè)計(jì)程序框架和類,無疑也有相當(dāng)大的幫助。
參考文獻(xiàn):
[1]侯俊杰著,深入淺出MFC(第2版)[M].湖北:華中科技大學(xué)出版社,2001.5
[2]DavidJ.Kruglinski著,VisualC++技術(shù)內(nèi)幕(第五版)[M].北京:北京希望電子出版社,2001.1