superclass
superclass是一種生成新的窗口類的方法。它的中心思想是依靠現有的窗口類,克隆出另一個窗口類。被克隆的類可以是Windows預定義的窗口類,這些預定義的窗口類有按鈕或下拉框控制等等。也可以是一般的類。克隆的窗口類使用被克隆的類(基類)的窗口消息處理函數。
克隆類可以有自己的窗口消息處理函數,也可以使用基類的窗口處理函數。
需要注意的是,superclass是在註冊窗口類時就改變了窗口的行爲。即通過指定基類的窗口函數或是自己定義的窗口函數。這與後面講到的subclass是不同的。後者是在窗口創建完畢後,通過修改窗口函數的地址等改變一個窗口的行爲的。
請看示例(摘自MSDN):
class CBeepButton: public CWindowImpl< CBeepButton > { public: DECLARE_WND_SUPERCLASS( _T("BeepButton"), _T("Button") ) BEGIN_MSG_MAP( CBeepButton ) MESSAGE_HANDLER( WM_LBUTTONDOWN, OnLButtonDown ) END_MSG_MAP() LRESULT OnLButtonDown( UINT, WPARAM, LPARAM, BOOL& bHandled ) { MessageBeep( MB_ICONASTERISK ); bHandled = FALSE; // alternatively: DefWindowProc() return 0; } }; // CBeepButton |
該類實現一個按鈕,在點擊它時,會有響聲。
該類的消息映射處理WM_LBUTTONDOWN消息。其它的消息由Windows缺省窗口函數處理。
在消息映射前面,有一個宏--DECLARE_WND_SUPERCLASS()。它的作用就是申明BeepButton是Button的一個superclass。
分析一下這個宏:
#define DECLARE_WND_SUPERCLASS(WndClassName, OrigWndClassName) \ static CWndClassInfo& GetWndClassInfo() \ { \ static CWndClassInfo wc = \ { \ { sizeof(WNDCLASSEX), 0, StartWindowProc, \ 0, 0, NULL, NULL, NULL, NULL, NULL, WndClassName, NULL }, \ OrigWndClassName, NULL, NULL, TRUE, 0, _T("") \ }; \ return wc; \ } |
這個宏定義了一個靜態函數GetWndClassInfo()。這個函數返回了一個窗口類註冊時用到的數據結構CWndClassInfo。該結構的詳細定義如下:
struct _ATL_WNDCLASSINFOA { WNDCLASSEXA m_wc; LPCSTR m_lpszOrigName; WNDPROC pWndProc; LPCSTR m_lpszCursorID; BOOL m_bSystemCursor; ATOM m_atom; CHAR m_szAutoName[13]; ATOM Register(WNDPROC* p) { return AtlModuleRegisterWndClassInfoA(&_Module, this, p); } }; struct _ATL_WNDCLASSINFOW { … … { return AtlModuleRegisterWndClassInfoW(&_Module, this, p); } }; typedef _ATL_WNDCLASSINFOA CWndClassInfoA; typedef _ATL_WNDCLASSINFOW CWndClassInfoW; #ifdef UNICODE #define CWndClassInfo CWndClassInfoW #else #define CWndClassInfo CWndClassInfoA #endif |
這個結構調用了一個靜態函數AtlModuleRegisterWndClassInfoA(&_Module, this, p);。這個函數的用處就是註冊窗口類。
它指定了WndClassName是OrigWdClassName的superclass。
subclass
subclass是普遍採用的一種擴展窗口功能的方法。它的大致原理如下。
在一個窗口創建完了之後,將該窗口的窗口函數替換成新的窗口消息處理函數。這個新的窗口函數可以對某些需要處理的特定的消息進行處理,然後再將處理傳給原來的窗口函數。
注意它與superclass的區別。
Superclass是以一個類爲原版,進行克隆。既在註冊新的窗口類時,使用的是基類窗口的窗口函數。
而subclass是在某一個窗口註冊並創建後,通過修改該窗口的窗口消息函數的地址而實現的。它是針對窗口實例。
看一個從MSDN來的例子:
class CNoNumEdit: public CWindowImpl< CNoNumEdit > { BEGIN_MSG_MAP( CNoNumEdit ) MESSAGE_HANDLER( WM_CHAR, OnChar ) END_MSG_MAP() LRESULT OnChar( UINT, WPARAM wParam, LPARAM, BOOL& bHandled ) { TCHAR ch = wParam; if( _T('0') <= ch && ch <= _T('9') ) MessageBeep( 0 ); else bHandled = FALSE; return 0; } }; |
這裏定義了一個只接收數字的編輯控件。即通過消息映射,定義了一個特殊的消息處理邏輯。
然後,我們使用CWindowImplT. SubclassWindow()來subclass一個編輯控件。
class CMyDialog: public CDialogImpl<CMyDialog> { public: enum { IDD = IDD_DIALOG1 }; BEGIN_MSG_MAP( CMyDialog ) MESSAGE_HANDLER( WM_INITDIALOG, OnInitDialog ) END_MSG_MAP() LRESULT OnInitDialog( UINT, WPARAM, LPARAM, BOOL& ) { ed.SubclassWindow( GetDlgItem( IDC_EDIT1 ) ); return 0; } CNoNumEdit ed; }; |
上述代碼中,ed.SubclassWindow( GetDlgItem( IDC_EDIT1 ) )語句是對IDC_EDIT1這個編輯控件進行subclass。該語句實際上是替換了編輯控件的窗口函數。
由於SubClassWindows()實現的機制和ATL封裝窗口函數的機制一樣,我們會在後面介紹ATL是怎麼實現它的。