Как отправить комбинации клавиш 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);