源码
cs
/// 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+Esc
case 0:
if (lParam.vkCode == 27)
AllowKey = true;
break;
// Windows键
case 1:
if ((lParam.vkCode == 91) || (lParam.vkCode == 92))
AllowKey = true;
break;
}
}
// Alt+Tab
if (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
语句初始化:
cs
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) |
通过枚举精确控制行为 |
参数枚举说明:
cs
public enum Parameters
{
None, // 拦截所有按键
AllowAltTab, // 允许Alt+Tab切换
AllowWindowsKey, // 允许Win键和Ctrl+Esc
AllowAltTabAndWindows, // 同时允许上述两种组合键
PassAllKeysToNextApp // 完全禁用拦截(仅监控)
}
事件处理
当按键被拦截时,会触发KeyIntercepted
事件,包含以下信息:
-
KeyName
:按键名称(通过System.Windows.Forms.Keys
转换) -
KeyCode
:原始键值代码 -
PassThrough
:是否允许该按键传递到其他应用
事件注册示例:
cs
kh.KeyIntercepted += new KeyboardHook.KeyboardHookEventHandler(kh_KeyIntercepted);
事件处理示例:
cs
void kh_KeyIntercepted(KeyboardHookEventArgs e)
{
// 执行自定义操作(显示按键名称)
MessageBox.Show(e.KeyName);
}