在講述窗口的子類化和超類化之前,我們必須 先了解windows窗口類的概念。windows的窗口類 是windos 用來創(chuàng)建窗口的依據(jù)之一,每個(gè)窗口 必然屬于某個(gè)窗口類。窗口類是一個(gè)窗口模板,包 含一個(gè)窗口所具有的部分窗口屬性。編寫一個(gè)windows程序時(shí),首先要做的工作就是注冊一個(gè)窗 口類,然后基于此注冊的窗口類創(chuàng)建一個(gè)新的窗 口。在 windows平臺(tái)中,注冊窗口類的函數(shù)是 RegisterClass和 RegisterClassEX, 其 中 RegisterClassEx是推薦使用的函數(shù),使用這個(gè)函數(shù)注冊窗 口類時(shí),需要先填寫一個(gè) WNDCLASS結(jié)構(gòu)。這 個(gè)結(jié)構(gòu)實(shí)際上反映了一個(gè)窗口類的特征,一個(gè)窗口 類有本類所有窗口公用的類屬性、窗口函數(shù)、類圖 標(biāo)和小圖標(biāo)、類鼠標(biāo)、窗口背景刷、類菜單,當(dāng)然還 有類名。除此之外,每個(gè)類還有一定大小的類存儲(chǔ)
區(qū),可以用來存儲(chǔ)該類的公共數(shù)據(jù)。 每一個(gè)創(chuàng)建的窗口都有一個(gè)窗口函數(shù),其地址 由結(jié)構(gòu)的 wndclASS參數(shù)lpfnWndProc設(shè)定, 該窗口函數(shù)處理對應(yīng)于該窗口類的所有實(shí)例的消 息。當(dāng)創(chuàng)建一個(gè)窗口時(shí),windows 將分配一個(gè)內(nèi)存 塊,用來存放與該窗口相關(guān)的信息,并將參數(shù) lpfnWndClass 從窗口類內(nèi)存塊拷貝到該內(nèi)存塊中。當(dāng)消 息被分發(fā)到窗口時(shí), windows 檢查該窗口中內(nèi)存塊 中的 lpfnWndClass值,并調(diào)用該內(nèi)存塊地址上的窗 口函數(shù)。
一個(gè)窗口的行為主要取決于它的窗口函數(shù),如 果能夠改變一個(gè)窗口的窗口函數(shù),使它指向自己寫 的某個(gè)函數(shù),那就意味著發(fā)給這個(gè)窗口的各種消息 將由我們自己寫的這個(gè)函數(shù)來處理。
子類化一個(gè)窗口,實(shí)際上就是改變窗口內(nèi)存塊 中的窗口函數(shù)的地址,使其指向用戶自定義的新的 窗口函數(shù)入口,以實(shí)現(xiàn)用戶希望的窗口特性。 超類化則是利用原來的那個(gè)窗口類的某些特 征,改變它另外的一些特征,包括窗口函數(shù),重新注 冊一個(gè)新的窗口類。 超類化和子類化的共同之處就是,這兩種方法 都是從一個(gè)已經(jīng)存在的窗口類產(chǎn)生新的窗口或窗 口類的方法,新的窗口或窗口類具有原來的窗口類 的某些特征,也具有一些新增的特征。但子類化是 從窗口的角度出發(fā)的,而超類化是從窗口類的角度 出發(fā)的。
窗日了類化技術(shù)最大的特點(diǎn)就是能夠截取eindows的消息。一日_用戶自定義的窗日函數(shù)截取獷傳向原窗日函數(shù)的消息,就可以對被截取的消息進(jìn)行如下處理曰:
.將其傳給原來的窗日函數(shù)。這是對大多數(shù)消息應(yīng)該采取的措施,因?yàn)榱祟愅ǔV粚υ瓉淼拇叭仗匦宰魃倭康男薷摹?br> .截取該消息,阻止其向原窗日函數(shù)發(fā)送。
.修改該消息,修改完畢以后再向原窗口函數(shù)發(fā)送。
GetWindowLong SetWindowLong
在Windows編程中,使用窗子類化技術(shù),可以方便地達(dá)到改變一個(gè)窗日的特性的日的。但了
子類化也存在其局限性。實(shí)際上,
了類化的概念是針對一個(gè)己經(jīng)創(chuàng)建的窗口來談的,所以修改窗口函數(shù)是在窗口創(chuàng)建之后進(jìn)行的,在窗口創(chuàng)建期間的消息無法捕獲,也就無法處理。另外有些窗日的特性與
窗日類本身的屬性有關(guān)。比如如果一個(gè)窗日類沒有CS_ DBLCLKS屬性的話,那么要通過了類化這些窗u達(dá)到處理WM_ LBUTTOIVDBLCLK消息的日的。對于了類化的以上局限性,可以通過窗口的超類化技術(shù)來消除。實(shí)際上超類化可以完全實(shí)現(xiàn)了類化的功能。
超類化需要注冊一個(gè)新的窗日類,達(dá)到改變窗日類的各種特征的目的。超類化實(shí)現(xiàn)的簡單過程是
獲得一個(gè)己經(jīng)存在的窗日類的特征,然后改變這些特征,最后重新注冊一個(gè)窗日類。具體的步驟如下:
①定義一個(gè)類型為WNDCLASSEX的變量。因?yàn)樾枰孕碌拇叭疹悾x這個(gè)變量是必要的。
②調(diào)用GetClasslnfoEx函數(shù)得到希望超類化的那個(gè)窗口類的信息。
③改變窗口類的基本特征,顯然窗口類名和模塊句柄hlnstance是必須改變的。注意如果需要改
變窗口類的窗口函數(shù)的話,在改變窗口函數(shù)之前應(yīng)該保存原來的窗口函數(shù),井且在新的窗日函數(shù)中把
不需要處理的消息傳遞給原來的窗口函數(shù),以保留原窗口類的一些特征。
④利用修改后的WIVDCLASSEX變量,調(diào)用RegisterClassEX函數(shù)重新注冊一個(gè)新的窗u類。
⑤創(chuàng)建這個(gè)新窗日類的一個(gè)窗日實(shí)例。
(1)用MFC Application Wizard新建一個(gè)MDl
程序SuperClassingo
2)利用ClassWizard建一個(gè)從 CWnd類派生
的新類CDblClkWnd。添加MDl客戶窗口對左鍵雙
擊的處理函數(shù):


3)重新注冊一個(gè)窗口類,進(jìn)行超類化。
BOOL CDblClkWnd::RegisterNewClass()
{
WNDCLASS wc;
if(!GetClassInfo(NULL,"MDIClient",&wc))
return FALSE;
wc.style=SC_DBLCLKS;
wc.lpszClassName="
DBLCLCMDIClient";修改名字
return RegisterClass(&wc);
}
在APP類的InitInstance函數(shù)前創(chuàng)建主框架的代碼前調(diào)用上面的注冊新窗口的類的代碼
if(!CDblClkWnd::RegisterNewClass())
return false;
在使用CreateWindowEx創(chuàng)建MDI客戶窗口的時(shí)候,把原來的窗口類MDIClient改為
DB LCLCMDIClient
在主窗口中添加變量CDblClkWnd m_client,在主窗口的OnCreate中對MDIClient進(jìn)行子類化。在OnDestroy中進(jìn)行反子類化。
pclient.SuhclassWindow(phWndMDlClient);
m client.UnsubclassWindow()://反子類化