C# 使用Windows API实现键盘钩子的类
源码
/// KEYBOARD.CS
/// 本文件包含以下内容:
/// - KeyboardHook: 使用Windows API实现低级键盘钩子的类
/// - KeyboardHookEventHandler: 处理KeyboardHook类触发的KeyIntercepted事件的委托
/// - KeyboardHookEventArgs: 包含KeyIntercepted事件返回信息的EventArgs类
/// using System;
using System.Diagnostics;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Text;/// <summary>
/// 低级键盘拦截类,用于捕获和屏蔽系统按键
/// </summary>
public class KeyboardHook : IDisposable
{/// <summary>/// KeyboardHook构造函数接受的参数枚举/// </summary>public enum Parameters{None,AllowAltTab,AllowWindowsKey,AllowAltTabAndWindows,PassAllKeysToNextApp}// 内部参数private bool PassAllKeysToNextApp = false;private bool AllowAltTab = false;private bool AllowWindowsKey = false;// 键盘API常量private const int WH_KEYBOARD_LL = 13;private const int WM_KEYUP = 0x0101;private const int WM_SYSKEYUP = 0x0105;// 修饰键常量private const int VK_SHIFT = 0x10;private const int VK_CONTROL = 0x11;private const int VK_MENU = 0x12;private const int VK_CAPITAL = 0x14;// SetWindowsHookEx调用使用的变量private HookHandlerDelegate proc;private IntPtr hookID = IntPtr.Zero;internal delegate IntPtr HookHandlerDelegate(int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);/// <summary>/// 当低级钩子拦截到击键时触发的事件/// </summary>public event KeyboardHookEventHandler KeyIntercepted;// 按键时钩子返回的结构体internal struct KBDLLHOOKSTRUCT{public int vkCode;int scanCode;public int flags;int time;int dwExtraInfo;}#region 构造函数/// <summary>/// 设置键盘钩子以捕获所有按键,并不传递给其他应用/// </summary>public KeyboardHook(){proc = new HookHandlerDelegate(HookCallback);using (Process curProcess = Process.GetCurrentProcess())using (ProcessModule curModule = curProcess.MainModule){hookID = NativeMethods.SetWindowsHookEx(WH_KEYBOARD_LL, proc,NativeMethods.GetModuleHandle(curModule.ModuleName), 0);}}/// <summary>/// 使用自定义参数设置键盘钩子/// </summary>/// <param name="param">Parameters枚举中的有效名称,否则将使用默认参数Parameter.None</param>public KeyboardHook(string param): this(){if (!String.IsNullOrEmpty(param) && Enum.IsDefined(typeof(Parameters), param)){SetParameters((Parameters)Enum.Parse(typeof(Parameters), param));}}/// <summary>/// 使用自定义参数设置键盘钩子/// </summary>/// <param name="param">Parameters枚举值</param>public KeyboardHook(Parameters param): this(){SetParameters(param);}private void SetParameters(Parameters param){switch (param){case Parameters.None:break;case Parameters.AllowAltTab:AllowAltTab = true;break;case Parameters.AllowWindowsKey:AllowWindowsKey = true;break;case Parameters.AllowAltTabAndWindows:AllowAltTab = true;AllowWindowsKey = true;break;case Parameters.PassAllKeysToNextApp:PassAllKeysToNextApp = true;break;}}#endregion#region 检查修饰键/// <summary>/// 检查Alt、Shift、Control或CapsLock是否与其他键同时启用/// 根据需要对相关部分和返回类型进行修改/// </summary>private void CheckModifiers(){StringBuilder sb = new StringBuilder();if ((NativeMethods.GetKeyState(VK_CAPITAL) & 0x0001) != 0){// 大写锁定开启sb.AppendLine("大写锁定已启用");}if ((NativeMethods.GetKeyState(VK_SHIFT) & 0x8000) != 0){// Shift键按下sb.AppendLine("Shift键按下");}if ((NativeMethods.GetKeyState(VK_CONTROL) & 0x8000) != 0){// Control键按下sb.AppendLine("Control键按下");}if ((NativeMethods.GetKeyState(VK_MENU) & 0x8000) != 0){// Alt键按下sb.AppendLine("Alt键按下");}Console.WriteLine(sb.ToString());}#endregion#region 钩子回调方法/// <summary>/// 处理钩子捕获的按键事件/// </summary>private IntPtr HookCallback(int nCode, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam){bool AllowKey = PassAllKeysToNextApp;// 仅过滤KeyUp事件if (nCode >= 0){if (wParam == (IntPtr)WM_KEYUP || wParam == (IntPtr)WM_SYSKEYUP){// 检查修饰键,但仅当当前处理的键不是修饰键时// (换句话说,只有当Ctrl、Shift、CapsLock或Alt与其他键同时按下时才会运行CheckModifiers)if (!(lParam.vkCode >= 160 && lParam.vkCode <= 164)){CheckModifiers();}// 检查允许传递给Windows的按键组合//// Ctrl+Esc或Windows键if (AllowWindowsKey){switch (lParam.flags){// Ctrl+Esccase 0:if (lParam.vkCode == 27)AllowKey = true;break;// Windows键case 1:if ((lParam.vkCode == 91) || (lParam.vkCode == 92))AllowKey = true;break;}}// Alt+Tabif (AllowAltTab){if ((lParam.flags == 32) && (lParam.vkCode == 9))AllowKey = true;}OnKeyIntercepted(new KeyboardHookEventArgs(lParam.vkCode, AllowKey));}// 如果此键被屏蔽,返回虚拟值if (AllowKey == false)return (System.IntPtr)1;}// 将按键传递给下一个应用return NativeMethods.CallNextHookEx(hookID, nCode, wParam, ref lParam);}#endregion#region 事件处理/// <summary>/// 触发KeyIntercepted事件/// </summary>/// <param name="e">KeyboardHookEventArgs实例</param>public void OnKeyIntercepted(KeyboardHookEventArgs e){if (KeyIntercepted != null)KeyIntercepted(e);}/// <summary>/// KeyboardHook事件处理的委托/// </summary>/// <param name="e">InterceptKeysEventArgs实例</param>public delegate void KeyboardHookEventHandler(KeyboardHookEventArgs e);/// <summary>/// KeyboardHook类KeyIntercepted事件的事件参数/// </summary>public class KeyboardHookEventArgs : System.EventArgs{private string keyName;private int keyCode;private bool passThrough;/// <summary>/// 按下的键名/// </summary>public string KeyName{get { return keyName; }}/// <summary>/// 按下的键的虚拟键码/// </summary>public int KeyCode{get { return keyCode; }}/// <summary>/// 如果此按键组合被传递给其他应用则为true,被捕获则为false/// </summary>public bool PassThrough{get { return passThrough; }}public KeyboardHookEventArgs(int evtKeyCode, bool evtPassThrough){keyName = ((Keys)evtKeyCode).ToString();keyCode = evtKeyCode;passThrough = evtPassThrough;}}#endregion#region IDisposable接口实现/// <summary>/// 释放键盘钩子/// </summary>public void Dispose(){NativeMethods.UnhookWindowsHookEx(hookID);}#endregion#region 原生方法[ComVisibleAttribute(false),System.Security.SuppressUnmanagedCodeSecurity()]internal class NativeMethods{[DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]public static extern IntPtr GetModuleHandle(string lpModuleName);[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]public static extern IntPtr SetWindowsHookEx(int idHook,HookHandlerDelegate lpfn, IntPtr hMod, uint dwThreadId);[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)][return: MarshalAs(UnmanagedType.Bool)]public static extern bool UnhookWindowsHookEx(IntPtr hhk);[DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]public static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode,IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);[DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true, CallingConvention = CallingConvention.Winapi)]public static extern short GetKeyState(int keyCode);}#endregion
}
键盘钩子类使用指南
类初始化
键盘钩子功能由KeyboardHook
类实现(位于keyboard.cs)。该类继承IDisposable
接口,推荐在应用主程序中通过using
语句初始化:
static class Program{public static KeyboardHook kh;/// <summary>/// 应用程序的主入口点。/// </summary>[STAThread]static void Main(){using (kh = new KeyboardHook()){Application.EnableVisualStyles();Application.SetCompatibleTextRenderingDefault(false);Application.Run(new Form1());}}}
注意:主窗体需能访问此实例,建议将其存储在公共成员变量中(建议放在Program.cs)
构造函数选项
类提供三种构造方式:
构造函数 | 说明 |
---|---|
KeyboardHook() | 拦截所有按键,不传递给系统和其他应用 |
KeyboardHook(string param) | 通过字符串参数指定行为(需匹配Parameters枚举值) |
KeyboardHook(KeyboardHook.Parameters) | 通过枚举精确控制行为 |
参数枚举说明:
public enum Parameters
{None, // 拦截所有按键AllowAltTab, // 允许Alt+Tab切换AllowWindowsKey, // 允许Win键和Ctrl+EscAllowAltTabAndWindows, // 同时允许上述两种组合键PassAllKeysToNextApp // 完全禁用拦截(仅监控)
}
事件处理
当按键被拦截时,会触发KeyIntercepted
事件,包含以下信息:
-
KeyName
:按键名称(通过System.Windows.Forms.Keys
转换) -
KeyCode
:原始键值代码 -
PassThrough
:是否允许该按键传递到其他应用
事件注册示例:
kh.KeyIntercepted += new KeyboardHook.KeyboardHookEventHandler(kh_KeyIntercepted);
事件处理示例:
void kh_KeyIntercepted(KeyboardHookEventArgs e)
{// 执行自定义操作(显示按键名称)MessageBox.Show(e.KeyName);
}