天天看點

視窗的子類化與超類化——子類化是視窗執行個體級别的,超類化是在視窗類(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. 總結

子類化修改視窗過程函數,  超類化修改視窗類(新的視窗類名)

(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);
   }      

參考:

繼續閱讀