核心程式碼
using System;
using System.Windows.Forms;
using System.Runtime.InteropServices;
namespace HookDemo
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void Form1_Load(object sender, EventArgs e)
{
Win32Hook hook = new Win32Hook();
hook.OnMouseChange += Hook_OnMouseChange;
hook.SetHook();
}
private void Hook_OnMouseChange(object sender, EventArgs e)
{
this.Text = Cursor.Position.ToString(); // 即時顯示滑鼠座標於標題列
}
}
public class Win32Hook
{
// 匯入 Windows API
[DllImport("kernel32.dll")]
public static extern int GetCurrentThreadId();
[DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
public static extern int SetWindowsHookEx(
HookType idHook,
HookProc lpfn,
int hMod,
int dwThreadId);
public enum HookType
{
WH_GETMESSAGE = 3,
WH_MOUSE = 7, // 滑鼠訊息
WH_KEYBOARD = 2, // 鍵盤訊息
WH_MOUSE_LL = 14, // 底層滑鼠攔截 (全域)
WH_KEYBOARD_LL = 13 // 底層鍵盤攔截 (全域)
}
public delegate int HookProc(int nCode, int wParam, int lParam);
public event EventHandler OnMouseChange;
public void SetHook()
{
// 安裝鉤子:WH_GETMESSAGE + 目前執行緒
SetWindowsHookEx(
HookType.WH_GETMESSAGE,
new HookProc(MyHookProc),
0,
GetCurrentThreadId());
}
private int MyHookProc(int nCode, int wParam, int lParam)
{
if (OnMouseChange != null)
{
OnMouseChange(null, EventArgs.Empty);
}
return CallNextHookEx(0, nCode, wParam, lParam); // 傳遞給下一個鉤子
}
[DllImport("user32.dll")]
private static extern int CallNextHookEx(int hhk, int nCode, int wParam, int lParam);
}
}
關鍵技術解析
| 技術點 | 說明 |
| SetWindowsHookEx | 安裝鉤子程序到鉤子鏈,參數:鉤子類型、回呼函式、模組控制代碼、執行緒 ID |
| WH_GETMESSAGE (3) | 攔截 GetMessage/PeekMessage 訊息,含滑鼠/鍵盤/視窗訊息 |
| WH_MOUSE_LL (14) | 低階滑鼠鉤子,**全域攔截** (不限本程序),需在非 UI 執行緒或註入 DLL |
| HookProc 委派 | 鉤子回呼函式簽名:nCode (處理代碼)、wParam (訊息 ID)、lParam (訊息參數指標) |
| CallNextHookEx | **必須呼叫**將訊息傳遞給鏈中下一個鉤子,否則系統訊息循環阻塞 |
| GetCurrentThreadId | 取得目前執行緒 ID,限定鉤子範圍為當前執行緒 (區域鉤子) |
鉤子類型選擇指南
| 需求 | 推薦鉤子類型 | 注意事項 |
| 僅監控自程序視窗訊息 | WH_GETMESSAGE / WH_MOUSE | 執行緒範圍,簡單安全 |
| 全域監控滑鼠/鍵盤 (含其他程式) | WH_MOUSE_LL / WH_KEYBOARD_LL | 需註入 DLL 或獨立執行緒,UAC 高權限可能受限 |
| 攔截並修改/阻擋訊息 | WH_MOUSE_LL + 回傳非 0 | 謹慎使用,可能導致系統無回應 |
現代替代方案
- Raw Input API (
RegisterRawInputDevices):取得原始輸入裝置資料,無鉤子效能問題
- Windows UI Automation:跨程式 UI 元素監控與操作
- GlobalMouseKeyHook (NuGet):封裝良好的全域鉤子庫,支援 .NET Core/5+
安全性與效能警示
- 鉤子回呼在**系統訊息泵執行緒**執行,**嚴禁執行耗時操作** (檔案 I/O、資料庫、MessageBox)
- 全域鉤子 (LL) 影響全系統效能,盡量縮小範圍改用區域鉤子
- 應用程式結束時**必須卸載鉤子** (
UnhookWindowsHookEx),否則導致系統資源洩漏
- 64 位元系統需確保簽名正確 (
IntPtr 而非 int 處理控制代碼)