窗口的子類化與超類化——子類化是窗口實例級別的,超類化是在窗口類(WNDCLASS)級別的(截獲發往另一個窗口的消息)


1. 子類化 

理論:子類化是這樣一種技術,它允許一個應用程序截獲發往另一個窗口的消息。一個應用程序通過截獲屬於另一個窗口的消息,從而實現增加、監視或者修改那個窗口的缺省行為。子類化是用來改變或者擴展一個已存在的窗口的行為、而不用重新開發的有效途徑。想要獲得那些預定義控件窗口類(按鈕控件、編輯控件、列表控件、下拉列表控件、靜態控件和滾動條控件)的功能而又要修改它們的某些行為的一個便利的方法就是對它們進行子類化。例如,對於一個在對話框中的多行編輯框來說,當用戶按下Enter鍵時,對話框會關閉。通過對編輯控件子類化,一個應用程序就能擁有一個可以往文本中插入回車和換行,而同時又不會關閉對話框的編輯控件,應用程序不用為這個特殊的需要而去專門開發一個編輯控件。

子類化使用消息的三種方式:當一個應用程序子類化一個窗口時,它可對消息采取三種操作:(1)把消息傳遞給原窗口過程;(2)修改消息然后再傳遞給原窗口過程;(3)不再往下傳遞消息。理管理好就行啦!

子類化僅被允許用在進程內,一個應用程序不能子類化屬於另一個進程的窗口或窗口類(即子類化僅用於修改自己的程序行為,而非修改別人的)。

有兩種子類化的類型,它們是實例子類化和全局子類化。
實例子類化是子類化一個獨立的窗口信息結構,實例子類化后,只有屬於一個特定的窗口實例的消息會被發送到新窗口過程。
全局子類化是替換一個窗口類的WNDCLASS結構中的窗口過程地址,所有在這之后使用該窗口類建立起來的窗口都具有這個被替換的窗口過程地址(所以超類化是全局子類化的一個替代方案)。全局子類化只對那些在子類化生效之后創建的窗口有效,在進行子類化之前,如果已經存在任何用這個被全局子類化的窗口類創建的窗口,這些已經存在的窗口不會被子類化。如果應用程序想要使子類化對這些已經存在的窗口生效,應用程序必須子類化每一個已經存在的該窗口類的實例。


改變一個已經存在的窗口實例的性質:消息處理與其他實例屬性。
在SDK編程范疇內,子類化就是改變一個窗口實例的窗口函數(通過GetWindowLong()和SetWindowLong()),子類化所要做的就是為某窗口實例編寫新的窗口函數。其操作是在實例級別上進行的。
在MFC中子類化的情況有所不同:所有MFC窗口有相同的窗口函數,由該窗口函數根據窗口句柄查找窗口實例,在把消息映射到該窗口類(class)得消息處理函數上。為了利用MFC的消息映射機制,不宜改變窗口函數(名),MFC也把子類化封裝在函數SubclassWindow()中。但子類化的本質沒有變:在實例級別影響窗口的消息及其處理。例:
Class  B :public A 

  ……
}
A  a; 
B  b; 
HWND ha=a.GetSafeHwnd();
b.SubclassWindow(ha); #當然A 和B 不一定是繼承關系。
注意:在被子類化的窗口銷毀之前,必須執行窗口的反子類化: 
b.UnSubclassWindow(); 


2 超類化
窗口超類化是在窗口類——WNDCLASS或WNDCLASSEX(非MFC類概念)級別進行的改變窗口類特征的
使用過程:首先獲得一個已存在的窗口類,然后設置窗口類,最后注冊該窗口類。
例:
WNDCLASSEX  wc; 
wc.cbSize=sizeof(wc); //Windows用來進行版本檢查的,與窗口特征無關 
GetClassInfoEx(hinst,”XXXXXX”,&wc);
 // hinst—定義窗口類XXXXXX的模塊的句柄,如為系統定義的窗口類(如:EDIT、BUTTON)則hinst=NULL.。 
