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 шт):
Благодаря ответу на английской версии сайта понял в чем проблема.
Мой код нажатия клавиши фактически не нажимает клавишу, а только отправляет сообщение о том, что клавиша была нажата. Обычные окна считывают только это сообщение, в то время как игры DirectX требуют фактического нажатия клавиши.
В ответе мне дали список кодов клавиш, которые будут работать для игр DirectX и уже после этого, добавив в библиотеку Python keyboard строку, выводящую коды клавиш, я заметил, что эти коды полностью совпадают с теми, что мне дали в ответе.
В общем, это заработало для Dota 2 и некоторых других играх, но не работало в CS:GO. Причина оказалась проста - нужно было поставить задержку в 20 мс между нажатием сочетания и отпусканием. То есть не "нажать и резко отпустить", а "нажать и подождать".