WPF Как отследить глобальные нажатия клавиатуры + сочетания?

В WPF есть несколько способов отследить глобальные нажатия клавиатуры и сочетания клавиш. Один из наиболее распространенных способов - использование класса KeyboardHook и интероперабельности с Win32 API.

Ниже приведены шаги, которые вам потребуется выполнить, чтобы отследить глобальные нажатия клавиш в WPF:

1. Создайте новый класс KeyboardHook:

using System;
using System.Runtime.InteropServices;
using System.Windows.Input;

public class KeyboardHook
{
    private const int WH_KEYBOARD_LL = 13;
    private const int WM_KEYDOWN = 0x0100;

    private static LowLevelKeyboardProc _proc = HookCallback;
    private static IntPtr _hookID = IntPtr.Zero;

    public static event EventHandler<KeyEventArgs> KeyDown;

    public static void Hook()
    {
        _hookID = SetHook(_proc);
    }

    public static void Unhook()
    {
        UnhookWindowsHookEx(_hookID);
    }

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

    private delegate IntPtr LowLevelKeyboardProc(int nCode, IntPtr wParam, IntPtr lParam);

    private static IntPtr HookCallback(int nCode, IntPtr wParam, IntPtr lParam)
    {
        if (nCode >= 0 && wParam == (IntPtr)WM_KEYDOWN)
        {
            int vkCode = Marshal.ReadInt32(lParam);
            Key key = KeyInterop.KeyFromVirtualKey(vkCode);

            KeyEventArgs args = new KeyEventArgs(Keyboard.PrimaryDevice, PresentationSource.FromVisual(App.Current.MainWindow), 0, key);
            KeyDown?.Invoke(null, args);
        }

        return CallNextHookEx(_hookID, nCode, wParam, lParam);
    }

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr SetWindowsHookEx(int idHook, LowLevelKeyboardProc lpfn, IntPtr hMod, uint dwThreadId);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool UnhookWindowsHookEx(IntPtr hhk);

    [DllImport("user32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr CallNextHookEx(IntPtr hhk, int nCode, IntPtr wParam, IntPtr lParam);

    [DllImport("kernel32.dll", CharSet = CharSet.Auto, SetLastError = true)]
    private static extern IntPtr GetModuleHandle(string lpModuleName);
}

2. В вашем главном окне WPF (класс MainWindow по умолчанию) добавьте код для подключения и отключения KeyboardHook в событиях Loaded и Closed:

public partial class MainWindow : Window
{
    public MainWindow()
    {
        InitializeComponent();
        this.Loaded += MainWindow_Loaded;
        this.Closed += MainWindow_Closed;
    }

    private void MainWindow_Loaded(object sender, RoutedEventArgs e)
    {
        KeyboardHook.Hook();
        KeyboardHook.KeyDown += KeyboardHook_KeyDown;
    }

    private void MainWindow_Closed(object sender, EventArgs e)
    {
        KeyboardHook.Unhook();
    }

    private void KeyboardHook_KeyDown(object sender, KeyEventArgs e)
    {
        // Ваши действия при нажатии клавиши
        MessageBox.Show("Нажата клавиша: " + e.Key.ToString());
    }
}

Теперь каждый раз, когда пользователь нажимает любую клавишу, будет вызываться обработчик KeyboardHook_KeyDown и вам будет доступен объект KeyEventArgs, содержащий информацию о нажатой клавише.

Обратите внимание, что использование глобального перехвата клавиатуры может повлечь некоторые проблемы с безопасностью и должно быть использовано с осторожностью.