WPF 利用键盘钩子来捕获键盘,做一些不为人知的事情...完整实例

简介: 原文:WPF 利用键盘钩子来捕获键盘,做一些不为人知的事情...完整实例键盘钩子是一种可以监控键盘操作的指令。 看到这句话是不是觉得其实键盘钩子可以做很多事情.   场景 当你的程序需要一个全局的快捷键时,可以考虑使用键盘钩子,如大家常用qq的截图快捷键,那么在WPF里怎么去实现呢? 当...
原文: WPF 利用键盘钩子来捕获键盘,做一些不为人知的事情...完整实例

键盘钩子是一种可以监控键盘操作的指令。

看到这句话是不是觉得其实键盘钩子可以做很多事情.

 

场景

当你的程序需要一个全局的快捷键时,可以考虑使用键盘钩子,如大家常用qq的截图快捷键,那么在WPF里怎么去实现呢?

当然不是直接在Window窗体里面去注册KeyDown、KeyUp,这样只有在程序是焦点的情况下才能触发,

我们这里要做的更为强大,即在非焦点下去获取到键盘的事件(要偷偷记录女朋友键盘记录的滚粗,当然我是在开玩笑,程序猿哪里有女朋友,我们只有男朋友(⊙0⊙))

 

实现

首先我们需要用到这么几个WinAPI

//安装钩子的函数 
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]

        public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);


        //卸下钩子的函数 

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]

        public static extern bool UnhookWindowsHookEx(int idHook);


        //下一个钩挂的函数 
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]

        public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);

大致的逻辑为:

安装键盘钩子,然后通过委托来处理获取到的键盘消息

 

整理为两个类(winapi[Win32Api.cs]类和键盘钩子[KeyboardHook.cs]类)如下

public class Win32Api
{
public const int WM_KEYDOWN = 0x100;

        public const int WM_KEYUP = 0x101;

        public const int WM_SYSKEYDOWN = 0x104;

        public const int WM_SYSKEYUP = 0x105;

        public const int WH_KEYBOARD_LL = 13;



        [StructLayout(LayoutKind.Sequential)] //声明键盘钩子的封送结构类型
        public class KeyboardHookStruct
        {

            public int vkCode; //表示一个在1到254间的虚似键盘码 

            public int scanCode; //表示硬件扫描码 

            public int flags;

            public int time;

            public int dwExtraInfo;

        }

public delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);

        //安装钩子的函数 
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);


        //卸下钩子的函数 

        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern bool UnhookWindowsHookEx(int idHook);


        //下一个钩挂的函数 
        [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);

[DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
        public static extern IntPtr GetModuleHandle(string lpModuleName);
}
public class KeyboardHook
    {
        int hHook;

        Win32Api.HookProc KeyboardHookDelegate;


/// <summary>
        /// 安装键盘钩子
        /// </summary>
        public void SetHook()
        {

            KeyboardHookDelegate = new Win32Api.HookProc(KeyboardHookProc);

            ProcessModule cModule = Process.GetCurrentProcess().MainModule;

            var mh = Win32Api.GetModuleHandle(cModule.ModuleName);

            hHook = Win32Api.SetWindowsHookEx(Win32Api.WH_KEYBOARD_LL, KeyboardHookDelegate, mh, 0);

        }

        /// <summary>
        /// 卸载键盘钩子
        /// </summary>
        public void UnHook()
        {

            Win32Api.UnhookWindowsHookEx(hHook);

        }
        
        /// <summary>
        /// 获取键盘消息
        /// </summary>
        /// <param name="nCode"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        private int KeyboardHookProc(int nCode, Int32 wParam, IntPtr lParam)
        {
            // 如果该消息被丢弃(nCode<0
            if (nCode >= 0)
            {

                Win32Api.KeyboardHookStruct KeyDataFromHook = (Win32Api.KeyboardHookStruct)Marshal.PtrToStructure(lParam, typeof(Win32Api.KeyboardHookStruct));

                int keyData = KeyDataFromHook.vkCode;

                //WM_KEYDOWN和WM_SYSKEYDOWN消息,将会引发OnKeyDownEvent事件
                if (OnKeyDownEvent != null && (wParam == Win32Api.WM_KEYDOWN || wParam == Win32Api.WM_SYSKEYDOWN))
                {
                        // 此处触发键盘按下事件
                        // keyData为按下键盘的值,对应 虚拟码

                }

                //WM_KEYUP和WM_SYSKEYUP消息,将引发OnKeyUpEvent事件 

                if (OnKeyUpEvent != null && (wParam == Win32Api.WM_KEYUP || wParam == Win32Api.WM_SYSKEYUP))
                {
                          // 此处触发键盘抬起事件
                }

            }

            return Win32Api.CallNextHookEx(hHook, nCode, wParam, lParam);

        }
}

 

当时拿在虚拟码的时候其实我是拒绝的,因为在接下来我竟然不知道要对这些虚拟码做什么处理,难道要一个一个转换才能在WPF里进一步的去判断按下的是什么键?

