using Newtonsoft.Json; using System; using System.Collections.Generic; using System.ComponentModel; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Windows.Forms; namespace Hook { public class Hook : IDisposable { public delegate int HookProc(WH_CODE nCode, Int32 wParam, IntPtr lParam); #region 常數和結構 public const int WM_KEYDOWN = 0x100; public const int WM_KEYUP = 0x101; public const int WM_SYSKEYDOWN = 0x104; public const int WM_SYSKEYUP = 0x105; public const int WH_KEYBOARD_LL = 13; #endregion public enum WH_CODE : int { WH_JOURNALRECORD = 0, WH_JOURNALPLAYBACK = 1, /// <summary> /// 進程鉤子 /// {系統級或線程級; 截獲鍵盤消息} /// </summary> WH_KEYBOARD = 2, /// <summary> /// 底層鍵盤鉤子 全局鉤子就是用這個 /// </summary> WH_KEYBOARD_LL = 13, } public enum HC_CODE : int { HC_ACTION = 0, HC_GETNEXT = 1, HC_SKIP = 2, HC_NOREMOVE = 3, HC_NOREM = 3, HC_SYSMODALON = 4, HC_SYSMODALOFF = 5 } [StructLayout(LayoutKind.Sequential)] public class KeyboardHookStruct { public int vkCode; //定一個虛擬鍵碼。該代碼必須有一個價值的範圍1至254 public int scanCode; // 指定的硬件掃描碼的關鍵 public int flags; // 鍵標誌 public int time; // 指定的時間戳記的這個訊息 public int dwExtraInfo; // 指定額外信息相關的信息 } /// <summary> /// 安裝鉤子 /// </summary> [DllImport("user32.dll", SetLastError = true, CallingConvention = CallingConvention.StdCall)] public static extern IntPtr SetWindowsHookEx(WH_CODE idHook, HookProc lpfn, IntPtr pInstance, uint threadId); /// <summary> /// 卸載鉤子 /// </summary> [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)] public static extern bool UnhookWindowsHookEx(IntPtr pHookHandle); /// <summary> /// 傳遞鉤子 /// </summary> [DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)] public static extern int CallNextHookEx(IntPtr pHookHandle, WH_CODE nCodem, Int32 wParam, IntPtr lParam); /// <summary> /// 獲取全部按鍵狀態 /// </summary> /// <param name="pbKeyState"></param> /// <returns>非0表示成功</returns> [DllImport("user32.dll")] public static extern int GetKeyboardState(byte[] pbKeyState); /// <summary> /// 獲取程序集模塊的句柄 /// </summary> /// <param name="lpModuleName"></param> /// <returns></returns> [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)] public static extern IntPtr GetModuleHandle(string lpModuleName); /// <summary> /// 獲取當前進程中的當前線程ID /// </summary> /// <returns></returns> [DllImport("kernel32.dll", CharSet = CharSet.Auto)] public static extern uint GetCurrentThreadId(); #region 私有變量 private byte[] mKeyState = new byte[256]; private Keys mKeyData = Keys.None; //專門用於判斷按鍵的狀態 /// <summary> /// 鍵盤鉤子句柄 /// </summary> private IntPtr mKetboardHook = IntPtr.Zero; /// <summary> /// 鍵盤鉤子委託實例 /// </summary> private HookProc mKeyboardHookProcedure; #endregion /// <summary> /// 構造函數 /// </summary> public Hook() { GetKeyboardState(this.mKeyState); } ~Hook() { Dispose(); } bool KeyDown = true; public bool Ekey = false; public bool Rkey = false; /// <summary> /// 鍵盤鉤子處理函數 /// </summary> private int KeyboardHookProcNew(WH_CODE nCode, Int32 wParam, IntPtr lParam) { /*全局鉤子應該這樣設定 KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct)); */ // 定義爲線程鉤子時,wParam的值是擊打的按鍵,與Keys裏的對應按鍵相同 if ((int)nCode == (int)HC_CODE.HC_NOREMOVE) { if (FormStatus) { mKeyData = (Keys)wParam; KeyEventArgs keyEvent = new KeyEventArgs(mKeyData); if (KeyDown && lParam.ToInt64() > 0) { //這裏簡單的通過lParam的值的正負情況與按鍵的狀態相關聯 if (mKeyData == Keys.Space && lParam.ToInt64() > 0) { this.OnSpaceKeyDown(); } } } } return CallNextHookEx(this.mKetboardHook, nCode, wParam, lParam); } #region 事件的聲明 // public event KeyEventHandler KeyDownEvent; private static readonly object _eventKeyDown = new object(); private EventHandlerList _events; protected EventHandlerList Events => _events ??= new EventHandlerList(); [Category("Property Changed")] public event KeyEventHandler KeyDownEvent { add { this.Events.AddHandler(_eventKeyDown, value); } remove { this.Events.RemoveHandler(_eventKeyDown, value); } } #endregion /// <summary> /// 鍵盤鉤子處理函數 /// </summary> private int KeyboardHookProc(WH_CODE nCode, Int32 wParam, IntPtr lParam) { /*全局鉤子應該這樣設定 KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct)); */ // 定義爲線程鉤子時,wParam的值是擊打的按鍵,與Keys裏的對應按鍵相同 if ((int)nCode == (int)HC_CODE.HC_NOREMOVE) { if (FormStatus) { // Debug.WriteLine($"{nCode} {(Keys)wParam} {lParam} "); mKeyData = (Keys)wParam; if (lParam.ToInt64() > int.MaxValue) { Debug.WriteLine($"鬆開了{mKeyData}鍵"); } if (lParam.ToInt64() > 0 && lParam.ToInt64() < int.MaxValue) { KeyEventArgs e = new(mKeyData);//獲取KeyEventArgs事件的相關信息 if (Events[_eventKeyDown] is KeyEventHandler handler) handler.Invoke(this, e); } } } return CallNextHookEx(this.mKetboardHook, nCode, wParam, lParam); } /// <summary> /// 安裝鉤子 /// </summary> /// <returns></returns> public bool InstallHook(bool status) { //線程鉤子時一定要通過這個取得的值纔是操作系統下真實的線程 uint result = GetCurrentThreadId(); if (this.mKetboardHook == IntPtr.Zero) { if (status) { this.mKeyboardHookProcedure = new HookProc(this.KeyboardHookProc); } else { this.mKeyboardHookProcedure = new HookProc(this.KeyboardHookProcNew); } //註冊線程鉤子時第三個參數是空 this.mKetboardHook = SetWindowsHookEx(WH_CODE.WH_KEYBOARD, this.mKeyboardHookProcedure, IntPtr.Zero, result); /* 如果是全局鉤子應該這樣使用 this.mKetboardHook = SetWindowsHookEx(WH_CODE.WH_KEYBOARD_LL, mKeyboardHookProcedure,GetModuleHandle(System.Diagnostics.Process.GetCurrentProcess().MainModule.ModuleName), 0); */ if (this.mKetboardHook == IntPtr.Zero) { return false; } } return true; } /// <summary> /// 卸載鉤子 /// </summary> /// <returns>true表示成功 </returns> public bool UnInstallHook() { bool result = true; if (this.mKetboardHook != IntPtr.Zero) { result = UnhookWindowsHookEx(this.mKetboardHook) && result; this.mKetboardHook = IntPtr.Zero; } return result; } public void Dispose() { UnInstallHook(); GC.SuppressFinalize(this); } } }