| 摘 要:本文介紹了窗口子類(lèi)化(SubClassing)的概念、技術(shù)原理、作用以及在Visual C++6.0中的實(shí)現方法,并給出了一個(gè)具體應用實(shí)例。實(shí)踐證明,適當地使用窗口子類(lèi)化技術(shù),可以大大增強應用程序的功能。 關(guān)鍵字:子類(lèi)化, 窗口函數, 消息, Visual C++ 一、引言 在Windows編程中,如果我們想在窗口程序執行時(shí)改變它所包含的控件(對話(huà)框中的按鈕、下拉式菜單等)的某些行為,采用窗口子類(lèi)化技術(shù)是一個(gè)不錯的選擇??梢允褂脤σ延锌丶缮宇?lèi)的方式定義一個(gè)子類(lèi),而控件的消息處理則在新定義的子類(lèi)里定義。適當使用子類(lèi)化技術(shù)創(chuàng )建出容易使用的新窗口類(lèi),往往可以使你的程序界面更具人性化。 二、窗口子類(lèi)化技術(shù)概述[1] Windows的窗口類(lèi)是一個(gè)窗口模板,包含一個(gè)窗口所具有的部分窗口屬性。編寫(xiě)一個(gè)Windows程序時(shí)首先要做的工作就是注冊一個(gè)窗口類(lèi),然后基于此注冊的窗口類(lèi)創(chuàng )建一個(gè)新的窗口。在WIN32平臺中,注冊窗口類(lèi)的API函數是RegisterClass和RegisterClassEX,其中RegisterClassEX是推薦使用的函數,使用這個(gè)函數注冊窗口類(lèi)時(shí),需要先填寫(xiě)一個(gè)WNDCLASSEX結構。這個(gè)結構實(shí)際上反映了一個(gè)窗口類(lèi)的特征,一個(gè)窗口類(lèi)有本類(lèi)所有窗口公用的類(lèi)屬性、窗口函數、類(lèi)圖標和小圖標、類(lèi)鼠標、窗口背景刷、類(lèi)菜單,當然還有類(lèi)名。除此之外,每個(gè)類(lèi)還有一定大小的類(lèi)存儲區,可以用來(lái)存儲該類(lèi)的公共數據。 每一個(gè)創(chuàng )建的窗口都有一個(gè)窗口函數,其地址由WNDCLASSEX結構的lpfnWndProc參數設定,該窗口函數處理對應于該窗口類(lèi)的所有實(shí)例的消息。當創(chuàng )建一個(gè)窗口時(shí),Windows將分配一個(gè)內存塊,用來(lái)存放與該窗口相關(guān)的信息,并將參數lpfnWndProc從窗口類(lèi)內存塊拷貝到該內存塊中。當消息被分發(fā)到窗口時(shí),Windows檢查該窗口中內存塊中的lpfnWndProc值,并調用該內存塊地址上的窗口函數。 一個(gè)窗口的行為主要取決于它的窗口函數,如果能夠改變一個(gè)窗口的窗口函數,使它指向自己寫(xiě)的某個(gè)函數,那就意味著(zhù)發(fā)給這個(gè)窗口的各種消息將由我們自己寫(xiě)的這個(gè)函數來(lái)處理。 子類(lèi)化一個(gè)窗口,實(shí)際上就是改變窗口內存塊中的窗口函數的地址,使其指向用戶(hù)自定義的新的窗口函數入口,以實(shí)現用戶(hù)希望的窗口特性。 三、窗口子類(lèi)化的作用 窗口子類(lèi)化技術(shù)最大的特點(diǎn)就是能夠截取Windows的消息。一旦用戶(hù)自定義的窗口函數截取了傳向原窗口函數的消息,就可以對被截取的消息進(jìn)行如下處理[2]: n 將其傳給原來(lái)的窗口函數。這是對大多數消息應該采取的措施,因為子類(lèi)通常只對原來(lái)的窗口特性作少量的修改。 n 截取該消息,阻止其向原窗口函數發(fā)送。 n 修改該消息,修改完畢以后再向原窗口函數發(fā)送。 Windows SDK提供了一些設計好的窗口類(lèi),如EDIT、LISTBOX、TREEVIEW等。通過(guò)截取這些通用窗口類(lèi)的消息,用戶(hù)程序可以為它們添加新的特性,改善其外觀(guān),擴充其功能。 子類(lèi)化的優(yōu)點(diǎn)主要體現在以下兩個(gè)方面:首先,它不需要創(chuàng )建新的窗口類(lèi),不需要了解一個(gè)窗口的窗口過(guò)程。這在原來(lái)的窗口函數是由別人編寫(xiě),而且創(chuàng )建過(guò)程不可見(jiàn)的情況下非常有用;其次,子類(lèi)化比較容易實(shí)現,因為所有要做的工作僅僅就是寫(xiě)一個(gè)窗口函數。 四、在VC中實(shí)現窗口子類(lèi)化 上面介紹的子類(lèi)化是從Windows本身的窗口函數概念來(lái)講的,實(shí)際上屬于SDK(Software Development Kit)編程的范疇,在MFC中情況有所不同。下面將分別描述在這兩種情況下窗口子類(lèi)化實(shí)現的方法。 4.1 VC中基于SDK編程的窗口子類(lèi)化 VC中基于SDK編程的窗口子類(lèi)化的基本步驟如下: (1) 正常創(chuàng )建原始窗口,得到窗口的句柄。 (2) 調用GetWindowLong得到原來(lái)的窗口函數OldWndProc。 (3) 調用SetWindowLong設置新的窗口函數NewWndProc。 新的窗口函數的代碼如下所示: LRESULT NewWndProc(HWND hWnd,UINT message,WPARAM wParam,LPARAM lParam) { if(message==WM_IcareIt) { //截取自己感興趣的消息,作一些處理,達到改變特性的目的 } //必要時(shí)可以調用原來(lái)的窗口函數,使被子類(lèi)化的窗口仍具有原來(lái)的很多特性 return CallWndowProc(OldWndProc,hWnd,message,wParam,lParam); } 值得注意的是,在調用舊的窗口函數時(shí),不能直接用OldWndProc(…),而必須用函數CallWndProc進(jìn)行調用,否則會(huì )出現堆棧錯誤。 4.2 MFC編程中的窗口子類(lèi)化 MFC窗口實(shí)際上已經(jīng)是被子類(lèi)化的窗口。所有的MFC窗口共享同一個(gè)窗口函數,由這個(gè)窗口函數根據窗口句柄,查找這個(gè)窗口對應的CWnd派生類(lèi)實(shí)例,再通過(guò)消息映射這個(gè)窗口類(lèi)的消息處理函數。鑒于以上原因,在MFC中要子類(lèi)化一個(gè)窗口就比較容易了,因為你的任務(wù)只是編寫(xiě)一個(gè)新的MFC窗口類(lèi)而不需要寫(xiě)一個(gè)窗口函數。 假如我們現在有一個(gè)對話(huà)框,里面有一個(gè)編輯控件,我們只希望在該控件中接受非數字字符輸入,我們可以攔截WM_CHAR消息,在它的處理函數中忽略任何數字的輸入。MFC編程中窗口子類(lèi)化的具體實(shí)現步驟在下一節筆者將用一個(gè)簡(jiǎn)單的實(shí)例來(lái)加以說(shuō)明。 五、VC中窗口子類(lèi)化的應用舉例 MFC為廣大編程者提供了很多功能豐富的窗口類(lèi),如果能在這些通用窗口類(lèi)的基礎上進(jìn)行子類(lèi)化的話(huà),將會(huì )給編程者帶來(lái)很多便利。下面舉一個(gè)例子來(lái)說(shuō)明MFC編程中的子類(lèi)化是多么的簡(jiǎn)單易行。該例完成上面提到的在編輯控件只接受非數字字符輸入的功能。實(shí)現這個(gè)子類(lèi)化的基本步驟和相關(guān)代碼如下: (1)利用AppWziard創(chuàng )建一個(gè)基于對話(huà)框的程序SubClassing。 (2)對MFC提供的標準的對話(huà)框中的控件進(jìn)行修改,刪除MFC提供的靜態(tài)文本控件,添加自己的一個(gè)編輯控件,設置新控件的ID為IDC_EDIT。合理布置對話(huà)框上各控件的位置,使程序界面布局合理、美觀(guān)。 (3)用ClassWizard從CEdit類(lèi)派生一個(gè)新的窗口類(lèi),新窗口的窗口類(lèi)叫CNoNumEdit。截取CNoNumEdit類(lèi)的WM_CHAR消息,在OnChar函中完成忽略任何數字的輸入的處理。實(shí)現代碼如下: void CNoNumEdit::OnChar(UINT nChar, UINT nRepCnt, UINT nFlags) { TCHAR ch=nChar; if(ch>=_T('0')&&ch<=_T('9')) { AfxMessageBox(("請不要輸入數字!"),MB_OK); //當輸入數字字符時(shí)將被忽略,并顯示警告信息 return; } CEdit::OnChar(nChar, nRepCnt, nFlags);//輸入為非數字字符時(shí)調用原處理函數 } (4)在對話(huà)框窗口類(lèi)CSubClassingDlg的定義中添加變量CNoNumEdit ed。在CSubClassingDlg::OnInitDialog()函數中調用CWnd類(lèi)的成員函數SubClassWindow進(jìn)行子類(lèi)化。 ed.SubclassWindow(GetDlgItem(IDC_EDIT)->m_hWnd); (5) 在對話(huà)框窗口類(lèi)CsubClassing的OnDestroy中調用ed.UnSubClassWindow()執行窗口類(lèi)的反子類(lèi)化。 現在可以編譯執行這個(gè)程序了,當用戶(hù)輸入數字字符時(shí)將會(huì )忽略該輸入,并顯示警告信息。 六、結束語(yǔ) 在Windows編程中,適當使用窗口子類(lèi)化技術(shù),可以很方便地達到改變一個(gè)窗口的特性的目的。當然子類(lèi)化也存在其局限性。實(shí)際上,子類(lèi)化的概念是針對一個(gè)已經(jīng)創(chuàng )建的窗口來(lái)談的,所以修改窗口函數是在窗口創(chuàng )建之后進(jìn)行的,在窗口創(chuàng )建期間的消息無(wú)法捕獲,也就無(wú)法處理。另外有些窗口的特性與窗口類(lèi)本身的屬性有關(guān)。比如如果一個(gè)窗口類(lèi)沒(méi)有CS_DBLCLKS屬性的話(huà),那么要想通過(guò)子類(lèi)化這些窗口達到處理WM_LBUTTONDBLCLK消息的目的是無(wú)法實(shí)現的。對于子類(lèi)化的以上局限性,可以通過(guò)超類(lèi)化(SuperClassing)技術(shù)消除。有興趣的讀者可以參閱文獻[1]。 參考文獻 [1] 陳俊,鄭靜. Visual C++中窗口子類(lèi)化和超類(lèi)化技術(shù)的應用. 現代計算機[J],2002(5):79~82 [2] 鄧雙成,田海晏.VB中窗口子類(lèi)化技術(shù)的實(shí)現和應用.計算機應用[J],2000,20(12):53~55 |