• [Silverlight]键盘钩子KeyboardHook


    前言

    一切的起因就是Silverlight对F10键根本没有响应。在按F10键时,根本不会触发KeyDown事件。

    Silverlight5之前的版本我不太清楚,不过Silverlight5新特性中有使用P/Invoke调用非托管代码。既然这样,做个键盘钩子不就解决了?我喜欢DllImport。

    正文

    先了解钩子相关的信息(SetWindowsHookEx、UnhookWindowsHookEx),下面是原生代码。

       1: private delegate int HookProc(int nCode, Int32 wParam, IntPtr lParam);
       2:  
       3: [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
       4: private static extern int SetWindowsHookEx(int idHook, HookProc lpfn, IntPtr hInstance, int threadId);
       5:  
       6: [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
       7: private static extern bool UnhookWindowsHookEx(int idHook);
       8:  
       9: [DllImport("user32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
      10: private static extern int CallNextHookEx(int idHook, int nCode, Int32 wParam, IntPtr lParam);
      11:  
      12: [DllImport("kernel32.dll", CharSet = CharSet.Auto, CallingConvention = CallingConvention.StdCall)]
      13: private static extern IntPtr GetModuleHandle(string lpModuleName);

    其中委托HookProc用于设置钩子的回调函数。

    处理回调的时候还会用到键盘钩子的封送结构。

       1: [StructLayout(LayoutKind.Sequential)]
       2: private class KeyboardHookStruct
       3: {
       4:     public int vkCode; //表示一个在1到254间的虚似键盘码 
       5:     public int scanCode; 
       6:     public int flags;
       7:     public int time;
       8:     public int dwExtraInfo;
       9: }

    以下为主体代码。

       1: //常量
       2: private const int WH_KEYBOARD_LL = 13; //低级键盘钩子
       3: private const int WM_KEYDOWN = 0x100;
       4: private const int WM_SYSKEYDOWN = 0x104;
       5:  
       6: private static int iKeyboardHook; //钩子句柄
       7: HookProc HookProcDelegate;
       8:  
       9: //回调函数
      10: [AllowReversePInvokeCalls]
      11: private int HookProcHandler(int nCode, Int32 wParam, IntPtr lParam)
      12: {
      13:     //可以增加当前窗口是否是活动窗口的判断,不然低级键盘钩子还会截获所有的键盘消息
      14:     //可以使用App.Current.MainWindow.IsActive
      15:     KeyboardHookStruct khs = new KeyboardHookStruct();
      16:     Marshal.PtrToStructure(lParam, khs);
      17:     //KeyDown
      18:     if (wParam == WM_KEYDOWN || wParam == WM_SYSKEYDOWN)
      19:     {
      20:         if (khs.vkCode == 0x79) //F10
      21:         {
      22:             //Do something
      23:         }
      24:     }
      25:     return CallNextHookEx(iKeyboardHook, nCode, wParam, lParam);
      26: }
      27:  
      28: /// <summary>
      29: /// 注册键盘钩子
      30: /// </summary>
      31: public void Hook()
      32: {
      33:     IntPtr ip = GetModuleHandle(System.Reflection.Assembly.GetExecutingAssembly().GetModules()[0].Name);
      34:     HookProcDelegate = new HookProc(HookProcHandler);
      35:     iKeyboardHook = SetWindowsHookEx(13, HookProcDelegate, ip, 0);
      36:     if (iKeyboardHook == 0)
      37:     {
      38:         throw new Exception("注册键盘钩子失败");
      39:     }
      40: }
      41:  
      42: /// <summary>
      43: /// 卸载键盘钩子
      44: /// </summary>
      45: public void UnHook()
      46: {
      47:     if (!UnhookWindowsHookEx(iKeyboardHook))
      48:     {
      49:         throw new Exception("卸载键盘钩子失败");
      50:     }
      51:     iKeyboardHook = 0;
      52: }

    回调函数的AllowReversePInvokeCalls属性允许非托管方法调用托管方法。

    结语

    现在截获F10就是小意思了。还可以根据这些封装一下,不过暂时不需要就先这样了。

    Tips:

    1. 注册钩子时SetWindowsHookEx函数的threadId参数设置为0,所以需要在回调函数里判断当前Silverlight窗体是否为活动窗体。

    2. KeyboardHookStruct.vkCode对应的键值是System.Windows.Forms命名空间里Keys枚举的键值,所以判断是否是F10键时用的是0x79,而不是System.Windows.Input命名空间里Key枚举的65。

    知识共享许可协议
    本文彭旭 创作,采用 知识共享 署名-相同方式共享 3.0 中国大陆 许可协议进行许可。欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接。
  • 相关阅读:
    艾伟:[WCF中的Binding模型]之六(完结篇):从绑定元素认识系统预定义绑定 狼人:
    艾伟:.NET框架4.0中都有些什么? 狼人:
    艾伟:WM有约(三):下一次是什么时候? 狼人:
    艾伟:为什么微软要推 ADO.NET Data Services Framework 狼人:
    艾伟:WM有约(二):配置信息 狼人:
    艾伟:F4何去何从 大视野观察Framework 4.0 狼人:
    艾伟:[WCF的Binding模型]之三:信道监听器(Channel Listener) 狼人:
    艾伟:.NET : 如何保护内存中的敏感数据? 狼人:
    艾伟:Silverlight 2.0 之旋转木马 狼人:
    艾伟:.NET和J2EE该相互学习什么 狼人:
  • 原文地址:https://www.cnblogs.com/ainijiutian/p/2877092.html
Copyright © 2020-2023  润新知