Сбой отправки после обработки горячей клавиши, содержащей «Ctrl»

#delphi #hotkeys #sendinput #registerhotkey

#delphi #горячие клавиши #отправка ввода #registerhotkey

Вопрос:

Я пишу исполняемый файл Windows в Dephi 2007 (32-разрядная версия), который отправляет нажатия клавиш в другое приложение.

Нажатие горячей клавиши или комбинации горячих клавиш из другого приложения приводит к тому, что мое приложение отправляет нажатия клавиш этому приложению. Мой подход работает нормально, если комбинация горячих клавиш не содержит Ctrl: т.е. F11 или Shift F11 работает должным образом, но Ctrl F11 приводит к сбою моего первого вызова SendInput после обработки горячей клавиши в моем приложении.

Я пробовал свое приложение с несколькими различными целевыми приложениями и как в 64-разрядной Windows 7, так и в 32-разрядной Windows XP. Такая же схема сбоя возникает во всех случаях, которые я пробовал.

При обработке комбинации горячих клавиш приведенный ниже код пытается отправить две клавиши Shift Tab, за которыми следует клавиша Tab, в целевое приложение. Однако, когда Ctrl является частью комбинации горячих клавиш, первая Shift клавиша Tab никогда не отображается в целевом приложении. (Последующие клавиши отображаются просто отлично.)

Чего мне не хватает?

РЕДАКТИРОВАТЬ: я изменил приведенный ниже код (согласно моему комментарию Sertac) в разделе «РЕШЕНИЕ» … теперь он работает нормально, даже с Ctrl как частью последовательности горячих клавиш.

 unit mainTEST;

interface

uses
  Windows, Messages, SysUtils, Classes, Graphics, Controls, Forms, Dialogs, Menus;

type
  T_Mainform = class(TForm)
  private
    procedure WMHotkey(var msg: TWMHotkey); message WM_HOTKEY;
    procedure DoRegisterHotkey;
    procedure SendTabKey(bShifted: boolean);
  public
    procedure AfterConstruction; override;
  end;

var
  _Mainform: T_Mainform;

implementation

{$R *.dfm}

const
  OurHotkeyID = 10;

procedure T_Mainform.AfterConstruction;
begin
  inherited;
  DoRegisterHotkey;
end;


procedure T_Mainform.DoRegisterHotkey;
const
  sHOTKEY = 'Shift Ctrl F11';         //Fails!
  //sHOTKEY = 'Shift F11';              //Succeeds
//  sHOTKEY = 'F11';                    //Succeeds
  //sHOTKEY = 'Shift Ctrl F9';          //Fails!
  //sHOTKEY = 'Shift F9';               //Succeeds
var
  AHotkey : TShortCut;
  hkModifiers: UINT;
  hkText: string;
  hkKey: Word;
  hkShiftState: TShiftState;
begin
  AHotkey := TextToShortcut(sHOTKEY);
  hkModifiers := 0;
  hkText := sHOTKEY;
  if Pos('Shift', hkText) > 0 then
    hkModifiers := hkModifiers or MOD_SHIFT;
  if Pos('Ctrl', hkText) > 0 then
    hkModifiers := hkModifiers or MOD_CONTROL;
  if Pos('Alt', hkText) > 0 then
    hkModifiers := hkModifiers or MOD_ALT;
  ShortCutToKey(AHotkey, hkKey, hkShiftState);

  if not RegisterHotkey(Handle, OurHotkeyID, hkModifiers, hkKey) then
    ShowMessageFmt( 'Unable to register hotkey %s.'#13#13 
        'LastError: %d', [GetLastError]);
end;


