惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

D
Docker
阮一峰的网络日志
阮一峰的网络日志
T
Tailwind CSS Blog
博客园 - 【当耐特】
量子位
博客园 - 叶小钗
有赞技术团队
有赞技术团队
Jina AI
Jina AI
钛媒体:引领未来商业与生活新知
钛媒体:引领未来商业与生活新知
博客园 - Franky
博客园 - 司徒正美
爱范儿
爱范儿
美团技术团队
小众软件
小众软件
酷 壳 – CoolShell
酷 壳 – CoolShell
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
V
V2EX
罗磊的独立博客
freeCodeCamp Programming Tutorials: Python, JavaScript, Git & More
Last Week in AI
Last Week in AI
Hugging Face - Blog
Hugging Face - Blog
I
InfoQ
D
DataBreaches.Net
宝玉的分享
宝玉的分享

博客园 - 彭旭

VNC远程Gnome图形桌面(openEuler 20.03 LTS系统) [WPF]控件应用多个样式 [ASP.NET MVC] Real-time之HTML5 服务器发送事件(server-sent event) [WCF]DomainServices客户端操作异常处理 [Silverlight]监听指定控件(FrameworkElement)的依赖属性(DependencyProperty)的更改 [Chrome扩展]屏蔽Google搜索结果重定向 Linq Distinct扩展 [Winform/C#]点击工作区域移动窗体 [WinForm] C#避免Panel(ScrollableControl)的子控件获取焦点时滚动条自动滚动 C#远程连接Oracle数据库(不安装客户端) RichTextBox.MouseWheel事件控制父控件Panel的内容滚动 ID3v1信息结构(MP3文件)的读取、修改(C#) 在线编辑Office文件 SQL优化 将自然数分解为若干连续的自然数 小写金额转换为大写金额 大数阶乘 查看文件夹大小及占用空间 VS2005开发WAP网站初识 - 彭旭 - 博客园
[Silverlight]键盘钩子KeyboardHook
彭旭 · 2013-01-25 · via 博客园 - 彭旭

前言

一切的起因就是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。