冰楓論壇

標題: [C#] (HOOK 鉤子)監聽系統鍵盤消息 [打印本頁]

作者: whitefox    時間: 2023-10-15 00:35
標題: [C#] (HOOK 鉤子)監聽系統鍵盤消息
當焦點不在程式上時,用程式加入監控按下鍵盤事件是無法捕抓到相關鍵盤消息!
這時就要用HOOK監聽系統整體的消息發送
先創一個專門監聽功能的類別,這裡命名空間 SystemHook
  1. using System;using System.Collections.Generic;
  2. using System.Text;
  3. using System.Runtime.InteropServices;
  4. using System.Windows.Forms;
  5. using System.Reflection;

  6. namespace SystemHook
  7. {
  8.     class KeyboardHook
  9.     {
  10.         public event KeyEventHandler KeyDownEvent;
  11.         public event KeyPressEventHandler KeyPressEvent;
  12.         public event KeyEventHandler KeyUpEvent;

  13.         public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
  14.         static int hKeyboardHook = 0;
  15.         public const int WH_KEYBOARD_LL = 13;
  16.         HookProc KeyboardHookProcedure;

  17.         //鍵盤鉤子結構
  18.         [StructLayout(LayoutKind.Sequential)]
  19.         public class KeyboardHookStruct
  20.         {
  21.             public int vkCode;
  22.             public int scanCode;
  23.             public int flags;
  24.             public int time;
  25.             public int dwExtraInfo;
  26.         }

  27.         // 裝載鉤子
  28.         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
  29.         public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);

  30.         // 卸載鉤子
  31.         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
  32.         public static extern bool UnhookWindowsHookEx(int idHook);

  33.         // 呼叫下一個鉤子
  34.         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
  35.         public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);

  36.         // 取得當前執行緒編號
  37.         [DllImport("kernel32.dll")]
  38.         static extern int GetCurrentThreadId();

  39.         // 使用WINDOWS API函數取代取得目前執行個體的函數,防止鉤子失效
  40.         [DllImport("kernel32.dll")]
  41.         public static extern IntPtr GetModuleHandle(string name);

  42.         public void Start()
  43.         {
  44.             // 裝載鍵盤鉤子
  45.             if (hKeyboardHook == 0)
  46.             {
  47.                 KeyboardHookProcedure = new HookProc(KeyboardHookProc);
  48.                 hKeyboardHook = SetWindowsHookEx(WH_KEYBOARD_LL, KeyboardHookProcedure, GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName), 0);

  49.                 if (hKeyboardHook == 0)
  50.                 {
  51.                     Stop();
  52.                     throw new Exception("裝載鍵盤鉤子失敗");
  53.                 }
  54.             }
  55.         }

  56.         public void Stop()
  57.         {
  58.             bool retKeyboard = true;
  59.             if (hKeyboardHook != 0)
  60.             {
  61.                 retKeyboard = UnhookWindowsHookEx(hKeyboardHook);
  62.                 hKeyboardHook = 0;
  63.             }
  64.             if (!(retKeyboard)) throw new Exception("卸載鉤子失敗!");
  65.         }

  66.         // ToAscii 轉換指定的虛擬鍵碼和鍵盤狀態的對應字元或字元
  67.         [DllImport("user32")]
  68.         public static extern int ToAscii(int uVirtKey,
  69.                                          int uScanCode,
  70.                                          byte[] lpbKeyState,
  71.                                          byte[] lpwTransKey,
  72.                                          int fuState);

  73.         // 取得按鍵的狀態
  74.         [DllImport("user32")]
  75.         public static extern int GetKeyboardState(byte[] pbKeyState);

  76.         [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
  77.         private static extern short GetKeyState(int vKey);

  78.         private const int WM_KEYDOWN = 0x100;
  79.         private const int WM_KEYUP = 0x101;
  80.         private const int WM_SYSKEYDOWN = 0x104;
  81.         private const int WM_SYSKEYUP = 0x105;

  82.         private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
  83.         {
  84.             // 監聽鍵盤事件
  85.             if ((nCode >= 0) && (KeyDownEvent != null || KeyUpEvent != null || KeyPressEvent != null))
  86.             {
  87.                 KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct));

  88.                 if (KeyDownEvent != null && (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN))
  89.                 {
  90.                     Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
  91.                     KeyEventArgs e = new KeyEventArgs(keyData);
  92.                     KeyDownEvent(this, e);
  93.                 }

  94.                 // 按下鍵盤
  95.                 if (KeyPressEvent != null && wParam == WM_KEYDOWN)
  96.                 {
  97.                     byte[] keyState = new byte[256];
  98.                     GetKeyboardState(keyState);

  99.                     byte[] inBuffer = new byte[2];
  100.                     if (ToAscii(MyKeyboardHookStruct.vkCode, MyKeyboardHookStruct.scanCode, keyState, inBuffer, MyKeyboardHookStruct.flags) == 1)
  101.                     {
  102.                         KeyPressEventArgs e = new KeyPressEventArgs((char)inBuffer[0]);
  103.                         KeyPressEvent(this, e);
  104.                     }
  105.                 }

  106.                 // 放開鍵盤
  107.                 if (KeyUpEvent != null && (wParam == WM_KEYUP || wParam == WM_SYSKEYUP))
  108.                 {
  109.                     Keys keyData = (Keys)MyKeyboardHookStruct.vkCode;
  110.                     KeyEventArgs e = new KeyEventArgs(keyData);
  111.                     KeyUpEvent(this, e);
  112.                 }

  113.             }
  114.             // 如果返回1,則結束訊息,這個訊息到此為止,不再傳遞。
  115.             // 如果回傳0或呼叫CallNextHookEx函數,則訊息離開這個鉤子繼續往下傳遞,也就是傳給訊息真正的接受者
  116.             return CallNextHookEx(hKeyboardHook, nCode, wParam, lParam);
  117.         }
  118.         ~KeyboardHook()
  119.         {
  120.             Stop();
  121.         }
  122.     }
  123. }
複製代碼
實作上把就是訂閱KeyboardHook類別的KeyDownEvent事件再回調自己設定的事件處理器函數
這裡舉例一個按下Alt+A就彈出訊息提示
  1. using System.Runtime.InteropServices;
  2. using Microsoft.Win32;

  3. KeyboardHook kb_hook = new KeyboardHook();
  4. kb_hook.KeyDownEvent += new KeyEventHandler(hook_KeyDown);//鉤住鍵按下
  5. kb_hook.Start();

  6. private void hook_KeyDown(object sender, KeyEventArgs e)
  7. {
  8.         //判斷按下的按鍵(Alt + A)
  9.         if (e.KeyValue == (int)Keys.A && (int)Control.ModifierKeys == (int)Keys.Alt)
  10.         {
  11.                 System.Windows.Forms.MessageBox.Show(“按下了指定快捷键组合”);
  12.         }
  13. }
複製代碼
若要再多組合幾個按鍵就把判斷條件再拓展就可以了





歡迎光臨 冰楓論壇 (https://bingfong.com/) Powered by 冰楓