關(guān)于子類化的話題雖然有些舊,但它至今仍然不失為一種開發(fā)Windows的強(qiáng)有力技術(shù),在MFC的內(nèi)核、甚至.NET的內(nèi)核中都離不開它,希望本連載能對Windows開發(fā)的愛好者有所幫助。
原文標(biāo)題:Safe Subclassing in Win32
作者:Kyle Marsh
MSDN技術(shù)組
點(diǎn)擊此處查看原文
摘要
本文描述了Win32環(huán)境下的子類化,描述了它是如何工作的以及實(shí)現(xiàn)安全的子類化必須要遵循的規(guī)則。本文涵蓋了實(shí)例子類化和全局子類化。而超類化則作為一個全局子類化的可選替代方案被介紹。
從Win16到Win32,子類化并沒有發(fā)生特別顯著的變化,但是,在Win32中,一個應(yīng)用程序還是要遵守幾個新的子類化規(guī)則。其中最重要(也是最明顯的)就是一個應(yīng)用程序不能子類化屬于另一個進(jìn)程的窗口或者類,除非有工作區(qū)提供給應(yīng)用程序使用,否則這條規(guī)則不能被打破。
子類化的定義
子
類化是這樣一種技術(shù),它允許一個應(yīng)用程序截獲發(fā)往另一個窗口的消息。一個應(yīng)用程序通過截獲屬于另一個窗口的消息,從而實(shí)現(xiàn)增加、監(jiān)視或者修改那個窗口的缺
省行為。子類化是用來改變或者擴(kuò)展一個已存在的窗口的行為、而不用重新開發(fā)的有效途徑。想要獲得那些預(yù)定義控件窗口類(按鈕控件、編輯控件、列表控件、下
拉列表控件、靜態(tài)控件和滾動條控件)的功能而又要修改它們的某些行為的一個便利的方法就是對它們進(jìn)行子類化。例如,對于一個在對話框中的多行編輯框來說,
當(dāng)用戶按下Enter鍵時,對話框會關(guān)閉。通過對編輯控件子類化,一個應(yīng)用程序就能擁有一個可以往文本中插入回車和換行,而同時又不會關(guān)閉對話框的編輯控件,應(yīng)用程序不用為這個特殊的需要而去專門開發(fā)一個編輯控件。
子類化基礎(chǔ)
創(chuàng)建一個窗口的第一步是填充一個WNDCLASS 結(jié)構(gòu),并調(diào)用RegisterClass 函數(shù)來注冊一個窗口類。WNDCLASS 結(jié)構(gòu)的其中一個成員是這個窗口類的窗口過程地址,當(dāng)一個窗口被建立,32位版本的Microsoft Windows操作系統(tǒng)會取出WNDCLASS 結(jié)構(gòu)中的窗口過程地址,并把它復(fù)制到一個新的窗口信息結(jié)構(gòu)中。當(dāng)一條消息被發(fā)往這個窗口時,Windows通過窗口信息結(jié)構(gòu)中的這個地址調(diào)用這個窗口的窗口過程。為了子類化一個窗口,你要用一個新的窗口過程地址取代原窗口過程地址,從而使新的窗口過程可以接收發(fā)往原窗口過程的所有消息。
當(dāng)一個應(yīng)用程序子類化一個窗口時,它可對消息采取三種操作:(1)把消息傳遞給原窗口過程;(2)修改消息然后再傳遞給原窗口過程;(3)不再往下傳遞消息。
應(yīng)用程序子類化一個窗口時,可以決定什么情況下對所接收的消息做出反應(yīng),應(yīng)用程序可以在將消息傳遞給原窗口過程之前或(和)之后處理這條消息。
子類化的類型
有兩種子類化的類型,它們是實(shí)例子類化和全局子類化。.
實(shí)例子類化是子類化一個獨(dú)立的窗口信息結(jié)構(gòu),在實(shí)例子類化后,只有屬于一個特定的窗口實(shí)例的消息會被發(fā)送到新窗口過程。
全局子類化是替換一個窗口類的WNDCLASS 結(jié)
構(gòu)中的窗口過程地址,所有在這之后使用該窗口類建立起來的窗口都具有這個被替換的窗口過程地址。全局子類化只對那些在子類化生效之后創(chuàng)建的窗口有效,在進(jìn)
行子類化之前,如果已經(jīng)存在任何用這個被全局子類化的窗口類創(chuàng)建的窗口,這些已經(jīng)存在的窗口不會被子類化。如果應(yīng)用程序想要使子類化對這些已經(jīng)存在的窗口
生效,應(yīng)用程序必須子類化每一個已經(jīng)存在的該窗口類的實(shí)例。
Win32子類化規(guī)則
有兩條規(guī)則應(yīng)用到Win32下的實(shí)例子類化和全局子類化。
子類化僅被允許用在進(jìn)程內(nèi),一個應(yīng)用程序不能子類化屬于另一個進(jìn)程的窗口或窗口類。
這條規(guī)則的起因很簡單:Win32進(jìn)程具有獨(dú)立的進(jìn)程地址空間。在一個特定的進(jìn)程里,一個窗口過程有一個地址,而在另一個不同的進(jìn)程里,這個地址值并未指向這個窗口過程,結(jié)果就是,在一個進(jìn)程中,使用從另一個進(jìn)程獲得的地址替換后的地址并不能獲得期望的結(jié)果,因此32位的Windows不允許這種地址替換發(fā)生。SetWindowLong 和SetClassLong 函數(shù)中防止了這種類型的子類化發(fā)生。你不能子類化屬于另一個進(jìn)程的窗口或窗口類,你能做的就到此為止。
不過,也還是有些途徑能讓你把子類化的功能用到每一個進(jìn)程上。只要能得到位于某個進(jìn)程地址空間里的某個函數(shù),你就能該進(jìn)程里的任何東西進(jìn)行子類化。有幾個方法可以達(dá)到這個目的,其中最容易(也是最不講理的)的一個方法就是在下面這個注冊表鍵中添加一個動態(tài)鏈接庫名稱:
HKEY_LOCAL_MACHINE\Software\Microsoft\Windows NT\CurrentVersion\Windows\APPINIT_DLLS
這個鍵導(dǎo)致Windows把你的DLL附加到系統(tǒng)里的每一個進(jìn)程上。你的DLL需要一些能在它要子類化的事件發(fā)生時被喚醒的方法,通常一個WH_CBT鉤子可以實(shí)現(xiàn)。這個DLL可以監(jiān)視HCBT_CREATEWND 事件,然后子類化它想子類化的窗口。例子程序CTL3D就使用了WH_CBT鉤子去實(shí)現(xiàn)它的子類化,盡管它沒有使用注冊表鍵為每個進(jìn)程實(shí)現(xiàn)子類化,想要得到CTL3D功能的應(yīng)用程序可以把他鏈接到進(jìn)程里。
另一個把你的子類化代碼附加到每個進(jìn)程的方法是使用一個系統(tǒng)鉤子。當(dāng)一個系統(tǒng)鉤子在另一個進(jìn)程的上下文中被調(diào)用時,系統(tǒng)就把包含這個鉤子的DLL加載到這個進(jìn)程空間中。樣例CTL3D 的代碼按照使用當(dāng)前線程中本地WH_CBT 鉤子的方式使用系統(tǒng)WH_CBT 鉤子。
第三個把子類化代碼附加到另一個進(jìn)程方法更復(fù)雜:它使用OpenProcess, WriteProcessMemory和 CreateRemoteThread 函數(shù)將代碼注入其它進(jìn)程。我并不推薦這個方法,并且不打算更詳細(xì)地介紹怎么實(shí)現(xiàn)這個方法。如果有人堅持要使用這個方法, Jeffrey Richter 曾說過他計劃在Microsoft Systems Journal 中他的一個即將開辟的Win32 Q&A專欄中描述這項(xiàng)技術(shù)。
現(xiàn)在很多Windows 3.1的應(yīng)用程序子類化其它進(jìn)程來擴(kuò)展操作和增加一些很酷的功能。當(dāng)Windows轉(zhuǎn)向一個面向?qū)ο蟮南到y(tǒng)時,對象鏈接和嵌入技術(shù)(OLE)提供了更好的辦法去實(shí)現(xiàn)這些功能。在Windows的未來版本中,子類化其它進(jìn)程可能會變得更加困難,而使用OLE也許會更容易。我推薦的是,只要可能,你應(yīng)該讓你的應(yīng)用程序轉(zhuǎn)向OLE,而不要子類化其它進(jìn)程。
子類化操作不要直接使用原窗口過程地址。
在Win16中,一個應(yīng)用程序會去使用從SetWindowLong 或 SetClassLong函數(shù)返回的窗口過程地址來直接調(diào)用窗口過程,畢竟返回值只是一個簡單的指向函數(shù)的指針,為什么不使用它呢?在Win32中,這種做法卻是個禁忌。SetWindowLong 或 SetClassLong函數(shù)的返回值可能根本不是原窗口過程的指針。Win32可能會返回指向一個數(shù)據(jù)結(jié)構(gòu)的指針,該數(shù)據(jù)結(jié)構(gòu)能被用來調(diào)用當(dāng)前的窗口過程。這種情況發(fā)生在Windows NT中,當(dāng)一個應(yīng)用程序用一個非Unicode的窗口過程子類化一個Unicode窗口時,或者用一個Unicode的窗口過程子類化一個非Unicode窗口時。在這兩種情況下, 操作系統(tǒng)必須為窗口收到的消息執(zhí)行一個Unicode和ANSI之間的轉(zhuǎn)換。如果應(yīng)用程序直接使用指向這個結(jié)構(gòu)的指針調(diào)用窗口過程,應(yīng)用程序會立即導(dǎo)致一個異常。使用SetWindowLong 或 SetClassLong函數(shù)返回的窗口地址的唯一做法是將返回值作為參數(shù)調(diào)用CallWindowProc 函數(shù)
實(shí)例子類化
SetWindowLong函數(shù)用來子類化一個窗口的一個實(shí)例。應(yīng)用程序必須知道子類化函數(shù)的地址,子類化函數(shù)是這樣一個函數(shù):它用來接收從Windows發(fā)來的消息,并把消息傳遞給原窗口過程。子類化函數(shù)必須在應(yīng)用程序中或DLL的模塊定義文件中導(dǎo)出。
應(yīng)用程序子類化窗口時,使用將要被子類化的窗口的句柄、GWL_WNDPROC標(biāo)志(在WINDOWS.H中定義)以及新的子類化函數(shù)地址作為參數(shù)調(diào)用函數(shù) SetWindowLong。 函數(shù)SetWindowLong返回一個DWORD類型的值,它是窗口的原窗口過程地址,應(yīng)用程序應(yīng)該保存該地址以用于將截獲的消息傳遞給原窗口過程,以及將來為窗口移除子類化之用。應(yīng)用程序使用原窗口過程的地址以及Windows消息所使用的hWnd, Message, wParam和lParam 參數(shù)調(diào)用函數(shù)CallWindowProc 向原窗口過程傳遞消息。通常應(yīng)用程序只是簡單地把它從Windows接收來的數(shù)據(jù)傳遞給函數(shù)CallWindowProc。
應(yīng)用程序同時需要原窗口過程地址來為窗口移除子類化。應(yīng)用程序通過再次調(diào)用函數(shù)SetWindowLong 來為窗口移除子類化,應(yīng)用程序向函數(shù)傳遞原窗口過程地址、GWL_WNDPROC 標(biāo)志以及已經(jīng)被子類化的窗口的句柄。
下面的代碼演示子類化一個編輯框控件以及為它移除子類化:
LONG FAR PASCAL SubClassFunc(HWND hWnd,UINT Message,WPARAM wParam,LONG lParam);
FARPROC lpfnOldWndProc;
HWND hEditWnd;
//
// Create an edit control and subclass it.
// The details of this particular edit control are not important.
//
hEditWnd = CreateWindow("EDIT", "EDIT Test",
WS_CHILD | WS_VISIBLE | WS_BORDER ,
0, 0, 50, 50,
hWndMain,NULL,hInst,NULL);
//
// Now subclass the window that was just Created.
//
lpfnOldWndProc = (FARPROC)SetWindowLong(hEditWnd,GWL_WNDPROC, (DWORD) SubClassFunc);
.
.
.
//
// Remove the subclass for the edit control.
//
SetWindowLong(hEditWnd, GWL_WNDPROC, (DWORD) lpfnOldWndProc);
//
// Here is a sample subclass function.
//
LONG FAR PASCAL SubClassFunc(HWND hWnd,
UINT Message,WPARAM wParam,LONG lParam)
{
//
// When the focus is in an edit control inside a dialog box, the
// default ENTER key action will not occur.
//
if ( Message == WM_GETDLGCODE )
return DLGC_WANTALLKEYS;
return CallWindowProc(lpfnOldWndProc, hWnd, Message, wParam,lParam);
}
潛在的缺陷
只要留意下面的保障規(guī)則,實(shí)例子類化通常是安全的。
當(dāng)子類化一個窗口時,你必須了解誰在對窗口的行為負(fù)責(zé),例如,Windows負(fù)
責(zé)它所提供的所有控件的行為,而應(yīng)用程序則對它自己定義的所有窗口負(fù)責(zé)。子類化可以作用在同一進(jìn)程中的任何一個窗口上,但是,當(dāng)一個應(yīng)用程序子類化一個不
屬于它自己負(fù)責(zé)的窗口時,應(yīng)用程序必須確保子類化函數(shù)不會破壞那個窗口原有的行為。由于應(yīng)用程序并未控制那個窗口,它不應(yīng)該依賴任何在未來很有可能會被窗
口的擁有者改變的窗口信息。除非確切地知道窗口附加字節(jié)或窗口類附加字節(jié)的含義以及原窗口過程如何使用它們,否則一個子類化函數(shù)不應(yīng)該使用
它們。退一步說,即使應(yīng)用程序了解關(guān)于窗口附加字節(jié)或窗口類附加字節(jié)的每一件事,除非應(yīng)用程序負(fù)責(zé)這個窗口,否則也不要使用它們。如果一個應(yīng)用程序使用了
由另一個組件負(fù)責(zé)的窗口的窗口附加字節(jié),當(dāng)那個組件決定更新窗口并且改變附加字節(jié)的結(jié)構(gòu)時,子類化過程很有可能會失敗。正是因?yàn)檫@個原因,Microsoft 建議你不要子類化控件類,因?yàn)?/span>Windows負(fù)責(zé)著這些它所提供的控件,而且下一個版本的Windows可能會改變這些控件的外觀。如果你的應(yīng)用程序必須子類化一個Windows提供的控件,在新版本的Windows發(fā)布后,代碼很可能也需要更新。
由于實(shí)例子類化發(fā)生在一個窗口被創(chuàng)建之后,應(yīng)用程序子類化窗口無法向窗口添加任何附加字節(jié),應(yīng)用程序也許需要將被子類化的窗口的實(shí)例所需的所有數(shù)據(jù)保存在窗口的屬性列表中。
SetProp函數(shù)可以把屬性附加到一個窗口。應(yīng)用程序使用窗口的句柄、一個標(biāo)示屬性的字符串和屬性數(shù)據(jù)的句柄作為參數(shù)調(diào)用SetProp函數(shù)。數(shù)據(jù)的句柄通常從調(diào)用LocalAlloc 或 GlobalAlloc 函數(shù)獲得。當(dāng)應(yīng)用程序要使用一個窗口的屬性列表中的這些數(shù)據(jù)時,可以使用窗口的句柄和標(biāo)示屬性的字符串作參數(shù)調(diào)用GetProp函數(shù),它返回用SetProp函數(shù)設(shè)置的數(shù)據(jù)的句柄。當(dāng)應(yīng)用程序不再需要這些數(shù)據(jù)或者當(dāng)窗口將要被銷毀時,應(yīng)用程序必須使用窗口的句柄和標(biāo)示屬性的字符串作參數(shù)調(diào)用RemoveProp函數(shù)從屬性列表中移除這些屬性數(shù)據(jù),該函數(shù)返回數(shù)據(jù)的句柄,然后應(yīng)用程序使用該句柄調(diào)用函數(shù)LocalFree 或函數(shù)GlobalFree。 關(guān)于函數(shù)SetProp, GetProp和RemoveProp的更多信息,參見平臺SDK文檔。
當(dāng)一個應(yīng)用程序子類化一個已經(jīng)被子類化過的窗口時,所有的子類化會被按照們被設(shè)置時的相反順序移除。
全局子類化
全局子類化類似于實(shí)例子類化。應(yīng)用程序通過調(diào)用函數(shù)SetClassLong對一個窗口類進(jìn)行全局子類化,就象在實(shí)例子類化中一樣,應(yīng)用程序同樣需要子類化函數(shù)的地址,并且這個子類化函數(shù)必須在應(yīng)用程序中或DLL的模塊定義文件中導(dǎo)出。
要全局子類化一個窗口類,應(yīng)用程序必須擁有一個該類的窗口實(shí)例。想要獲得該類的窗口實(shí)例,大多數(shù)應(yīng)用程序采
取建立一個屬于將要被全局子類化的窗口類的窗口的方法,當(dāng)應(yīng)用程序要移除子類化,也必須有一個窗口句柄,該句柄應(yīng)該是屬于應(yīng)用程序要子類化的窗口類的,因
此,為此而專門創(chuàng)建并保存一個窗口是個不錯的辦法。如果應(yīng)用程序需要創(chuàng)建它所要子類化的窗口類的窗口實(shí)例,這個窗口實(shí)例通常應(yīng)該是不可見的。在擁有了一個
正確類型的窗口句柄之后,應(yīng)用程序可以使用該窗口句柄、GCL_WNDPROC 標(biāo)志(在WINDOWS.H 中有定義)和新子類化函數(shù)的地址作為參數(shù)調(diào)用函數(shù)SetClassLong ,該函數(shù)返回一個DWORD 值,該值是該窗口類的原窗口過程地址。原窗口過程地址在全局子類化中的用處和在實(shí)例子類化中一樣,窗口消息也象在實(shí)例子類化中一樣通過調(diào)用函數(shù)CallWindowProc傳遞給原窗口過程。應(yīng)用程序可以通過再次調(diào)用SetClassLong函數(shù)來從窗口類移除子類化,這時需通過傳遞參數(shù)是原窗口過程地址、GCL_WNDPROC 標(biāo)志和被子類化的窗口類的窗口實(shí)例句柄。全局子類化一個控件的應(yīng)用程序必須在應(yīng)用程序結(jié)束時移除所做的子類化。
下面的代碼演示了全局子類化一個編輯框控件以及為它移除子類化:
LONG FAR PASCAL SubClassFunc(HWND hWnd,UINT,Message,WORD wParam,LONG lParam);
FARPROC lpfnOldClassWndProc;
HWND hEditWnd;

