C#: Почему программное нажатие сочетания клавиш работает не во всех окнах?

Имеется код, который при нажатии определенной клавиши/сочетания клавиш прогарммно нажимает сочетание клавиш CTRL+A (Выделить текст в строке).


Проблема:

Все отлично работает в блокноте, браузере, в командной строке и других окнах, но не работает с текстовыми полями в играх (например, Dota 2, CS:GO).

То есть, когда я нажимаю G (кнопка для вызова CTRL+A) вообще ничего не происходит. При этом функция нажатия CTRL+A вызывается, то есть клавиша G не перехватывается игрой.


Решение на Python

Так или иначе, например этот код на Python отлично работает и в браузере и в игре, при этом даже не перехватывая клавишу из других программ:

import keyboard
keyboard.add_hotkey('G', callback=lambda: keyboard.press_and_release('ctrl+a'))
keyboard.wait()

Насколько я знаю, он тоже ссылается к user32.dll, но почему он работает, а на C# нет - мне не понятно.

Вот исходник функции keyboard.press_and_release() (477 строка), которая ссылается на функцию send().


Код:

using System;
using System.Linq;
using System.Threading.Tasks;
using System.Windows;
using System.Windows.Input;

namespace Test
{
    public partial class MainWindow : Window
    {
        public MainWindow()
        {
            InitializeComponent();
            // при нажатии на кнопку G программа нажмет CTRL+A 
            NHotkey.Wpf.HotkeyManager.Current.AddOrReplace("G", Key.G, ModifierKeys.None, (sender, e) => { PressCtrlA(sender, e); });
        }

        // метод для ожидания освобождения всех клавиш клавиатуры
        // чтобы программное нажатие CTRL+A сработало корректно
        private static readonly Key[] keys = Enum.GetValues(typeof(Key)).Cast<Key>().Where(key => key != Key.None).ToArray();
        private async Task WaitForKeysReleaseAsync()
        {
            int threshold = 0;
            while (Keyboard.Modifiers != ModifierKeys.None || keys.Any(key => Keyboard.IsKeyDown(key)))
            {
                if (threshold++ == 100) // ~5 секунд
                    throw new TimeoutException("Keyboard release timed out or stuck keys");
                await Task.Delay(50);
            }
        }

        // подождать пока освободится клавиатура и нажать CTRL+A
        private async void PressCtrlA(object sender, EventArgs e)
        {
            await WaitForKeysReleaseAsync();
            KeyboardManager.PressHotkey(Key.A, ModifierKeys.Control);
        }
    }
}

В коде используется класс KeyboardManager для программного нажатия клавиш, вот код от него: https://pastebin.com/xRtv3drq

Также используется пакет NHotkey.Wpf


Ответы (1 шт):

Автор решения: denisnumb

Благодаря ответу на английской версии сайта понял в чем проблема.

Мой код нажатия клавиши фактически не нажимает клавишу, а только отправляет сообщение о том, что клавиша была нажата. Обычные окна считывают только это сообщение, в то время как игры DirectX требуют фактического нажатия клавиши.

В ответе мне дали список кодов клавиш, которые будут работать для игр DirectX и уже после этого, добавив в библиотеку Python keyboard строку, выводящую коды клавиш, я заметил, что эти коды полностью совпадают с теми, что мне дали в ответе.

В общем, это заработало для Dota 2 и некоторых других играх, но не работало в CS:GO. Причина оказалась проста - нужно было поставить задержку в 20 мс между нажатием сочетания и отпусканием. То есть не "нажать и резко отпустить", а "нажать и подождать".

→ Ссылка