在C#应用程序中接收操作系统级别的按键事件

Posted

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了在C#应用程序中接收操作系统级别的按键事件相关的知识,希望对你有一定的参考价值。

我不知道这个问题有更好的标题,但我会说明我的问题。

我正在开发类似MP3播放器的应用程序,它使用多媒体键来播放/暂停,停止歌曲,我实际上使它工作但是FormApplication必须在顶部[聚焦]

protected override void WndProc(ref Message msg)
{
    if (msg.Msg == 0x319)   // WM_APPCOMMAND message
    {
        // extract cmd from LPARAM (as GET_APPCOMMAND_LPARAM macro does)
        int cmd = (int)((uint)msg.LParam >> 16 & ~0xf000);
        switch (cmd)
        {
            case 13:  // APPCOMMAND_MEDIA_STOP constant
                MessageBox.Show("Stop");
                break;
            case 14:  // APPCOMMAND_MEDIA_PLAY_PAUSE
                MessageBox.Show("Play/Pause");
                break;
            case 11:  // APPCOMMAND_MEDIA_NEXTTRACK
                MessageBox.Show("Next");
                break;
            case 12:  // APPCOMMAND_MEDIA_PREVIOUSTRACK
                MessageBox.Show("Previous");
                break;
            default:  
                break;
        }
    }
    base.WndProc(ref msg);
}


private void Form1_KeyPress(object sender, KeyPressEventArgs e)
{
    Message m = new Message();
    m.Msg = e.KeyChar;

    WndProc(ref m);
}

所以我需要做的是:即使程序在后台并且不在顶部或聚焦,使多媒体键工作,就像winamp和Windows Media Player的工作方式一样。

我在互联网上搜索了很多,但大多数给了我类似于我正在使用的方式。

非常感谢您的帮助。

答案

您需要一个OS级别的键盘钩子。例如user32.dll SetWindowsHookEx通过P/Invoke。您可以在下面找到一个示例,但您必须根据自己的需要进行定制。

注意:这里的Dispatcher是WPF调度程序,因此可以引发RawKeyEventArgs

KeyboardListener.cs