//
// Create an edit control and subclass it.
// Notice that the edit control is not visible.
// Other details of this particular edit control are not important.
//
hEditWnd = CreateWindow("EDIT", "EDIT Test",
WS_CHILD,
0, 0, 50, 50,
hWndMain,
NULL,
hInst,
NULL);

lpfnOldClassWndProc = (FARPROC)SetClassLong(hEditWnd, GCL_WNDPROC, (DWORD)SubClassFunc);
.
.
.
//
// To remove the subclass:
//
SetClassLong(hEditWnd, GWL_WNDPROC, (DWORD) lpfnOldClassWndProc);
DestroyWindow(hEditWnd);

潛在的缺陷
全局子類化具有和實(shí)例子類化一樣的限制,除非明確知道原窗口過程如何使用窗口類和窗口實(shí)例的附加字節(jié),否則應(yīng)用程序不應(yīng)嘗試去使用它們。如果數(shù)據(jù)必須和一個窗口相關(guān)聯(lián),可以象實(shí)例子類化中介紹的一樣,使用窗口屬性列表。
在Win32中, 全局子類化不會對任何其它進(jìn)程中的窗口類或從這些類創(chuàng)建的窗口實(shí)例生效,這對于Win16環(huán)境是個重大的變化。在系統(tǒng)中,Windows分別為每個Win32進(jìn)程單獨(dú)保存窗口類的信息,可以參見MSDN中的技術(shù)文章Window Classes in Win32來了解Windows在這方面的細(xì)節(jié)。目前全局子類化不能對其它進(jìn)程生效,這對開發(fā)人員來講,是個有用的技術(shù)。在Win16中,
全局子類化對被子類化的窗口類的每一個窗口實(shí)例都生效:不僅僅是屬于執(zhí)行了子類化操作的應(yīng)用程序,還包括了屬于整個系統(tǒng)的,這點(diǎn)讓人感到失望。通常這是應(yīng)
用程序不想達(dá)到的效果,所以應(yīng)用程序不得不使用更不方便,不好用的方法來改變從系統(tǒng)窗口類創(chuàng)建的窗口實(shí)例行為。而現(xiàn)在,在Win32中,使用全局子類化卻是非常容易的。
超類化
子類化一個窗口類,導(dǎo)致原本屬于窗口過程的消息被發(fā)送至子類化函數(shù),然后該子類化函數(shù)再把消息傳遞給原窗口
過程,而超類化(也被稱作窗口類克隆)是創(chuàng)建一個新的窗口類。這個新窗口類使用一個已經(jīng)存在的窗口類的窗口過程,來為它自己添加和已經(jīng)存在的窗口類一樣的
功能,超類化是基于其它窗口類的――也被稱為基類。基類常常是Windows預(yù)定義的控件類,但它也可以是任何其它窗口類。
注意 不要超類化滾動條控件類,因?yàn)?/span>Windows 使用該類名來為滾動條提供標(biāo)準(zhǔn)的行為。
超類化擁有它自己的窗口過程――超類化過程,它能起和子類化函數(shù)一樣的作用。超類化過程可以對消息實(shí)施三種動作: (1)直接將消息傳遞給原窗口過程 。(2)在傳遞給原窗口過程前修改消息。 (3)不在往下傳遞消息。超類化可以在把消息傳遞給原窗口過程之前、之后或兩者都有的情況下對消息進(jìn)行操作。
和子類化函數(shù)不一樣的是,一個超類化過程也可以從Windows接收創(chuàng)建消息(例如WM_NCCREATE, WM_CREATE 之類的),超類化可以處理這些消息,但它必須把這些消息傳遞給原基類窗口過程,這樣基類窗口過程才能進(jìn)行初始化操作
應(yīng)用程序調(diào)用函數(shù)GetClassInfo 來使一個超類化基于一個基類。函數(shù)GetClassInfo 使用從基類的WNDCLASS 結(jié)構(gòu)得來的值填充一個新WNDCLASS結(jié)構(gòu)。然后超類化基類的應(yīng)用程序把新WNDCLASS結(jié)構(gòu)的hInstance域的值設(shè)置成應(yīng)用程序自己的實(shí)例句柄,同時也必須把lpszClassName域的值設(shè)置成它要給該超類化的新名稱。如果基類擁有一個菜單,超類化該基類的應(yīng)用程序必須提供一個新菜單,該新菜單必須和基類的菜單擁有相同的菜單標(biāo)識。如果該超類化打算處理WM_COMMAND消息的,并且不再把該消息傳遞給基類的窗口過程, 那么菜單的標(biāo)識可以不必和基類的一樣。函數(shù)GetClassInfo 不會返回WNDCLASS結(jié)構(gòu)中域 lpszMenuName, lpszClassName, 和 hInstance 的值。
最后一個必須在超類化的WNDCLASS 結(jié)構(gòu)中設(shè)置的是域lpfnWndProc,函數(shù)GetClassInfo 用原窗口過程的地址填充它。應(yīng)用程序必須保存這個地址,以便能用函數(shù)CallWindowProc把消息傳遞給基類的窗口過程。應(yīng)用程序要在WNDCLASS 結(jié)構(gòu)中把該地址值設(shè)置成它的超類化過程的地址。這個地址并不是個過程實(shí)例地址,因?yàn)楹瘮?shù)RegisterClass 才能得到過程實(shí)例地址。應(yīng)用程序可以修改WNDCLASS 結(jié)構(gòu)中其它域的值,以便符合應(yīng)用程序的需要。
應(yīng)用程序可以往窗口類附加字節(jié)和窗口實(shí)例附加字節(jié)后添加內(nèi)容,這是因?yàn)樗粤艘粋€新窗口類。當(dāng)應(yīng)用程序做這件事時,必須遵從兩個規(guī)則: (1) 原類附加字節(jié)和窗口實(shí)例附加字節(jié)不能被子類化覆蓋,這和在實(shí)例子類化與全局子類化中的原因一樣。(2) 如果應(yīng)用程序因自身需要為窗口類或窗口實(shí)例添加了附加字節(jié),它在引用這些附加字節(jié)時,必須保持是相對于基類所使用的附加字節(jié)數(shù)來引用的。而且因?yàn)槟硞€版本的基類所使用的附加字節(jié)數(shù)可能會與下一個版本不同,所以超類化自己的附加字節(jié)的起始偏移也因基類版本不同而不同。
當(dāng)填充完WNDCLASS 結(jié)構(gòu)后,應(yīng)用程序應(yīng)該調(diào)用函數(shù)RegisterClass 來注冊新的窗口類,現(xiàn)在,就可以創(chuàng)建并使用屬于該新窗口類的窗口實(shí)例了。
應(yīng)用程序通常是在Win16環(huán)境下使用超類化,因?yàn)樵?/span>Win16環(huán)境下全局子類化是令人沮喪的。現(xiàn)在在Win32下,
全局子類化不再令人失望,所以超類化就不再那么具有吸引力了。但在你的應(yīng)用程序要改變一些窗口的行為,而這些窗口又只是從一個系統(tǒng)窗口類所創(chuàng)建的所有窗口
中的一部分時,你仍然可以發(fā)現(xiàn)使用超類化是很有用的,相反,對從一個系統(tǒng)窗口類所創(chuàng)建的所有窗口都有效,那是全局子類化的功能。
總結(jié)
子類化是個強(qiáng)大的技術(shù),而且在Win32中的使用也沒有發(fā)生什么特別重大的改變,唯一的比較主要的變化是你不能再屬于另一個進(jìn)程的窗口或窗口類,雖然有方法可以繞過這個限制,如果你確實(shí)需要這種能力,我還是建議你把你的應(yīng)用程序移植到OLE,這比仍然依賴子類化更好。
好了,至此整篇文章都翻譯完了(終于趕在放假前弄完了),在Win32中安全的子類化(1)中,提供了本文的英語原文的鏈接,由于本人時間、水平有限,所以歡迎大家指正文中的錯誤和疏漏之處,謝謝!