wc.lpszClassName = “YYYYYYY”;//必須改變窗口類的名字 
wc.hbrBackGround = CreateSolidBrush(RGB(0,0.0));//改變背景刷 
wc.lpfnWndProc = NewWndProc;//改變窗口函數 
……
RegisterClassEx(&wc);// 注冊新窗口類 
//使用窗口類 
……
::CreateWindow(_T(“YYYYYYYY”,……);

故超類化只能改變自己創建的窗口的特征,而不能用於由Windows創建的窗口(如對話框上的按鈕就不能進行超類化) 。而子類化是實例級別上的,只要能獲得窗口的實例,就可對其子類化,這是唯一的子類化對於超類化的優勢。另外,凡是子類化可實現的,超類化都可實現,不過超類化用起來較麻煩。


3. 總結

(0) 子類化修改窗口過程函數,  超類化修改窗口類(新的窗口類名)
(1) 子類化是在窗口實例級別上的,超類化是在窗口類(WNDCLASS)級別上的。 
(2) 超類化可以完成比子類化更復雜的功能,在SDK范疇上,可以認為子類化是超類化的子集。 
(3) 子類化只能改變窗口創建后的性質,對於窗口創建期間無能為力(無法截獲ON_CREATE 事件),而超類化可以實現;超類化不能用於Windows已創建的窗口,子類化可以。 


4. 其他
在 眼見為實(2):介紹Windows的窗口、消息、子類化和超類化 這里有一個例子.. 
可以得出結論
a) 子類化的classname 是不會變化的, 而超類化使用新注冊classname
b) 子類化 & 超類化 描述的是一個動作 和實現方法沒什么關系..... 主要是子類化是SubclassWindow, SubclassDlgItem, 而超類化是RegisterClassEx(&newwindowclass)
c) 感覺具體沒有必要區分這些, 實現功能就行了, 呵呵 

參考:http://www.cppblog.com/bigsml/archive/2007/08/24/30780.aspx

--------------------------------------------------------------------------------------------

Delphi里的TButton就是使用超類化技術,包裝了Windows的原生Button,從而變成TButton的

--------------------------------------------------------------------------------------------

子類化:

// 保存窗口默認的消息響應函數指針
WNDPROC pSubclassOldEditProc;
// 用於替換子類化窗口的消息響應函數
LRESULT CALLBACK JcEditProcSubClass(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
    case WM_CHAR:
        {
            ::MessageBox(hWnd, "WM_CHAR響應", "子類化", MB_OK);
            return 0;
        }
    default: return ::CallWindowProc(pSubclassOldEditProc, hWnd, message, wParam, lParam);
    }
}

// 對創建好的窗體進行子類化代碼
   {
       // 創建
       HWND hEdit = CreateWindowEx(NULL, "EDIT", "SubClass", 
           WS_CHILD|WS_BORDER|ES_LEFT|ES_AUTOHSCROLL, 100,120, 128, 16, hWnd, NULL, hInstance, NULL);
       pSubclassOldEditProc = (WNDPROC)::SetWindowLong(hEdit, GWL_WNDPROC, (DWORD)JcEditProcSubClass);
       // 顯示
       ShowWindow(hEdit, nCmdShow);
       UpdateWindow(hWnd);
   }

 

超類化:

WNDPROC pSuperOldEditProc;// 保存窗口默認消息處理函數
// 用於替換的超類化消息響應函數
LRESULT CALLBACK JcEditProcSuper(HWND hWnd, UINT message, WPARAM wParam, LPARAM lParam)
{
    switch(message)
    {
    case WM_CHAR:
        {
            ::MessageBox(hWnd, "WM_CHAR響應", "超類化", MB_OK);
            return 0;
        }
    default: return ::CallWindowProc(pSuperOldEditProc, hWnd, message, wParam, lParam);
    }
}

// 創建超類化控件代碼
   {
       // 取得原控件信息
       WNDCLASSEX myeditClass;
       ::GetClassInfoEx(hInstance, "EDIT", &myeditClass);
       // 保存原控件默認消息處理函數
       pSuperOldEditProc = myeditClass.lpfnWndProc;
       // 設置替換的消息處理函數
       myeditClass.lpfnWndProc = JcEditProcSuper;
       // 指定新的窗口類名字
       myeditClass.lpszClassName = "JcilyEdit";
       // 設置結構體大小
       myeditClass.cbSize = sizeof(WNDCLASSEX);
       // 注冊新信息
       RegisterClassEx(&myeditClass);
       // 創建
       HWND hEdit = CreateWindowEx(NULL, myeditClass.lpszClassName, "SuperClass", 
           WS_CHILD|WS_BORDER|ES_LEFT|ES_AUTOHSCROLL, 100,100, 128, 16, hWnd, NULL, hInstance, NULL);
       // 顯示
       ShowWindow(hEdit, nCmdShow);
       UpdateWindow(hWnd);
   }

參考:

http://www.cnblogs.com/jcily/archive/2009/10/22/1587778.html
http://blog.csdn.net/chenhao518530/article/details/628556

http://www.cnblogs.com/tonybain/archive/2006/01/19/320366.html
http://www.cnblogs.com/tonybain/archive/2006/01/20/320788.html
http://www.cnblogs.com/tonybain/archive/2006/01/20/320887.html

http://www.fmddlmyy.cn/text19.html


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM