首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
您现在的位置: 程式碼之家 >> 編程語言 >  >> C語言 >> VC >> 關於VC++

Win32 視窗子類化深度解析:從 C++ 多態到 SetWindowLong 實戰

     編輯:關於VC++

核心概念:什麼是子類化?

子類化 = **攔截目標視窗的特定訊息,自行處理後,選擇性轉交原視窗程序**。類比 C++ 繼承覆寫虛函式:


class Parent { void func() { cout << "Parent"; } };
class Child : Parent { void func() { cout << "Child"; } }; // 覆寫

Win32 無繼承機制,改用**視窗程序指標替換**達成同樣目的。

機制原理:SetWindowLong + CallWindowProc


WNDPROC g_OldEditProc; // 全域/靜態保存原程序

// 初始化時替換
g_OldEditProc = (WNDPROC)SetWindowLong(hEdit, GWL_WNDPROC, (LONG)NewEditProc);

// 新視窗程序
LRESULT CALLBACK NewEditProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
    if (msg == WM_CHAR) { // 攔截字元輸入
        if (wp != '.' && (wp < '0' || wp > '9') && wp != VK_BACK) {
            MessageBeep(MB_OK); return 0; // 擋下非數字/小數點/退格
        }
    }
    return CallWindowProc(g_OldEditProc, hWnd, msg, wp, lp); // 轉交原程序
}

關鍵點:

  • GWL_WNDPROC 索引對應視窗結構內的程序指標
  • 必須保存舊指標,否則原功能永久失效
  • CallWindowProc 正確處理 Unicode/ANSI 轉換與堆疊清理

完整實戰範例:浮點數限制編輯框 + 超連結靜態文字

對話框初始化 WM_INITDIALOG


case WM_INITDIALOG:
    g_OldEditProc = (WNDPROC)SetWindowLong(GetDlgItem(hDlg, IDC_EDIT),
                                           GWL_WNDPROC, (LONG)FloatEditProc);
    g_OldStaticProc = (WNDPROC)SetWindowLong(GetDlgItem(hDlg, IDC_ST_LINK),
                                             GWL_WNDPROC, (LONG)LinkStaticProc);
    break;

浮點數編輯框程序 FloatEditProc


LRESULT CALLBACK FloatEditProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
    if (msg == WM_CHAR) {
        // 允許:0-9、小數點(一次)、退格鍵
        if (wp != '.' && (wp < '0' || wp > '9') && wp != VK_BACK) {
            MessageBeep(MB_OK); return 0;
        }
        if (wp == '.') { // 簡易防重複小數點(完整版需檢查現有文字)
            // 可加強:GetWindowText 檢查是否已含 '.'
        }
    }
    return CallWindowProc(g_OldEditProc, hWnd, msg, wp, lp);
}

超連結靜態文字程序 LinkStaticProc


LRESULT CALLBACK LinkStaticProc(HWND hWnd, UINT msg, WPARAM wp, LPARAM lp) {
    switch (msg) {
        case WM_SETCURSOR:
            SetCursor(LoadCursor(NULL, IDC_HAND)); // 手型游標
            break;
        case WM_LBUTTONDOWN:
            ShellExecute(NULL, "open", "https://example.com", NULL, NULL, SW_SHOWNORMAL);
            break;
        default:
            return CallWindowProc(g_OldStaticProc, hWnd, msg, wp, lp);
    }
    return 0;
}

配合主對話框 WM_CTLCOLORSTATIC 設定外觀


case WM_CTLCOLORSTATIC:
    if ((HWND)lp == GetDlgItem(hDlg, IDC_ST_LINK)) {
        SetTextColor((HDC)wp, RGB(255,0,0)); // 紅字
        SetBkMode((HDC)wp, TRANSPARENT);
        return (LRESULT)GetSysColorBrush(COLOR_BTNFACE);
    }
    break;

必要條件:靜態文字控制項須設 SS_NOTIFY 樣式,才會接收 WM_LBUTTONDOWN 等滑鼠訊息。

進階議題與最佳實踐

議題建議
64 位元相容用 SetWindowLongPtr/GetWindowLongPtr + GWLP_WNDPROC,指標寬度正確
多執行緒子類化僅影響呼叫執行緒的視窗;跨執行緒 UI 需 PostMessage 封送
清理視窗銷毀前 SetWindowLong(..., g_OldProc) 還原,防止記憶體洩漏/崩潰
MFC 專案優先用 CWnd::SubclassWindow/CSubclassWnd 封裝,自動管理生命週期

總結

子類化是 Win32 客製化系統控制項的核心技術。掌握 SetWindowLongPtr → 攔截訊息 → CallWindowProc 三部曲,即可在不重寫控制項的前提下,注入任意行為:輸入限制、視覺特效、滑鼠互動、無障礙增強等。配合 MFC 封裝類別,工程化維護更輕鬆。

  1. 上一篇文章:
  2. 下一篇文章:
Copyright © 程式碼之家 All Rights Reserved