在WPF中使用键盘钩子

2010年2月5日 分类: C#, WinForm, 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:

  1. Winform/WPF实例中的相互操作
  2. WPF 仿QQ无边框窗体(自动隐藏/可缩放/最大化不覆盖任务栏)
  3. WPF无边框透明窗体的缩放
  4. WPF中的拖放(三)
  5. WPF最大化避免覆盖任务栏
标签: , , ,
目前还没有任何评论.

Leave a Comment