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

WinForms 介面開發 (一):全域 Hook 攔截視窗建立 — CBT Hook 與 SkinEngine 架構啟動

     編輯:關於.NET

系列首篇:建立自動化皮膚引擎的起點 — 使用 Windows CBT Hook (WH_CBT) 攔截系統中所有視窗的建立與啟動訊息,自動掛接皮膚邏輯,無需手動修改每個 Form 代碼。

Windows Hook 基礎與三大 API

Hook 監控訊息傳遞,在訊息到達目標視窗程序前介入處理。核心三 API (均在 user32.dll):

[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, int hMod, int dwThreadId);

[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

[DllImport("User32.dll", CharSet = CharSet.Auto)]
public static extern bool UnhookWindowsHookEx(IntPtr hhk);

HookProc 委派簽名:IntPtr HookProc(int nCode, IntPtr wParam, IntPtr lParam)。

WH_CBT Hook 選擇理由

WH_CBT (idHook=5) — Computer-Based Training Hook,專門攔截視窗 啟動、建立、銷毀、最小化/最大化、移動、大小變更 等關鍵生命週期事件 (對應 HCBT_CREATEWND, HCBT_ACTIVATE, HCBT_DESTROYWND 等 nCode)。適合在視窗完全建立前/後注入皮膚邏輯。

SkinEngine 元件架構啟動流程

public partial class SkinEngine : Component
{
    private static HookProc _cbtHook;
    private static IntPtr _hookHandle;
    internal static ArrayList SkinHandleList = new ArrayList(); // 已皮膚化視窗句柄集合

    private void InternalConstructor()
    {
        if (!IsDesignMode && Engine == null)
        {
            Engine = this;
            if (_hookHandle == IntPtr.Zero)
            {
                _cbtHook = new HookProc(FnHookProc); // 必須保持委派存活防止 GC
                _hookHandle = NativeMethod.SetWindowsHookEx(5, _cbtHook, 0, AppDomain.GetCurrentThreadId());
                Application.ApplicationExit += Application_ApplicationExit; // 確保卸載 Hook
            }
        }
    }

    private static unsafe IntPtr FnHookProc(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (Engine != null && nCode == 5) // HCBT_CREATEWND
        {
            Control control = Control.FromHandle(wParam);
            if (control == null) // 可能是原生對話框 (#32770)
            {
                StringBuilder clsName = new StringBuilder(260);
                NativeMethod.GetClassName(wParam, clsName, 260);
                if (clsName.ToString() == "#32770")
                    SkinHandleList.Add(wParam);
            }
            else if (control is Form && !SkinHandleList.Contains(wParam))
            {
                SkinHandleList.Add(wParam);
                Debug.WriteLine("Hooked Form: " + control.Name);
            }
        }
        return NativeMethod.CallNextHookEx(_hookHandle, nCode, wParam, lParam);
    }
}

關鍵實作細節

  • 委派防 GC:_cbtHook 必須為靜態欄位保持參考,否則 GC 回收導致 Hook 回呼失效進而崩潰進程。
  • 執行緒親和性:SetWindowsHookEx 最後一個參數傳 AppDomain.GetCurrentThreadId() 針對當前 UI 執行緒掛鉤,符合 WinForms 單執行緒模型。
  • 對話框處理:原生對話框 (MessageBox, OpenFileDialog 等) 類別名為 #32770,無對應 Managed Control,直接記錄 HWND 稍後處理。
  • 清理機制:Application.ApplicationExit 事件中呼叫 UnhookWindowsHookEx 釋放系統資源。

執行驗證

Debug 輸出視窗顯示依序 Hook 到的 Form 名稱:Form1, Form2, AboutBox 等

所有應用程式視窗 (含對話框) 自動被記錄,為後續 NativeWindow 掛接與 Region 設定鋪平道路。

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