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

C# 全域鍵盤滑鼠攔截與監控實作指南

     編輯:關於C#

在 C# 中實作系統層級的鍵盤與滑鼠鉤子,一直是開發者面臨的挑戰。本文完整展示如何封裝 Windows API 實現全域攔截、監控與屏蔽功能,並解決委托被垃圾回收導致的 CallbackOnCollectedDelegate 問題。

核心 API 與流程

三大關鍵步驟:

  1. 安裝鉤子:SetWindowsHookEx(WH_KEYBOARD_LL / WH_MOUSE_LL, ...)
  2. 回調處理:CallNextHookEx 傳遞訊息給下一個鉤子
  3. 移除鉤子:UnhookWindowsHookEx 釋放資源

關鍵程式碼解析

封裝類別 Hocy_Hook 核心設計要點:

委托實例必須保持引用

private HookProc m_KeyboardHookProcedure;
private HookProc m_MouseHookProcedure;
// 切勿省略此欄位,否則委托會被 GC 回收觸發 MDA 錯誤

鍵盤鉤子處理函數

private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam) {
    switch (flags) {
        case "2": return 1; // 屏蔽模式:直接攔截不傳遞
        case "1": break;    // 監控模式:繼續處理事件
    }
    
    bool handled = false;
    if (nCode >= 0 && (OnKeyDown != null || OnKeyUp != null || OnKeyPress != null)) {
        KeyboardHookStruct khs = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));
        
        // KeyDown 事件
        if (OnKeyDown != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)) {
            Keys key = (Keys)khs.VKCode;
            KeyEventArgs e = new KeyEventArgs(key);
            OnKeyDown(this, e);
            handled |= e.Handled;
        }
        
        // KeyPress 事件(含大小寫判斷)
        if (OnKeyPress != null && wParam == WM_KEYDOWN) {
            // 取得 Shift、CapsLock 狀態轉換字元...
            OnKeyPress(this, e);
            handled |= e.Handled;
        }
        
        // KeyUp 事件
        if (OnKeyUp != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP)) {
            OnKeyUp(this, new KeyEventArgs((Keys)khs.VKCode));
        }
    }
    return handled ? 1 : CallNextHookEx(m_pKeyboardHook, nCode, wParam, lParam);
}

安裝與移除鉤子

public bool InstallHook(string flagsinfo) {
    this.flags = flagsinfo;
    IntPtr hInstance = Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().ManifestModule);
    
    if (m_pMouseHook == IntPtr.Zero) {
        m_MouseHookProcedure = new HookProc(MouseHookProc);
        m_pMouseHook = SetWindowsHookEx(WH_MOUSE_LL, m_MouseHookProcedure, hInstance, 0);
        if (m_pMouseHook == IntPtr.Zero) { UnInstallHook(); return false; }
    }
    
    if (m_pKeyboardHook == IntPtr.Zero) {
        m_KeyboardHookProcedure = new HookProc(KeyboardHookProc);
        m_pKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, m_KeyboardHookProcedure, hInstance, 0);
        if (m_pKeyboardHook == IntPtr.Zero) { UnInstallHook(); return false; }
    }
    return true;
}

public bool UnInstallHook() {
    bool result = true;
    if (m_pMouseHook != IntPtr.Zero) {
        result &= UnhookWindowsHookEx(m_pMouseHook);
        m_pMouseHook = IntPtr.Zero;
    }
    if (m_pKeyboardHook != IntPtr.Zero) {
        result &= UnhookWindowsHookEx(m_pKeyboardHook);
        m_pKeyboardHook = IntPtr.Zero;
    }
    return result;
}

使用範例

private Hocy_Hook hook_Main = new Hocy_Hook();

private void btnStartMonitor_Click(object sender, EventArgs e) {
    hook_Main.InstallHook("1"); // 監控模式
}

private void btnBlockKeyboard_Click(object sender, EventArgs e) {
    hook_Main.InstallHook("2"); // 屏蔽鍵盤
}

private void btnStop_Click(object sender, EventArgs e) {
    hook_Main.UnInstallHook();
}

常見坑點提醒

  • 模組控制代碼:GetHINSTANCE 取得的值必須為 4194304 (0x400000),否則安裝失敗
  • 全域 vs 執行緒鉤子:WH_KEYBOARD_LL 為全域低階鉤子,不需 DLL 注入
  • 64 位元相容性:確保專案目標平台設定正確,IntPtr 自動適配

程式執行介面示意圖

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