C#全局鉤子和局部鉤子記錄
阿新 • • 發佈:2019-01-19
原創 code too pmod 效果 display ptr == services
源自:https://blog.csdn.net/programvae/article/details/80292076
最近碰巧要使用鍵盤鉤子,於是在網上搜索了一番,發現大多數博客的文章都是雷同的,根本就沒有講清楚全局鉤子和局部鉤子的區別,於是特開一貼,講全局鉤子和局部鉤子捋一捋。也供後面的人學習。
因為大部分應用都應該采用局部鉤子,所以我這兒使用的是局部鉤子,而全局鉤子的例子網上到處都是。
大部分網上參考文章都只是展示了全局鉤子的寫法,而線程鉤子的寫法和介紹相對少一些,特別是關鍵語句上如果定義的不正確是沒有任何效果的,在自己反復嘗試後決定留下一個正確的版本分享出來
代碼如下
usingView CodeSystem; using System.Collections.Generic; using System.Linq; using System.Text; using System.Reflection; using System.Runtime.InteropServices; using System.Windows.Forms; namespace AssistToolSet.Util { /// <summary> /// 鍵盤鉤子類 /// </summary> public class Hook { public delegateint HookProc(WH_CODE nCode, Int32 wParam, IntPtr lParam); 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 } /// <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 #region 鍵盤事件 public event KeyEventHandler OnKeyDown; public event KeyEventHandler OnKeyUp; #endregion /// <summary> /// 構造函數 /// </summary> public Hook() { GetKeyboardState(this.mKeyState); } ~Hook() { UnInstallHook(); } /// <summary> /// 鍵盤鉤子處理函數 /// </summary> private int KeyboardHookProc(WH_CODE nCode, Int32 wParam, IntPtr lParam) { /*全局鉤子應該這樣設定 KeyboardHookStruct MyKeyboardHookStruct = (KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(KeyboardHookStruct)); */ // 定義為線程鉤子時,wParam的值是擊打的按鍵,與Keys裏的對應按鍵相同 if ((nCode == (int)HC_CODE.HC_ACTION) && (this.OnKeyDown != null || this.OnKeyUp != null)) { mKeyData = (Keys)wParam; KeyEventArgs keyEvent = new KeyEventArgs(mKeyData); //這裏簡單的通過lParam的值的正負情況與按鍵的狀態相關聯 if (lParam.ToInt32() > 0 && this.OnKeyDown != null) { this.OnKeyDown(this, keyEvent); } else if (lParam.ToInt32() < 0 && this.OnKeyUp != null) { this.OnKeyUp(this, keyEvent); } } if (ShortcutManagement.s_bHotkeyUsed) { ShortcutManagement.s_bHotkeyUsed = false; return 1; } return CallNextHookEx(this.mKetboardHook, nCode, wParam, lParam); } /// <summary> /// 安裝鉤子 /// </summary> /// <returns></returns> public bool InstallHook() { //線程鉤子時一定要通過這個取得的值才是操作系統下真實的線程 uint result = GetCurrentThreadId(); if (this.mKetboardHook == IntPtr.Zero) { this.mKeyboardHookProcedure = new HookProc(this.KeyboardHookProc); //註冊線程鉤子時第三個參數是空 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; } } } --------------------- 作者:PGEva 來源:CSDN 原文:https://blog.csdn.net/programvae/article/details/80292076 版權聲明:本文為博主原創文章,轉載請附上博文鏈接!
通過這次認知,意識到,以後如果要做這些接觸相關的api的時候,我們應該盡量去查官方文檔,而不是一開始就是查看別人的博客。應該以官方文檔為主。一定要記住鍵盤鉤子事件。
32位和64位的系統不一樣。
C#全局鉤子和局部鉤子記錄