控制項訊息處理函式由系統定義,通常無需自行提供。但需特殊控制時,可提供處理函式替換原有函式,處理完成後再呼叫控制項預設處理——這就是「子類別化」技術。
LONG GetWindowLong(HWND hWnd, int nIndex);
LONG SetWindowLong(HWND hWnd, int nIndex, LONG dwNewLong);
可存取的索引值(nIndex):
| 索引 | 意義 |
|---|---|
| GWL_EXSTYLE | 擴充視窗樣式 |
| GWL_STYLE | 視窗樣式 |
| GWL_WNDPROC | 訊息處理函式指標 |
| GWL_HINSTANCE | 執行個體控制代碼 |
| GWL_ID | 視窗 ID |
| GWL_USERDATA | 使用者自訂資料 |
| DWL_DLGPROC | 對話方塊訊息處理函式 |
| DWL_MSGRESULT | 訊息處理結果 |
| DWL_USER | 對話方塊使用者資料 |
替換控制項訊息處理函式:
OldMsgProc = (WNDPROC)SetWindowLong(hControlWnd, GWL_WNDPROC, (LONG)MyMsgProc);
原處理函式被 OldMsgProc 記錄。
LRESULT CallWindowProc(
WNDPROC lpPrevWndFunc, // 前一個處理函式
HWND hWnd, // 視窗控制代碼
UINT Msg, // 訊息
WPARAM wParam, // 參數 1
LPARAM lParam); // 參數 2
// 記錄原處理函式的全域變數
WNDPROC OldMsgProc;
// 新訊息處理函式
LRESULT MyMsgProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch (message)
{
case WM_LBUTTONDOWN:
::MessageBox(NULL, "click!", "", MB_OK);
break;
}
// 呼叫控制項原始訊息處理函式
return CallWindowProc(OldMsgProc, hwnd, message, wParam, lParam);
}
case WM_CREATE:
{
HWND hControlWnd = CreateWindowEx(0, "BUTTON",
TEXT("按鈕(&A)"),
WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON,
10, 10, 100, 100,
hwnd,
(HMENU)1000, // 控制項 ID
((LPCREATESTRUCT)lParam)->hInstance,
NULL);
// 嵌入新訊息處理函式
OldMsgProc = (WNDPROC)SetWindowLong(hControlWnd, GWL_WNDPROC, (LONG)MyMsgProc);
}
return 0;
| 特性 | 子類別化 | 超類別化 |
|---|---|---|
| 定義 | 修改現有視窗實例的處理函式 | 以系統類別為基礎註冊新類別 |
| 作用範圍 | 單一視窗實例 | 所有該新類別的實例 |
| 實作方式 | SetWindowLong(GWL_WNDPROC) | GetClassInfo + 修改 + RegisterClass |
| 繼承系統行為 | 透過 CallWindowProc | 透過 CallWindowProc 或 DefWindowProc |
| 適用場景 | 少數特定控制項需特殊行為 | 大量同類控制項共享自訂行為 |
Win64 下指標為 64 位元,需使用 GetWindowLongPtr/SetWindowLongPtr 與 GWLP_WNDPROC:
#ifdef _WIN64
#define SetWindowLongPtrGWL SetWindowLongPtr
#define GetWindowLongPtrGWL GetWindowLongPtr
#define GWLP_WNDPROC GWLP_WNDPROC
#else
#define SetWindowLongPtrGWL SetWindowLong
#define GetWindowLongPtrGWL GetWindowLong
#define GWLP_WNDPROC GWL_WNDPROC
#endif
OldMsgProc = (WNDPROC)SetWindowLongPtrGWL(hControlWnd, GWLP_WNDPROC, (LONG_PTR)MyMsgProc);
Spy++ 觀察訊息流向確認子類別化生效CallWindowProc 參數正確傳遞,避免堆疊損壞OldMsgProc 非 NULL 驗證原函式成功取得