C#全局钩子和局部钩子记录
源自:https://blog.csdn.net/programvae/article/details/80292076
最近碰巧要使用键盘钩子,于是在网上搜索了一番,发现大多数博客的文章都是雷同的,根本就没有讲清楚全局钩子和局部钩子的区别,于是特开一贴,讲全局钩子和局部钩子捋一捋。也供后面的人学习。
因为大部分应用都应该采用局部钩子,所以我这儿使用的是局部钩子,而全局钩子的例子网上到处都是。
大部分网上参考文章都只是展示了全局钩子的写法,而线程钩子的写法和介绍相对少一些,特别是关键语句上如果定义的不正确是没有任何效果的,在自己反复尝试后决定留下一个正确的版本分享出来
代码如下
using System; 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 delegate int 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位的系统不一样。