[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。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM