系列首篇:建立自動化皮膚引擎的起點 — 使用 Windows CBT Hook (WH_CBT) 攔截系統中所有視窗的建立與啟動訊息,自動掛接皮膚邏輯,無需手動修改每個 Form 代碼。
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 (idHook=5) — Computer-Based Training Hook,專門攔截視窗 啟動、建立、銷毀、最小化/最大化、移動、大小變更 等關鍵生命週期事件 (對應 HCBT_CREATEWND, HCBT_ACTIVATE, HCBT_DESTROYWND 等 nCode)。適合在視窗完全建立前/後注入皮膚邏輯。
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);
}
}
_cbtHook 必須為靜態欄位保持參考,否則 GC 回收導致 Hook 回呼失效進而崩潰進程。SetWindowsHookEx 最後一個參數傳 AppDomain.GetCurrentThreadId() 針對當前 UI 執行緒掛鉤,符合 WinForms 單執行緒模型。#32770,無對應 Managed Control,直接記錄 HWND 稍後處理。Application.ApplicationExit 事件中呼叫 UnhookWindowsHookEx 釋放系統資源。
所有應用程式視窗 (含對話框) 自動被記錄,為後續 NativeWindow 掛接與 Region 設定鋪平道路。