關鍵字:子類化窗口,窗口過程.
關鍵函數:SetWindowLong,SetWindowLongPtr,CallWindowProc.
我們可以用SetWindowLong函數子類化一個窗口,使我們自己的窗口過程處理該窗口的大部分事件.不過有幾點需要注意:
1.SetWindowLong只能用於Win32程序中,而SetWindowLongPtr可以用於64位操作系統.
2.SetWindowLong函數包含自commctl32.dll中,頭文件是comctrl.h.
3.commctl32.dll在5.8版本(或6.0版本)之後增加了一些新的API,SetWindowSubclass函數可以替代SetWindowLong函數並且SetWindowSubclass有着比SetWindowLong更優越的方面.其一是可以通過uIdSubclass設置不同的子類ID.其二是可以通過dwRefData設置每個子類自己的數據.這樣,不同的子類可以共享相同的窗口過程但卻有着不同的子類ID和自己的數據.
4.個人認爲BCB6.0是不支持SetWindowSubclass函數的,雖然可以在system32/commctl32.dll(XPSP3)中找到相關的導出函數並且在comctrl.h中也有該函數的定義.不過在VC9.0中是可以調用該函數的.這可能是因爲編譯器(我用的是BCB6.0)中對應的commctl32.lib中沒有該函數.原因可能是BCB6.0中的commctl32.lib在commctl32.dll之前就已經編譯好的.
5.MSDN中說的很詳細,需要加上下面的代碼.
#ifdef STRICT
WNDPROC OldEditProc;
#else
FARPROC OldEditProc;
#endif
同樣的.
#ifdef STRICT
OldEditProc = (WNDPROC)SetWindowLong(edt1->Handle,GWL_WNDPROC,(LONG)NewEditProc);
#else
OldEditProc = (FARPROC)SetWindowLong(edt1->Handle,GWL_WNDPROC,(LONG)NewEditProc);
#endif
下面貼一下自己寫的Demo.
Form.cpp
Form.h
這個小例子是爲了控制Edit中輸入的字符只能是0-9.
值得補充的一點是,可以通過再次調用SetWindowLongPtr函數將窗口過程替換回原來的默認函數.
(WNDPROC)SetWindowLongPtr(hWnd,GWL_WNDPROC,(LONG)OldEditProc);
關於Subclassing 更詳細的說明在MSDN上可以找到.
下面是從別的網頁上摘下來的:
子類化(subclass)控件的新方法
注:本文原文(英文)出自微軟MSDN網站,由袁曉輝翻譯併發布在http://www.farproc.com/,供廣大編程愛好者交流、學習只用。譯者保留譯文的一切權利,禁止將本譯文用於商業用途。你可以轉載該文章,但請保持其完整性並註明來自http://www.farproc.com/。
如果一個控件擁有我們想要的特性,但是我們想給它添加一些新的特性,可以通過子類化(subclass)這個控件來實現。一個子類除了擁有父類的一切特性外還擁有我們給它添加的任何新特性。
這篇文章講討論任何實現子類化,幷包含如下話題:
* ComCtl32.dll version 6之前的控件子類化方法
* 使用ComCtl32.dll version 6 實現控件子類
ComCtl32.dll version 6之前的控件子類
在ComCtl32.dll 6.0 發佈以前,我們也有辦法來創建子類(譯者注:使用SetWindowLong,SetWindowLongPtr API),但是這種方法存在一些缺點。
子類化控件
創建一個新的控件最好是以Windows通用控件爲基礎進行擴充以使它滿足特定的需要。要擴展一個控件,可以創建一個控件然後用一個新的窗口過程(window procedure)替換它原來的窗口過程,新的窗口程序截獲控件的消息,要麼處理該消息,要麼傳遞給原來的窗口過程進行默認處理。使用SetWindowLong 或 SetWindowLongPtr 函數可以替換 WNDPROC。下面的代碼演示了任何替換 WNDPROC。
OldWndProc = (WNDPROC)SetWindowLongPtr (hButton,
GWLP_WNDPROC, (LONG_PTR)NewWndProc);
存儲自定義數據
你可能需要爲一個窗口存儲一些自定義數據,新的窗口過程可以根據自定義數據來決定任何繪製控件或向哪個窗口發送一些特定的消息,還有,比如,你可能需要存儲一個代表該控件的C++窗口類指針。下面的代碼演示任何使用SetProp 來在一個窗口內存儲一個字符串。
SetProp (hwnd, TEXT("MyData"), (HANDLE)pMyData);
舊的子類化方法的缺陷
下面的清單顯示了用前面提到的那種方法來子類化控件時的一些缺陷
* 窗口過程只能被替換一次。
* 一個子類創建後很難清除。
* 給一個窗口管理私有數據效率比較低。
* 在傳遞消息給子類鏈中的下一個函數時你不能直接調用它,而是必須用過CallWindowProc 函數來實現。
使用ComCtl32.dll version 6 進行子類化
Windows XP 帶的ComCtl32.dll version 6 提供了4個可以讓創建子類化更簡單,並且可以消除前面提到的缺陷的函數。這些新的函數封裝了對多組參考數據(multiple sets of reference data)的管理操作,使得開發者能將精力集中到具體的程序特性而不是對子類的管理上。這些新的函數爲:
* SetWindowSubclass
* GetWindowSubclass
* RemoveWindowSubclass
* DefSubclassProc
下面是對這些函數的描述。
SetWindowSubclass
這個函數用來子類化一個窗口。每個子類可以用p pfnSubclass 和 uIdSubclass (SetWindowSubclass的參數)唯一標識。多個子類可以共享同一個子類過程,而用標識(ID)來區分。改變參考數據可以提供再一次調用SetWindowSubclass 來實現。一個重要的優點是每一個子類實例可以擁有自己的參考數據。
子類過程的聲明和傳統的窗口過程有點細微的差別,它多了兩個參數:子類ID和參考數據。參看下面這個函數聲明的最後兩個參數:
LRESULT CALLBACK MyWndProc (HWND hWnd, UINT msg,
WPARAM wParam, LPARAM lParam, UINT_PTR uIdSubclass,
DWORD_PTR dwRefData);
每次新的窗口過程收到一個消息時,它同時得到了一個子類ID和參考數據。
注意:所有作爲參數傳遞給該函數的字符串均爲Unicode,不管有沒有定義Unicode編譯選項。
GetWindowSubclass
該函數取回一個子類的信息。比如你可以用GetWindowSubclass 來訪問參考數據。
RemoveWindowSubclass
該函數移除一個子類. RemoveWindowSubclass 和 SetWindowSubclass 聯合使用可以動態添加和刪除子類.
DefSubclassProc
這個函數調用子類鏈中的下一個處理者。這個函數可以自行取得正確的ID和參考數據並傳遞給下一個窗口過程。