/// <summary>
    /// Listens keyboard globally.
    /// 
    /// <remarks>Uses WH_KEYBOARD_LL.</remarks>
    /// </summary>
    public class KeyboardListener : IDisposable
    {
        /// <summary>
        /// Creates global keyboard listener.
        /// </summary>
        public KeyboardListener(Dispatcher dispatcher)
        {
            // Dispatcher thread handling the KeyDown/KeyUp events.
            _dispatcher = dispatcher;

            // We have to store the LowLevelKeyboardProc, so that it is not garbage collected runtime
            _hookedLowLevelKeyboardProc = LowLevelKeyboardProc;

            // Set the hook
            _hookId = InterceptKeys.SetHook(_hookedLowLevelKeyboardProc);

            // Assign the asynchronous callback event
            _hookedKeyboardCallbackAsync = new KeyboardCallbackAsync(KeyboardListener_KeyboardCallbackAsync);
        }

        private readonly Dispatcher _dispatcher;

        /// <summary>
        /// Destroys global keyboard listener.
        /// </summary>
        ~KeyboardListener()
        {
            Dispose();
        }

        /// <summary>
        /// Fired when any of the keys is pressed down.
        /// </summary>
        public event EventHandler<RawKeyEventArgs> KeyDown;

        /// <summary>
        /// Fired when any of the keys is released.
        /// </summary>
        public event EventHandler<RawKeyEventArgs> KeyUp;

        #region Inner workings

        /// <summary>
        /// Hook ID
        /// </summary>
        private readonly IntPtr _hookId = IntPtr.Zero;

        /// <summary>
        /// Asynchronous callback hook.
        /// </summary>
        /// <param name="character">Character</param>
        /// <param name="keyEvent">Keyboard event</param>
        /// <param name="vkCode">VKCode</param>
        private delegate void KeyboardCallbackAsync(InterceptKeys.KeyEvent keyEvent, int vkCode, string character);

        /// <summary>
        /// Actual callback hook.
        /// 
        /// <remarks>Calls asynchronously the asyncCallback.</remarks>
        /// </summary>
        /// <param name="nCode"></param>
        /// <param name="wParam"></param>
        /// <param name="lParam"></param>
        /// <returns></returns>
        [MethodImpl(MethodImplOptions.NoInlining)]
        private IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam)
        {
            if (nCode >= 0)
                if (wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_KEYDOWN ||
                    wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_KEYUP ||
                    wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_SYSKEYDOWN ||
                    wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_SYSKEYUP)
                {
                    // Captures the character(s) pressed only on WM_KEYDOWN
                    string chars = InterceptKeys.VKCodeToString((uint)Marshal.ReadInt32(lParam),
                                                                (wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_KEYDOWN ||
                                                                 wParam.ToUInt32() == (int)InterceptKeys.KeyEvent.WM_SYSKEYDOWN));

                    _hookedKeyboardCallbackAsync.BeginInvoke((InterceptKeys.KeyEvent)wParam.ToUInt32(), Marshal.ReadInt32(lParam), chars, null, null);
                }

            return InterceptKeys.CallNextHookEx(_hookId, nCode, wParam, lParam);
        }

        /// <summary>
        /// Event to be invoked asynchronously (BeginInvoke) each time key is pressed.
        /// </summary>
        private readonly KeyboardCallbackAsync _hookedKeyboardCallbackAsync;

        /// <summary>
        /// Contains the hooked callback in runtime.
        /// </summary>
        private readonly InterceptKeys.LowLevelKeyboardProc _hookedLowLevelKeyboardProc;

        /// <summary>
        /// HookCallbackAsync procedure that calls accordingly the KeyDown or KeyUp events.
        /// </summary>
        /// <param name="keyEvent">Keyboard event</param>
        /// <param name="vkCode">VKCode</param>
        /// <param name="character">Character as string.</param>
        void KeyboardListener_KeyboardCallbackAsync(InterceptKeys.KeyEvent keyEvent, int vkCode, string character)
        {
            switch (keyEvent)
            {
                // KeyDown events
                case InterceptKeys.KeyEvent.WM_KEYDOWN:
                    if (KeyDown != null)
                        _dispatcher.BeginInvoke(new EventHandler<RawKeyEventArgs>(KeyDown), this, new RawKeyEventArgs(vkCode, false, character));
                    break;
                case InterceptKeys.KeyEvent.WM_SYSKEYDOWN:
                    if (KeyDown != null)
                        _dispatcher.BeginInvoke(new EventHandler<RawKeyEventArgs>(KeyDown), this, new RawKeyEventArgs(vkCode, true, character));
                    break;

                // KeyUp events
                case InterceptKeys.KeyEvent.WM_KEYUP:
                    if (KeyUp != null)
                        _dispatcher.BeginInvoke(new EventHandler<RawKeyEventArgs>(KeyUp), this, new RawKeyEventArgs(vkCode, false, character));
                    break;
                case InterceptKeys.KeyEvent.WM_SYSKEYUP:
                    if (KeyUp != null)
                        _dispatcher.BeginInvoke(new EventHandler<RawKeyEventArgs>(KeyUp), this, new RawKeyEventArgs(vkCode, true, character));
                    break;

                default:
                    break;
            }
        }

        #endregion

        #region IDisposable Members

        /// <summary>
        /// Disposes the hook.
        /// <remarks>This call is required as it calls the UnhookWindowsHookEx.</remarks>
        /// </summary>
        public void Dispose()
        {
            InterceptKeys.UnhookWindowsHookEx(_hookId);
        }

        #endregion
    }

InterceptKeys.cs

    /// <summary>
    /// Winapi Key interception helper class.
    /// </summary>
    internal static class InterceptKeys
    {
        public delegate IntPtr LowLevelKeyboardProc(int nCode, UIntPtr wParam, IntPtr lParam);

        private const int WH_KEYBOARD_LL = 13;

        /// <summary>
        /// Key event
        /// </summary>
        public enum KeyEvent : int
        {
            /// <summary>
            /// Key down
            /// </summary>
            WM_KEYDOWN = 256,

            /// <summary>
            /// Key up
            /// </summary>
            WM_KEYUP = 257,

            /// <summary>
            /// System key up
            /// </summary>
            WM_SYSKEYUP = 261,

            /// <summary>
            /// System key down
            /// </summary>
            WM_SYSKEYDOWN = 260
        }

        public static IntPtr SetHook(LowLevelKeyboardProc proc)
        {
            using (Process curProcess = Process.GetCurrentProcess())
            using (ProcessModule curModule = curProcess.MainModule)
            {
                return SetWindowsHookEx(WH_KEYBOARD_LL, proc, GetModuleHandle(curModule.ModuleName), 0);
            }
        }

        [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
        private static extern IntPtr SetWindowsHookEx(int idH

以上是关于在C#应用程序中接收操作系统级别的按键事件的主要内容,如果未能解决你的问题,请参考以下文章

如何检测 LWUIT 表单中的按键事件?

Android接收蓝牙多媒体按键事件的bug

10.3 键盘事件

Labview中怎样实现用多个按键分别控制执行多个不同的操作?

android源码解析(二十七)-->HOME事件流程

python如何设置按键松开