在WPF中使用键盘钩子
在WPF开发中,WPF给了我们很好的关于键盘的支持,不过,我在开发测试中发现,使用WPF内置的方式判断组合键会造成反应低下,如Ctrl+A组合键,如果用wpf自己的组合键判断方式,如:
if(e.KeyStates == Keyboard.GetKeyStates(Key.A) && Keyboard.Modifiers == ModifierKeys.Alt)
如果,我们的客户在很快的情况下,按下组合键,会造成无效,为了解决这个问题使用Win32 API做个键盘钩子,在google中搜索,偶然发现,老外,给我们写了现成的关于Win32键盘监听的类。我们只要使用这个类就能判断用户的键盘事件。从而达到快速反应键盘事件的方式,次代码对WinForm同样有效。代码如下:
/*
* Class that demonstrates how to use a low level system hook to detect
* when any key is pressed. Converts the low level keyboard data into
* .NET friendly info and raises two events: KeyUp and KeyDown.
*
* Sample usage:
*
* private KeyboardHook _hook;
*
* _hook = new KeyboardHook();
* _hook.KeyDown += new KeyboardHook.HookEventHandler(OnHookKeyDown);
*
* void OnHookKeyDown(object sender, HookEventArgs e)
* {
* // process the key that was pressed
* }
*
* Make sure do disable the Visual Studio 'Enable the Visual Studio
* hosting process' option in the project debug options.
*/
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Forms;
using System.Runtime.InteropServices;
using System.Reflection;
using System.Diagnostics;
namespace Common
{
// argument sent in event handler
public class HookEventArgs : EventArgs
{
// using Windows.Forms.Keys instead of Input.Key since the Forms.Keys maps
// to the Win32 KBDLLHOOKSTRUCT virtual key member, where Input.Key does not
public Keys Key;
public bool Alt;
public bool Control;
public bool Shift;
public HookEventArgs(UInt32 keyCode)
{
// detect what modifier keys are pressed, using
// Windows.Forms.Control.ModifierKeys instead of Keyboard.Modifiers
// since Keyboard.Modifiers does not correctly get the state of the
// modifier keys when the application does not have focus
this.Key = (Keys)keyCode;
this.Alt = (System.Windows.Forms.Control.ModifierKeys & Keys.Alt) != 0;
this.Control = (System.Windows.Forms.Control.ModifierKeys & Keys.Control) != 0;
this.Shift = (System.Windows.Forms.Control.ModifierKeys & Keys.Shift) != 0;
}
}
// hooks system keyboard
public class KeyboardHook
{
#region pinvoke details
private enum HookType : int
{
WH_JOURNALRECORD = 0,
WH_JOURNALPLAYBACK = 1,
WH_KEYBOARD = 2,
WH_GETMESSAGE = 3,
WH_CALLWNDPROC = 4,
WH_CBT = 5,
WH_SYSMSGFILTER = 6,
WH_MOUSE = 7,
WH_HARDWARE = 8,
WH_DEBUG = 9,
WH_SHELL = 10,
WH_FOREGROUNDIDLE = 11,
WH_CALLWNDPROCRET = 12,
WH_KEYBOARD_LL = 13,
WH_MOUSE_LL = 14
}
public struct KBDLLHOOKSTRUCT
{
public UInt32 vkCode;
public UInt32 scanCode;
public UInt32 flags;
public UInt32 time;
public IntPtr extraInfo;
}
[DllImport("user32.dll")]
private static extern IntPtr SetWindowsHookEx(
HookType code, HookProc func, IntPtr instance, int threadID);
[DllImport("user32.dll")]
private static extern int UnhookWindowsHookEx(IntPtr hook);
[DllImport("user32.dll")]
private static extern int CallNextHookEx(
IntPtr hook, int code, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);
#endregion
HookType _hookType = HookType.WH_KEYBOARD_LL;
IntPtr _hookHandle = IntPtr.Zero;
HookProc _hookFunction = null;
// hook method called by system
private delegate int HookProc(int code, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam);
// events
public delegate void HookEventHandler(object sender, HookEventArgs e);
public event HookEventHandler KeyDown;
public event HookEventHandler KeyUp;
public KeyboardHook()
{
_hookFunction = new HookProc(HookCallback);
Install();
}
~KeyboardHook()
{
Uninstall();
}
// hook function called by system
private int HookCallback(int code, IntPtr wParam, ref KBDLLHOOKSTRUCT lParam)
{
if (code < 0)
return CallNextHookEx(_hookHandle, code, wParam, ref lParam);
// KeyUp event
if ((lParam.flags & 0x80) != 0 && this.KeyUp != null)
this.KeyUp(this, new HookEventArgs(lParam.vkCode));
// KeyDown event
if ((lParam.flags & 0x80) == 0 && this.KeyDown != null)
this.KeyDown(this, new HookEventArgs(lParam.vkCode));
return CallNextHookEx(_hookHandle, code, wParam, ref lParam);
}
private void Install()
{
// make sure not already installed
if (_hookHandle != IntPtr.Zero)
return;
// need instance handle to module to create a system-wide hook
Module[] list = System.Reflection.Assembly.GetExecutingAssembly().GetModules();
System.Diagnostics.Debug.Assert(list != null && list.Length > 0);
// install system-wide hook
_hookHandle = SetWindowsHookEx(_hookType,
_hookFunction, Marshal.GetHINSTANCE(list[0]), 0);
}
private void Uninstall()
{
if (_hookHandle != IntPtr.Zero)
{
// uninstall system-wide hook
UnhookWindowsHookEx(_hookHandle);
_hookHandle = IntPtr.Zero;
}
}
}
}
使用时,我们只要new KeyboardHook()对象,并且实现它的KeyDown事件就可以了。判断的时候要使用WinForm的类库。
原创文章,转载请注明: 转载自.NET开发者
本文链接地址: 在WPF中使用键盘钩子
Related posts: