C# 使用Windows API实现键盘钩子的类

源码

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);
}
相关推荐
EanoJiang3 小时前
CSharp_base
c#
zhishishe3 小时前
工具指南:免费将 PDF 转换为 Word 的 10 个工具
android·windows·pdf·word
h39743 小时前
MFC文件-写MP4
c++·windows·音视频·mfc
love530love5 小时前
PyCharm 链接 Podman Desktop 的 podman-machine-default Linux 虚拟环境
linux·运维·windows·pycharm·podman
人猿泰飞7 小时前
【AI训练环境搭建】在Windows11上搭建WSL2+Ubuntu22.04+Tensorflow+GPU机器学习训练环境
windows·ubuntu·机器学习·wsl·gpu训练
十步杀一人_千里不留行8 小时前
面向 C# 初学者的完整教程
开发语言·c#
LcVong8 小时前
一篇文章学会开发第一个ASP.NET网页
后端·c#·asp.net·web
刘梦凡呀10 小时前
C# .NET Core 批量下载文件
windows·c#·.netcore