Имитация нажатия клавиш в неактивных окнах

Я хотел бы имитировать «настоящие» нажатия клавиш в неактивном окне. После некоторых исследований я нашел Windows API. Так как я привык к Java, я быстро нашел JNA, которая реализует winapi. Я написал некоторый код, который мог имитировать нажатия клавиш в активном окне с помощью метода sendInput(). Окно не обнаружило виртуальные коды клавиш. После некоторого поиска окна с прямым вводом, очевидно, нуждаются в скан-кодах. И это работало со следующим кодом:

import com.sun.jna.platform.win32.BaseTSD;
import com.sun.jna.platform.win32.User32;
import com.sun.jna.platform.win32.WinDef;
import com.sun.jna.platform.win32.WinDef.HWND;
import com.sun.jna.platform.win32.WinUser;

int KEYEVENT_SCANCODE = 0x0008;
int KEYEVENT_UP = 0x0002;
WinUser.INPUT input = new WinUser.INPUT();

input.type = new WinDef.DWORD(WinUser.INPUT.INPUT_KEYBOARD);
input.input.setType("ki");
input.input.ki.wVk = new WinDef.WORD(0);// 0 cause scancodes are used
input.input.ki.time = new WinDef.DWORD(0);
input.input.ki.dwExtraInfo = new BaseTSD.ULONG_PTR(0);

input.input.ki.wScan = new WinDef.WORD(0x2C); // scancode for 'y'
input.input.ki.dwFlags = new WinDef.DWORD(KEYEVENT_SCANCODE); // keydown

User32.INSTANCE.SendInput(new WinDef.DWORD(1), (WinUser.INPUT[]) input.toArray(1), input.size());

input.input.ki.wScan = new WinDef.WORD(0x2C);
input.input.ki.dwFlags = new WinDef.DWORD(KEYEVENT_SCANCODE | KEYEVENT_UP); // keyup

User32.INSTANCE.SendInput(new WinDef.DWORD(1), (WinUser.INPUT[]) input.toArray(1), input.size());

Теперь я хотел, чтобы он был «гладким», чтобы окно могло быть в фоновом режиме или свернуто. Я нашел SendMessage() и методы PostMessage() и думал, что понял его концепции. Вы передаете обработчику окна сообщение (wm_keydown и wm_keyup кажутся подходящими для этой задачи) и и его конкретные параметры.

Я пробовал это, но это не сработало. (то же самое для postMessage).

User32.INSTANCE.SendMessage(handler, WinUser.WM_KEYDOWN, new   WinDef.WPARAM(0x5A), new WinDef.LPARAM(0x002C0001));
Thread.sleep(100); //tried with and without
User32.INSTANCE.SendMessage(handler, WinUser.WM_KEYUP, new WinDef.WPARAM(0x5A), new WinDef.LPARAM(0xC02C0001)); 

Затем я попробовал WM_CHAR, и это частично сработало. Это работало в чате окна, но не вызывало горячих клавиш. После некоторых исследований люди говорят, что вы должны использовать перехватчики прямого ввода (?), поскольку некоторые окна не распознают сообщения от winapi, но мое, по-видимому, распознало от sendInput и sendMessage с WM_CHAR.

Я передал неправильные параметры? Что значит, что wm_char распознан, а wm_keydown и wm_keyup нет?

Я уже нашел множество примеров на стейк-оверфлоу и в Интернете, но это не особо помогло. Спасибо, что прочитали и ответили.

@update Я использовал инструмент для обнаружения сообщений. Сообщения, созданные реальными нажатиями клавиш, и сообщения, созданные из кода, равны:

В обоих случаях есть WM_keydown, затем WM_char и WM_keyup. Каждый параметр каждого сообщения равен. В дополнение к этому, как я уже сказал, если чат открыт, там пишутся символы, но действия не будут выполняться, когда чат закрыт. Я проверил, какие сообщения отправляются методом sendInput(): они аналогичны обоим случаям выше, но действие выполняется. Я не могу понять это поведение.


person bob    schedule 22.01.2019    source источник
comment
Я пытаюсь имитировать нажатия клавиш в окне, которое не находится в фокусе клавиатуры. Окно использует прямой ввод. Он обнаруживает нажатия клавиш, отправленные с помощью sendInput, но sendInput влияет на активное окно. Насколько я понимаю, sendInput со сканкодами будет распознан из directinput.   -  person bob    schedule 22.01.2019
comment
Насколько я знаю, DirectInput не использует очередь сообщений для получения ввода с клавиатуры, он получает его прямо с аппаратного обеспечения. Так что отправка сообщений в окна все равно не сработает. Вы должны использовать SendInput() для имитации аппаратных событий клавиатуры, это единственный вариант для этого. Просто не существует официального, стандартного способа отправить ввод с клавиатуры в скрытое/неактивное окно.   -  person Remy Lebeau    schedule 22.01.2019
comment
Если я использую PostMessage с keydown+up или SendMessage с keydown+char+keyup, в чате неактивного окна будут написаны символы, но ТОЛЬКО там. Если чат не запущен, ничего не происходит. Это действительно сбивает с толку.   -  person bob    schedule 22.01.2019
comment
Вы читали комментарии Реми? Ты хочешь совета или нет?   -  person David Heffernan    schedule 23.01.2019
comment
Неактивные окна не имеют фокуса клавиатуры и поэтому не могут принимать ввод. Какой бы метод вы ни нашли сегодня, работая над тем, чего вы пытаетесь достичь (что неясно), он может сломаться завтра со следующим обновлением.   -  person Michael Chourdakis    schedule 23.01.2019
comment
@bob Если чат не запущен, ничего не происходит. Вы имеете в виду, что при использовании этого кода User32.INSTANCE.SendMessage(handler, WinUser.WM_KEYDOWN, new WinDef.WPARAM(0x5A), new WinDef.LPARAM(0x002C0001)); User32.INSTANCE.SendMessage(handler, WinUser.WM_KEYUP, new WinDef.WPARAM(0x5A), new WinDef.LPARAM(0xC02C0001)); вы не можете получить ни WM_KEYDOWN, ни WM_KEYUP, за исключением того, что вы дополнительно отправляете WM_CHAR?   -  person Rita Han    schedule 23.01.2019
comment
@RitaHan-MSFT Да, это правильно. Если чат триггерный, нажатия клавиш распознаются как отправленные с помощью SendMessage wm_keydown, затем wm_char, затем wm_keyup или с PostMessage wm_keydown, затем wm_keyup, поскольку PostMessage переводит wm_keydown в wm_char. Если чат не запущен, нажатия клавиш не распознаются, потому что ничего не происходит, но происходит, если я сам нажимаю клавишу. Это действительно сбивает меня с толку.   -  person bob    schedule 23.01.2019
comment
@DavidHeffernan я прочитал каждый комментарий   -  person bob    schedule 23.01.2019
comment
@bob Но я не могу воспроизвести эту проблему с двумя консольными приложениями win32 на моем рабочем столе Windows 10. Один отправляет нажатие клавиши, а другой может получать как WM_KEYDOWN, так и WM_KEYUP без WM_CHAR.   -  person Rita Han    schedule 23.01.2019