Как отправить комбинации клавиш Ctrl/Shift/Alt + Key в окно приложения? (через SendMessage)
Я могу успешно отправить какое-либо одно ключевое сообщение в приложение, но я не знаю, как отправлять комбинации ключей (например, Ctrl + F12, Shift + F1, Ctrl + R и т.д.)
Пробовал это так:
SendMessage(handle, WM_KEYDOWN, Keys.Control, 0);
SendMessage(handle, WM_KEYDOWN, Keys.F12, 0);
SendMessage(handle, WM_KEYUP, Keys.F12, 0);
SendMessage(handle, WM_KEYUP, Keys.Control, 0);
но это не работает (приложение действует, как только нажатие F12, а не Ctrl + F12).
Любые идеи, как сделать эту работу?
Ответы
Ответ 1
Вероятно, вы обнаружите, что использование SendInput (документация здесь) работает намного лучше. Вам понадобится P/Invoke из примера С#, здесь. Вы можете предоставить массивы данных с клавишами вниз и вверх и правильно установить другие параметры сообщения, например, были ли нажаты левая или правая Ctrl/Shift/Alt.
Вы также можете использовать SendKeys
класс (здесь). Это позволяет вам указывать ключи по имени, например, {^F12}
для Ctrl + F12.
Изменить. OP теперь говорит, что ему нужно отправлять входные данные в свернутые приложения, не активируя их. Это невозможно сделать надежно каким-либо образом, в том числе даже со специализированным оборудованием. Я работал в области автоматизации. Это просто невозможно. OP должен использовать FindWindow
/SetForegroundWindow
для включения целевого приложения, а затем он может переключиться обратно на свое приложение.
Ответ 2
Я уже пробовал метод с GetKeyboardState и SetKeyboardState (которому предшествовало присоединение потока окна и завершилось отсоединением от потока окна). Я не работаю для комбинаций клавиш, таких как Ctrl + Something или комбинации, используя Alt или Shift. Клавиши управления, Alt и Shift не отображаются как нажатые. Кажется, что максимум, который вы можете получить, когда минимизируется окно, - это нажатие отдельных клавиш с помощью PostMessage с сообщениями WM_KEYDOWN. Еще одна вещь, которую я заметил, заключается в том, что если вы отправляете WM_KEYDOWN и WM_KEYUP (для одного и того же ключа), клавиша будет нажата дважды. Поэтому используйте только WM_KEYDOWN один раз. Это не 100% точный метод, но когда окно минимизировано, есть некоторые ограничения.
Такая же ситуация возникает, когда экран заблокирован.
Ответ 3
OP теперь говорит, что ему нужно отправить вход для минимизированных приложений не активируя их. Это невозможно сделать надежно в любом в том числе даже со специализированным оборудованием. Я работал в автоматизации. Это просто невозможно.
Отправка ввода в миниатюрные приложения на самом деле возможна, не уверен в отношении ввода мыши, но ввод с клавиатуры работает очень хорошо.
Принимая идею в предыдущем ответе здесь, что я смог выполнить (код находится в Delphi, но это довольно просто, поэтому вы можете перевести его на требуемый язык):
procedure SendKeys(const Win : HWND; const Key,sKey: Cardinal);
var
thrID : Cardinal;
KB : TKeyBoardState;
begin
if sKey <> 0 then
begin
thrID := GetWindowThreadProcessId(win,nil);
GetKeyboardState(KB);
AttachThreadInput(GetCurrentThreadID, thrID, True);
KB[sKey] := KB[sKey] or $80;
SetKeyboardState(KB);
end;
SendMessage(Win,WM_KEYDOWN,Key,0);
SendMessage(Win,WM_KEYUP,Key,0);
if sKey <> 0 then
begin
KB[sKey] := 0;
SetKeyBoardState(KB);
AttachThreadInput(GetCurrentThreadId, thrID, False);
end;
end;
[Win] должен быть элементом управления для получения ввода, а не его родительской формы и т.д.
[Ключ] - клавиша для нажатия;
[sKey] - это альтернативный ключ, который нужно нажать, нажав клавишу [Key], например CTRL/SHIFT (ALT передается через само сообщение, подробнее см. ссылку MSDN WM_KEYDOWN).
Отправка единственного нажатия клавиши довольно проста, вы просто делаете sendmessage и делаете это, но если вам нужно что-то вроде CTRL + SPACE здесь, где это усложняется. Каждый поток имеет свой собственный KeyboardState, изменение KeyboardState в вашем собственном приложении не повлияет на другое, если вы не присоедините свои потоковые входы функцией AttachThreadInput. Когда приложение обрабатывает сообщение WM_KEYDOWN, оно также проверяет текущие состояния сдвига (CTRL/SHIFT), вызывая функцию GetKeyboardState (ключ ALT может быть отправлен через дополнительный параметр сообщения WM_KEYDOWN) и что при подключении вложенного потока вход в игру.
Ответ 4
Возможно, вы смотрите что-то вроде этого:
procedure GoCursorUp(Obj: TControl);
var KeyState : TKeyboardState;
begin
GetKeyboardState(KeyState);
KeyState[VK_CONTROL] := KeyState[VK_CONTROL] or $80;
SetKeyboardState(KeyState);// control down
Obj.Perform(WM_KEYDOWN,VK_HOME,0); //ex. with HOME key
KeyState[VK_CONTROL] := $0;
SetKeyboardState(KeyState);// control up
end;
...
GoCursorUp (Self);
Или что-то вроде этого:
//for example: SHIFT + TAB
keybd_event(VK_SHIFT, 0, 0, 0);
keybd_event(VK_TAB, 0, 0, 0);
keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);
Ответ 5
Если вы хотите имитировать нажатия клавиш для свернутых окон, вы можете сделать что-то вроде этого:
uint windowThreadId = GetWindowThreadProcessId(hwnd, IntPtr.Zero);
uint myThreadId = GetCurrentThreadId();
AttachThreadInput(myThreadId, windowThreadId, true);
Следующим шагом будет использование GetKeyboardState для извлечения массива всех состояний клавиш в потоке окна.
Измените состояние клавиш SHIFT или CTRL или ALT, чтобы их нажали, используя их коды виртуальных клавиш. Затем вызовите SetKeyboardState для применения этих состояний. Нажмите клавишу F12:
SendMessage(hwnd, WM_KEYDOWN, Keys.F12, 0);
SendMessage(hwnd, WM_KEYUP, Keys.F12, 0);
Измените состояния клавиш SHIFT, CTRL или ALT как освобожденные. Вызовите SetKeyboardState еще раз. Наконец, отсоедините от окна:
AttachThreadInput(myThreadId, windowThreadId, false);