procedure T_Mainform.SendTabKey(bShifted: boolean);
var
  KeyInputs: array of TInput;
  KeyInputCount: Integer;
  //------------------------------
  procedure KeybdInput(VKey: Byte; Flags: DWORD);
  begin
    Inc(KeyInputCount);
    SetLength(KeyInputs, KeyInputCount);
    KeyInputs[KeyInputCount - 1].Itype := INPUT_KEYBOARD;
    with  KeyInputs[KeyInputCount - 1].ki do
    begin
      wVk := VKey;
      wScan := MapVirtualKey(wVk, 0);
      dwFlags := KEYEVENTF_EXTENDEDKEY;
      dwFlags := Flags or dwFlags;
      time := 0;
      dwExtraInfo := 0;
    end;
  end;
  //------------------------------
begin
  KeyInputCount := 0;

  if bShifted then
    KeybdInput(VK_SHIFT, 0);
  KeybdInput(VK_TAB, 0);
  KeybdInput(VK_TAB, KEYEVENTF_KEYUP);
  if bShifted then
    KeybdInput(VK_SHIFT, KEYEVENTF_KEYUP);
  SendInput(KeyInputCount, KeyInputs[0], SizeOf(KeyInputs[0]));
  Sleep(50);
end;


procedure T_Mainform.WMHotkey(var msg: TWMHotkey);
begin
  case msg.Hotkey of
    OurHotkeyID:
      if (Screen.ActiveForm = Self) or    //None of our modal dlgs is showing
          (GetLastActivePopup(Application.Handle) = Application.Handle)   //No system dlgs are modal (FileOpen, etc.)
          then begin

        Sleep(200);  //<== THE SOLUTION. Note: values of 150 or less failed!

        SendTabKey(True);       //True=Shift Tab
        SendTabKey(True);
        Sleep(1000);            //...to observe UI effect
        SendTabKey(False);      //False=Tab
      end;
  end;
end;

end.
  

Ответ №1:

Я предполагаю, что целевое приложение считывает состояние Ctrl клавиши и, распознав его, фактически реагирует на Shift Ctrl Tab вместо Shift Tab .

Отправьте клавишу «ctrl» перед Shift Tab, когда ваша горячая клавиша включает клавишу «ctrl».

 begin
  KeyInputCount := 0;

  if CtrlInHotkey then              // to be implemented
    KeybdInput(VK_CONTROL, KEYEVENTF_KEYUP);

  if bShifted then
    KeybdInput(VK_SHIFT, 0);
  ...
  

Комментарии:

1. Отличная идея … спасибо! В конечном итоге это не было точным решением, но при тестировании вашей идеи я наткнулся на решение как на проблему с синхронизацией, поэтому я принимаю ваш ответ и изменил код, чтобы показать, что добавление Sleep (200) перед последовательностью нажатий клавиш решило проблему …. теперь мне просто нужно изучить, почему и при других обстоятельствах. (Меня удивляет, что 200 мс. требовалось, и меньшие значения работали ненадежно.)

2. @Mark — Всегда пожалуйста. Я немного озадачен тем, что вы наблюдаете. Режим ожидания, являющийся решением, предполагает, что вы, возможно, ожидаете нажатия клавиши «ctrl». Но в этом случае то, что я предложил, должно работать надежно.

3. Я тоже озадачен. Я только сейчас понял, что у аналогичного приложения, которое я написал давным-давно, нет такой проблемы с Ctrl в последовательности горячих клавиш (не требуется длительный режим ожидания () перед отправкой нажатий клавиш). Он использует цикл PeekMessage (), чтобы проверить, обрабатывало ли целевое приложение каждое отправленное мной нажатие клавиши SendInput, чего не делает вышеупомянутое тестовое приложение, потому что у меня нет дескриптора окна цели. Мне придется поэкспериментировать … и я прокомментирую здесь, когда что-нибудь узнаю. Еще раз спасибо.

4. Я попробовал несколько подходов, и на данный момент я отказался от попыток выяснить, почему Sleep (200) работает и / или необходим. Может быть, когда-нибудь я наткнусь на причину. До тех пор добавление этого в мой код не сильно мешает.