zoukankan      html  css  js  c++  java
  • 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

  • 相关阅读:
    实战 Windows下搭建Objectivec的编译环境
    C# 协变和逆变 精解(直观明了,简单易懂)
    求两个字符串的最大公共串
    [C++][数据结构]队列(queue)的实现
    转换一个矩阵(2维数组)为HTML Table
    [C++][数据结构][算法]单链式结构的深拷贝
    LaTeX 中的特殊符号
    [C++11][数据结构]自己的双链表实现
    现代诗十则
    [C++11][算法][穷举]输出背包问题的所有可满足解
  • 原文地址:https://www.cnblogs.com/mcc7/p/5010719.html
Copyright © 2011-2022 走看看