C#键盘钩子实现:屏蔽系统热键与全局事件捕获
本文介绍如何使用C#结合Windows API实现键盘钩子(Keyboard Hook),以拦截并屏蔽系统级热键或捕获全局按键事件。核心逻辑通过SetWindowsHookEx安装WH_KEYBOARD_LL钩子,在回调函数中解析消息并选择是否调用CallNextHookEx放行。
原理概述
C#实现的键盘钩子主要依赖 user32.dll 中的非托管函数。通过安装低级别键盘钩子(WH_KEYBOARD_LL),程序可以在消息到达目标窗口之前截获按键信号。关键点在于判断是否阻止消息继续传递:
1. 若需要屏蔽热键,不调用 CallNextHookEx 或直接返回特定值。
2. 若仅需监听而不停止输入,则调用 CallNextHookEx 将控制权交还给下一个钩子或系统。
核心类实现
以下代码展示了完整的 Hooks 类实现,包含私有变量定义、WinAPI引用、钩子处理逻辑以及安装/卸载方法。
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Reflection;
using System.IO;
namespace KeyboardHookDemo
{
class Hooks
{
#region 私有变量
private IntPtr m_pKeyboardHook = IntPtr.Zero; // 键盘钩子句柄
public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam); // 钩子委托声明
private HookProc m_KeyboardHookProcedure; // 键盘钩子委托实例
public const int idHook = 13; // WH_KEYBOARD_LL 的低级别键盘钩子标识
// 导入WinAPI函数
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
public static extern IntPtr SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr pInstance, int threadId); // 安装钩子
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
public static extern bool UnhookWindowsHookEx(IntPtr pHookHandle); // 卸载钩子
[DllImport("user32.dll", CallingConvention = CallingConvention.StdCall)]
public static extern int CallNextHookEx(IntPtr pHookHandle, int nCode, Int32 wParam, IntPtr lParam); // 传递给下一个钩子
// 结构体用于接收钩子数据
[StructLayout(LayoutKind.Sequential)]
public struct KeyMSG
{
public int vkCode;
public int scanCode;
public int flags;
public int time;
public int dwExtraInfo;
}
protected const int WM_QUERYENDSESSION = 0x0011;
protected const int WM_KEYDOWN = 0x100;
protected const int WM_KEYUP = 0x101;
protected const int WM_SYSKEYDOWN = 0x104;
protected const int WM_SYSKEYUP = 0x105;
// 虚拟键码常量
protected const byte VK_SHIFT = 0x10;
protected const byte VK_CAPITAL = 0x14;
protected const byte VK_NUMLOCK = 0x90;
protected const byte VK_LSHIFT = 0xA0;
protected const byte VK_RSHIFT = 0xA1;
protected const int VK_LWIN = 91;
protected const int VK_RWIN = 92;
protected const byte VK_LCONTROL = 0xA2;
protected const byte VK_RCONTROL = 0x3;
protected const byte VK_LALT = 0xA4;
protected const byte VK_RALT = 0xA5;
protected const byte LLKHF_ALTDOWN = 0x20;
// 状态标志
public bool PowerKeyBlocked = true; // 是否屏蔽Power键
public static int pp = 0; // 热键返回值: 1为屏蔽, 0为允许(根据具体需求调整)
public static bool isSet = false; // 是否设置屏蔽热键
public static bool isHotkey = false;
public static bool isInstall = false; // 是否安装钩子
#endregion
#region 事件的声明
public event KeyEventHandler KeyDown; // 键盘按下事件
public event KeyEventHandler KeyUp; // 键盘松开事件
public event KeyPressEventHandler KeyPress; // 键盘单击事件
#endregion
#region 方法
/// <summary>
/// 钩子捕获消息后,对消息进行处理
/// </summary>
private int KeyboardHookProc(int nCode, int wParam, IntPtr lParam)
{
if (nCode > -1 && (KeyDown != null || KeyUp != null || KeyPress != null))
{
// 将非托管内存转换为结构体
KeyMSG keyboardHookStruct = (KeyMSG)Marshal.PtrToStructure(lParam, typeof(KeyMSG));
KeyEventArgs e = new KeyEventArgs((Keys)(keyboardHookStruct.vkCode));
switch (wParam)
{
case WM_KEYDOWN:
case WM_SYSKEYDOWN:
if (KeyDown != null)
{
KeyDown(this, e);
}
break;
case WM_KEYUP:
case WM_SYSKEYUP:
if (KeyUp != null)
{
KeyUp(this, e);
}
break;
}
}
// 关键判断:如果需要屏蔽,不执行CallNextHookEx或返回非零值
// 这里示例中直接返回pp的值,若需完全屏蔽通常return 1即可
return pp;
}
#endregion
#region 安装、卸载钩子
/// <summary>
/// 安装钩子
/// </summary>
public bool Start()
{
// 注意:对于低级别钩子(WH_KEYBOARD_LL),pInstance通常传入当前模块句柄或0
// 此处保留原代码逻辑:IntPtr pInstance = (IntPtr)4194304;
// 实际工程中建议使用 Marshal.GetHINSTANCE(Assembly.GetExecutingAssembly().GetModules()[0])
IntPtr pInstance = IntPtr.Zero;
if (this.m_pKeyboardHook == IntPtr.Zero)
{
this.m_KeyboardHookProcedure = new HookProc(KeyboardHookProc);
// 安装全局键盘钩子
this.m_pKeyboardHook = SetWindowsHookEx(idHook, m_KeyboardHookProcedure, pInstance, 0);
if (this.m_pKeyboardHook == IntPtr.Zero)
{
this.Stop(); // 安装失败则清理
return false;
}
}
isInstall = true;
return true;
}
/// <summary>
/// 卸载钩子
/// </summary>
public bool Stop()
{
if (isInstall == false)
{
return true;
}
bool result = true;
if (this.m_pKeyboardHook != IntPtr.Zero)
{
result = (UnhookWindowsHookEx(this.m_pKeyboardHook) && result);
this.m_pKeyboardHook = IntPtr.Zero;
}
return result;
}
#endregion
}
}
使用注意事项
- 权限要求:安装全局钩子通常需要足够的权限,部分安全软件可能会拦截此类操作。
- 内存管理:确保在应用程序退出时调用
Stop()方法,否则可能导致InvalidOperation异常或钩子残留。 - 性能影响:全局钩子会在每个进程的消息队列中触发,过于频繁的回调可能影响系统性能,建议仅在必要时启用。
- 线程安全:钩子回调运行在注入进程的上下文中(虽然LL钩子在本地进程),事件触发时需考虑UI线程的跨线程访问问题。
常见问题排查
如果钩子安装后立即失败(Start() 返回 false):
- 检查是否已存在其他冲突的钩子。
- 确认
pInstance参数是否正确,对于LL钩子通常传IntPtr.Zero即可由系统自动处理。
以上就是基于C#实现键盘钩子(HOOK)以屏蔽系统热键或捕获全局按键的详细内容,更多关于C# WinAPI编程的资料请关注本站其它相关文章!
本站声明
本文内容仅供参考,不代表本站观点。转载或引用请注明出处。如发现内容问题,请通过联系我们页面反馈。













