Страницы

Поиск по вопросам

пятница, 27 декабря 2019 г.

Как отловить нажатие клавиш в WPF-приложении, когда оно свёрнуто?

#c_sharp #wpf #windows #clickonce #keyboardhook


Имеется WPF-приложение (аудио-проигрыватель). Необходимо добавить возможность отлавливать
нажатие определённых клавиш, даже когда приложение свёрнуто. Например, при нажатии
Num4 - останавливать вопроизведение, при нажатии других клавиш (в зависимости от настроек
- продолжать воспроизведение музыки и т.п.). 
Пробовал реализовать через функцию GetAsyncKeyState(int vkey) из user32.dll, но мне
это не совсем подходит, вот пример кода:

public static class KeyState
{
    public static void Wait(Key key, Action action)
    {
        var asyncOperation = AsyncOperationManager.CreateOperation(null);
        SendOrPostCallback callBack = state => action((KeyStates) state);
        ThreadPool.QueueUserWorkItem(state =>
        {
            var vk = KeyInterop.VirtualKeyFromKey(key);                
            var prev = ((GetAsyncKeyState(vk) & 0x8000) == 0x8000);
            if (prev)
            {
                asyncOperation.Post(callBack, KeyStates.Down);
            }
            while (true)
            {
                var res = ((GetAsyncKeyState(vk) & 0x8000) == 0x8000);
                if (res != prev && res)
                {
                    asyncOperation.Post(callBack, KeyStates.Down);
                }
                prev = res;
            }
        });
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto, ExactSpelling = true)]
    private static extern short GetAsyncKeyState(int vkey);
}


Проблема здесь в том, что мне не только нужно отловить нажатие клавиши, но и предотвратить
"продвижение" нажатия клавиши далее. Т.е. нажатие клавиши должно перехватываться моим
приложением и не попадать в другие (например, в текстовый редактор).
Буду благодарен за помощь. Если кто-нибудь ещё и подскажет как это корректно реализовать
для приложения, которое развертывается через ClickOnce, было бы вообще шикарно. Спасибо! 
    


Ответы

Ответ 1



Начиная с Windows Vista можно использовать функцию RegisterHotKey. Она позволяет зарегистрировать глобальную горячую клавишу и указать окно, которое будет принимать сообщения. Чтобы обработать эти сообщения в WPF приложении, нужно зарегистрировать обработчик через HwndSource.AddHook. Пример как обработать событие горячей клавиши и вывести момент ее нажатия в TextBlock: using System; using System.Collections.Generic; using System.Text; using System.Windows; using System.Windows.Controls; using System.Runtime.InteropServices; using System.Windows.Interop; namespace WpfHotkey { public partial class MainWindow : Window { [DllImport("user32.dll", SetLastError = true)] [return: MarshalAs(UnmanagedType.Bool)] public static extern bool RegisterHotKey(IntPtr hWnd, int id, uint fsModifiers, uint vk); //необходимые константы public const int MOD_ALT = 0x1; public const int MOD_CONTROL = 0x2; public const int MOD_SHIFT = 0x4; public const int MOD_WIN = 0x8; public const int WM_HOTKEY = 0x312; //несколько примеров виртуальных кодов public const uint VK_F1 = 0x70; public const uint VK_F2 = 0x71; public const uint VK_UP = 0x26; public const uint VK_DOWN = 0x28; //обработчик сообщений для окна private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled) { if (msg == WM_HOTKEY) { textBlock1.Text += DateTime.Now.ToString() + " WM_HOTKEY message, ID: 0x" + wParam.ToString("X"); textBlock1.Text += Environment.NewLine; handled = true; } return IntPtr.Zero; } public MainWindow() { InitializeComponent(); } private void Window_Loaded(object sender, RoutedEventArgs e) { WindowInteropHelper h = new WindowInteropHelper(this); HwndSource source = HwndSource.FromHwnd(h.Handle); source.AddHook(new HwndSourceHook(WndProc));//регистрируем обработчик сообщений bool res = RegisterHotKey(h.Handle, 1, 0, VK_F1);//регистрируем горячую клавишу if (res == false) MessageBox.Show("RegisterHotKey failed"); } } }

Комментариев нет:

Отправить комментарий