MFC 重大缺陷及其改進方法
作者: Solomon ()
日期: 2001/12/29
在侯老前輩的《深入淺出MFC2e》中,第九章是講述MFC是如何進行消息映射的。其中,侯老自扮西修斯(P418),引領讀者走入MFC消息唧筒的設計迷宮。但是,當我從迷宮走出時,卻經入了沉思。MFC真的做的如此之好嗎?有沒有什么缺陷?
在講述MFC重大缺陷之前,我給大家講述一下關于子類化(SubClass)的兩種方法。子類化可是萬里長征的第一步(不是第一站 P421),第一步都走不穩,何來第一站?其一就是MFC使用的CBT Hook,在侯老的書中已經詳細說明,此處不再重復。另一種方法就是在 WM_NCCREATE 中進行子類化操作。WM_NCCREATE 是一個窗口接受到的第一個消息,能截獲他,就可以截獲其他消息,也就能沒有任何遺漏地處理任何消息了。如何進行呢?
首先,我們必須知道一個指向窗口實例的指針。比如在 CWnd::Create() 中要調用API CreateWindow, 那么 this 就是指向窗口實例的指針.CWnd::Create()大致如下:
CWnd::Create(char szWindowTitle, ......){
char szXTitle[256];
wsprintf("%ld:%s", (long)this, szWindowTitle);
CreateWindowEx(.., szXTitle...);
}
注意 Title 改成了 Point to this(指向窗口實例的指針) 和原來Title 的組合。
接著 WndProc 在 WM_NCCREATE中取得指針和并恢復原來的Title, 同時使用SetProp,把指向窗口實例的指針保存。最后,在WndProc 進行消息分發處理。WndProc大致如下:
LRESULT WndProc(....) // 全局函數
{
static szTitle[256];
if (message == WM_NCCREATE)
{
if( GetProp(hWnd, "ClassInstance") == NULL ) {
//提取 指針 到 hClsInst(使用atol())
//恢復 Title 到 szTitle
// lParam->lpszWindowName 指向 szTitle
SetProp(hWnd, (HANDLE)hClsInst);
}
}
CWnd* p = (CWnd*)GetProp(hWnd, "ClassInstance");
p->DisPatchMessage(...);
}
這樣,窗口處理過程就被替換了。以上兩種方法都有一個問題。表現在那里呢?讓我們做個實驗。
首先,建立一個基于對話框的MFC App,接著創建 CMyButton 繼承自CButton,然后,用ClassWizard添加OK按鈕的一個變量,類型為CMyButton。在 CMyButton 分別處理 WM_LBUTTONDOWM 和 WM_NCCREATE。在 WM_LBUTTONDOWM 中調用 MessageBox() 顯示信息,在 WM_NCCREATE 中什么也不做,直接調用基類的處理。運行該程序。按下OK按鈕,應該出現MessageBox()顯示的信息。這說明OK按鈕已經被子類化(SubClass)了?,F在,分別在 CMyButton 的 OnNcCreate 和 OnLButtonDown 中設置斷點,然后運行程序。你會驚訝的發現,程序沒有執行CMyButton 的OnNcCreate。為什么???
到底為什么呢?其實,窗口的創建分為顯式和隱式兩種,通過調用CreateWindow, CreateWindowEx函數創建的窗口是顯式創建,可以使用前面提到的任何一種方法進行子類化,而不會損失對任何消息的截獲。而通過CreateDailog,DialogBox創建的窗口除了對話框本身是顯式創建,其含有的控件都是系統創建的,這樣,只有在該控件創建后才能被子類化,因此會損失對某些消息的截獲。
問題既然發生了,就要改進。方法有兩種。
其一,必須通過分析DialogTemplate手動創建子控件(FoxPro, VB 好像是這種形式)。需要改寫DoMadal, Create. 太復雜了。
另一種方法比較方便。由于子控件的創建必須在主窗口的WM_CREATE之后,這樣,就給了我們一個子類化控件的機會。方法如下:
1. 在對話框的WM_CREATE 中建立CBT, 傳入參數是 對話框的 CMyDlg 指針
2. 在CBT中,判斷 創建的窗口是否為 CMyDlg 的子控件,如果是, 并且需要時就進行子類化操作。
3. 在 WM_INITDIALOG 中銷毀CBT.
4. CMyDlg中DDX_Control()必須注釋掉
鑒于篇幅原因,源程序不在此處列出。如果大家有其他好的方法,請一起討論。
原文轉自:http://www.anti-gravitydesign.com