最后经博客园<Launcher>告知,在System.Windows.Input命名空间下其实已经封装了转换的方法

namespace System.Windows.Input
{
    // 摘要: 
    //     提供在 Win32 虚拟键和 WPFSystem.Windows.Input.Key 枚举之间进行转换的静态方法。
    public static class KeyInterop
    {
        // 摘要: 
        //     将 Win32 虚拟键转换为 WPFSystem.Windows.Input.Key。
        //
        // 参数: 
        //   virtualKey:
        //     要转换的虚拟键。
        //
        // 返回结果: 
        //     WPF 键。
        public static Key KeyFromVirtualKey(int virtualKey);
        //
        // 摘要: 
        //     将 WPFSystem.Windows.Input.Key 转换为 Win32 虚拟键。
        //
        // 参数: 
        //   key:
        //     要转换的 WPF。
        //
        // 返回结果: 
        //     Win32 虚拟键。
        public static int VirtualKeyFromKey(Key key);
    }
}

 

结果

于是我们开开心心的拿到了基友的键盘操作记录

KeyInterop.KeyFromVirtualKey(KeyData)

nono,我只是拿来做了一个钢琴键盘

 

代码已贴,恕不给基友另行提供Demo

目录
相关文章
|
3月前
|
C# UED 开发者
WPF与性能优化:掌握这些核心技巧,让你的应用从卡顿到丝滑,彻底告别延迟,实现响应速度质的飞跃——从布局到动画全面剖析与实例演示
【8月更文挑战第31天】本文通过对比优化前后的方法,详细探讨了提升WPF应用响应速度的策略。文章首先分析了常见的性能瓶颈,如复杂的XAML布局、耗时的事件处理、不当的数据绑定及繁重的动画效果。接着,通过具体示例展示了如何简化XAML结构、使用后台线程处理事件、调整数据绑定设置以及利用DirectX优化动画,从而有效提升应用性能。通过这些优化措施,WPF应用将更加流畅,用户体验也将得到显著改善。
239 1
|
前端开发 C# Windows
WPF鼠标、键盘、拖拽事件、用行为封装事件
本文主要介绍了WPF中常用的鼠标事件、键盘事件以及注意事项,同时使用一个案例讲解了拓展事件。除此之外,本文还讲述如何用行为(Behavior)来封装事件。
WPF界面异常:未将对象引用设置到对象实例
WPF界面异常:未将对象引用设置到对象实例
|
C#
C# WPF 中用代码模拟鼠标和键盘的操作
原文:C# WPF 中用代码模拟鼠标和键盘的操作   原文地址   C#开发者都知道,在Winform开发中,SendKeys类提供的方法是很实用的。
2244 0
|
C# 前端开发
WPF中的多进程(Threading)处理实例(一)
原文:WPF中的多进程(Threading)处理实例(一) 说明:希望通过揣摩这些案例,能进一步了解进程的工作原理。 1.方法一描述的是在同一窗口中,在计算素数的同时而不影响Canvas的工作。 方法1 1 #region Long-Running Calculat...
1580 0
|
C# Windows
WPF无边框捕获消息改变窗口大小
原文:WPF无边框捕获消息改变窗口大小 文章大部分转载自http://blog.csdn.net/fwj380891124,如有问题,请联系删除  最近一直在学习 WPF,看着别人做的WPF程序那么漂亮,眼红啊~ 很多漂亮的程序都是无边框的。
1215 0
|
C#
WPF,强制捕获鼠标事件,鼠标移出控件外依然可以执行强制捕获的鼠标事件
原文:WPF,强制捕获鼠标事件,鼠标移出控件外依然可以执行强制捕获的鼠标事件 在WPF中,只有鼠标位置在某个控件上的时候才会触发该控件的鼠标事件。例如,有两个控件都注册了MouseDown和MouseUp事件,在控件1上按下鼠标,不要放开,移动到控件2上再放开。
2317 0
|
C# Windows
WPF 窗体中获取键盘和鼠标无操作时的超时提示
原文:WPF 窗体中获取键盘和鼠标无操作时的超时提示 通过调用Windows API中的GetLastInputInfo来获取最后一次输入的时间 using System;using System.
899 0
|
C#
WPF 自定义键盘焦点样式(FocusVisualStyle)
原文:WPF 自定义键盘焦点样式(FocusVisualStyle) 版权声明:本作品采用知识共享署名-非商业性使用-相同方式共享 4.0 国际许可协议进行许可。欢迎转载、使用、重新发布,但务必保留文章署名吕毅(包含链接:http://blog.csdn.net/wpwalter/),不得用于商业目的,基于本文修改后的作品务必以相同的许可发布。
1349 0
|
C# 索引
WPF中元素拖拽的两个实例
原文:WPF中元素拖拽的两个实例   今天结合之前做过的一些拖拽的例子来对这个方面进行一些总结,这里主要用两个例子来说明在WPF中如何使用拖拽进行操作,元素拖拽是一个常见的操作,第一个拖拽的例子是将ListBox中的子元素拖拽到ListView的某一个节点,从而将该子元素作为当前节点的子节点。
1191 0