MFC的訊息回應機制詳解:
1.MFC是Windows下程式設計的最流行的一個類別庫,但是該類別庫比較龐雜,尤其是它的訊息映射機制,更是涉及到很多低層的東西,接下來詳細講解,
2.在講解MFC的訊息回應之前先講解一下SDK的訊息回應: SDK下的訊息機制實作
講解一下SDK下我們是如何進行Windows的程式開發的,一般來說,Windows的訊息都是和執行緒相對應的,即Windows會把訊息發送給和該訊息相對應的執行緒,
在SDK的模式下,程式是通過GetMessage函式從和某個執行緒相對應的訊息佇列里面把訊息取出來并放到一個特殊的結構里面,一個訊息的結構是一個如下的STRUCTURE,
typedef struct tagMSG {
HWND hwnd;
UINT message;
WPARAM wParam;
LPARAM lParam;
DWORD time;
POINT pt;
}MSG;
說明一下結構體里的內容:
1)hwnd表示和視窗程序相關的視窗的句柄,
2)message表示訊息的ID號,
3)wParam和lParam表示和訊息相關的引數,
4)time表示訊息發送的時間,
5)pt表示訊息發送時的滑鼠的位置,
再用TranslateMessage函式用來把虛鍵訊息翻譯成字符訊息并放到回應的訊息佇列里面,最后DispatchMessage函式把訊息分發到相關的視窗程序,
然后視窗程序根據訊息的型別對不同的訊息進行相關的處理,在SDK編程程序中,用戶需要在視窗程序中分析訊息的型別和跟訊息一起的引數的含義,做不同的處理,
相對比較麻煩,而MFC把訊息呼叫的程序給封裝起來,使用戶能夠通過ClassWizard方便的使用和處理Windows的各種訊息,
3.MFC的訊息實作機制
在MFC的框架結構下,我們可以清晰的看到,可以進行訊息處理的類的頭檔案里面都會含有DECLARE_MESSAGE_MAP()宏,這里主要進行訊息映射和訊息處理函式的宣告,
可以進行訊息處理的類的實作檔案里一般都含有如下的結構,
BEGIN_MESSAGE_MAP(CInheritClass, CBaseClass)
END_MESSAGE_MAP()
1)所有能夠進行訊息處理的類都是基于CCmdTarget類的,也就是說CCmdTarget類是所有可以進行訊息處理類的父類,CCmdTarget類是MFC處理命令訊息的基礎和核心,
2)同時MFC定義了下面的兩個主要結構:
AFX_MSGMAP_ENTRY
struct AFX_MSGMAP_ENTRY
{
UINT nMessage; // windows message
UINT nCode; // control code or WM_NOTIFY code
UINT nID;
UINT nLastID;
UINT nSig;
AFX_PMSG pfn;
};
其中AFX_MSGMAP_ENTRY結構包含了一個訊息的所有相關資訊:
1)nMessage為Windows訊息的ID號,
2)nCode為控制訊息的通知碼,
3)nID為Windows控制訊息的IDnLastID表,
4)如果是一個指定范圍的訊息被映射的話,nLastID用來表示它的范圍,nSig表示訊息的動作標識,
5)AFX_PMSG pfn 它實際上是一個指向和該訊息相應的執行函式的指標,
AFX_MSGMAP
struct AFX_MSGMAP
{
#ifdef _AFXDLL
const AFX_MSGMAP* (PASCAL* pfnGetBaseMap)();
#else
const AFX_MSGMAP* pBaseMap;
#endif
const AFX_MSGMAP_ENTRY* lpEntries;
};
FX_MSGMAP主要作用是兩個:
1)用來得到基類的訊息映射入口地址,
2)得到本身的訊息映射入口地址,
實際上,MFC把所有的訊息一條條填入到AFX_MSGMAP_ENTRY結構中去,形成一個陣列,該陣列存放了所有的訊息和與它們相關的引數,
同時通過AFX_MSGMAP能得到該陣列的首地址,同時得到基類的訊息映射入口地址,這是為了當本身對該訊息不回應的時候,就呼叫其基類的訊息回應,
分析一下MFC是如何讓視窗程序來處理訊息的,實際上所有MFC的視窗類都通過鉤子函式_AfxCbtFilterHook截獲訊息,并且在鉤子函式_AfxCbtFilterHook中把視窗程序設定為
AfxWndProc,原來的視窗程序保存在成員變數m_pfnSuper中,
在MFC框架下,一般一個訊息的處理程序是這樣的,以下作為說明:
函式AfxWndProc接收Windows作業系統發送的訊息,
函式AfxCallWndProc呼叫CWnd類的方法WindowProc進行訊息處理,注意AfxWndProc和AfxCallWndProc都是AFX的API函式,而WindowProc已經是CWnd的一個方法,
所以可以注意到在WindowProc中已經沒有關于句柄或者是CWnd的引數了,
方法WindowProc呼叫方法OnWndMsg進行正式的訊息處理,即把訊息派送到相關的方法中去處理,訊息是如何派送的呢?實際上在CWnd類中都保存了一個AFX_MSGMAP的
結構,而在AFX_MSGMAP結構中保存有所有我們用ClassWizard生成的訊息的陣列的入口,我們把傳給OnWndMsg的message和陣列中的所有的message進行比較,
找到匹配的那一個訊息,實際上系統是通過函式AfxFindMessageEntry來實作的,找到了那個message,實際上我們就得到一個AFX_MSGMAP_ENTRY結構,
而我們在上面已經提到AFX_MSGMAP_ENTRY保存了和該訊息相關的所有資訊,其中主要的是訊息的動作標識和跟訊息相關的執行函式,
然后我們就可以根據訊息的動作標識呼叫相關的執行函式,而這個執行函式實際上就是通過ClassWizard在類實作中定義的一個方法,
這樣就把訊息的處理轉化到類中的一個方法的實作上,
舉一個簡單的例子,比如在View中對WM_LButtonDown訊息的處理就轉化成對如下一個方法的操作,
void CInheritView::OnLButtonDown(UINT nFlags, CPoint point)
{
handler code here and/or call default
CView::OnLButtonDown(nFlags, point);
}
注意這里CView::OnLButtonDown(nFlags, point)實際上就是呼叫CWnd的Default()方法, 而Default()方法所做的作業就是呼叫DefWindowProc對訊息進行處理,
這實際上是呼叫原來的視窗程序進行預設的訊息處理,
如果OnWndMsg方法沒有對訊息進行處理的話,就呼叫DefWindowProc對訊息進行處理,這是實際上是呼叫原來的視窗程序進行預設的訊息處理,所以如果正常的訊息處理的
話,MFC視窗類是完全脫離了原來的視窗程序,用自己的一套體系結構實作訊息的映射和處理,即先呼叫MFC視窗類掛上去的視窗程序,再呼叫原先的視窗程序,并且用戶面
對和訊息相關的引數不再是死板的wParam和lParam,而是和訊息型別具體相關的引數,
比如和訊息WM_LbuttonDown相對應的方法OnLButtonDown的兩個引數是nFlags和point,nFlags表示在按下滑鼠左鍵的時候是否有其他虛鍵按下,point更簡單,
就是表示滑鼠的位置,
同時MFC視窗類訊息傳遞中還提供了兩個函式,分別為WalkPreTranslateTree和PreTranslateMessage,我們知道利用MFC框架生成的程式,都是從CWinApp開始執行的,
而CWinapp實際繼承了CWinThread類,在CWinThread的運行程序中會呼叫視窗類中的WalkPreTranslateTree方法,
而WalkPreTranslateTree方法實際上就是從當前視窗開始查找愿意進行訊息翻譯的類,直到找到視窗沒有父類為止,
在WalkPreTranslateTree方法中呼叫了PreTranslateMessage方法,實際上PreTranslateMessage最大的好處是我們在訊息處理前可以在這個方法里面先做一些事情,
舉一個簡單的例子:
比如我們希望在一個CEdit物件里,把所有的輸入的字母都以大寫的形式出現,我們只需要在PreTranslateMessage方法中判斷message是否為WM_CHAR,
如果是的話,把wParam(表示鍵值)由小寫字母的值該為大寫字母的值就實作了這個功能,
繼續上面的例子,根據我們對MFC訊息機制的分析,我們很容易得到除了上面的方法,我們至少還可以在另外兩個地方進行操作,
a.在訊息的處理方法里面即OnChar中,當然最后我們不再呼叫CEdit::OnChar(nChar, nRepCnt, nFlags),而是直接呼叫DefWindowProc(WM_CHAR,nChar,MAKELPARAM
(nRepCnt,nFlags)),
因為從我們上面的分析可以知道CEdit::OnChar(nChar, nRepCnt, nFlags)實際上也就是對DefWindowProc方法的呼叫,
b.我們可以直接多載DefWindowProc方法,對message型別等于WM_CHAR的,直接修改nChar的值即可,
4.今天就寫這些,明天繼續,程式員們要想要精通代碼,最主要的方式之一就是要多練習,多除錯,這樣面對bug的時候才能臨危不動,
改變自己,從現在做起-----------久館
轉載請註明出處,本文鏈接:https://www.uj5u.com/houduan/224523.html
標籤:其他
