SendInput показывает данные, отправленные не по порядку

Я экспериментирую с SendInput, отправляя строки в заметку. Я смешиваю команды SendInput с вызовами Memo.Lines.Add ('....'). К моему удивлению, все команды Memo.Lines.Add выполняются перед любой из подпрограмм SendInput. Почему? Как я могу заставить заметку отображать информацию в правильном порядке?

Мой код выглядит так:

procedure TForm1.Button1Click(Sender: TObject);
const
  AStr = '123 !@# 890 *() abc ABC';
var
  i: integer;
  KeyInputs: array of TInput;

  procedure KeybdInput(VKey: Byte; Flags: DWORD);
  begin
    SetLength(KeyInputs, Length(KeyInputs)+1);
    KeyInputs[high(KeyInputs)].Itype := INPUT_KEYBOARD;
    with  KeyInputs[high(KeyInputs)].ki do
    begin
      wVk := VKey;
      wScan := MapVirtualKey(wVk, 0);
      dwFlags := Flags;
    end;
  end;

begin
  Memo1.SetFocus;
  Memo1.Lines.Add('AStr := ' + AStr);

  Memo1.Lines.Add('');
  Memo1.Lines.Add('Use:   KeybdInput(ord(AStr[i]),0)');
  SetLength(KeyInputs,0);
  for i := 1 to Length(AStr) do KeybdInput(ord(AStr[i]),0);
  SendInput(Length(KeyInputs), KeyInputs[0], SizeOf(KeyInputs[0]));

  Memo1.Lines.Add('');
  Memo1.Lines.Add('Use:   KeybdInput(vkKeyScan(AStr[i]),0)');
  SetLength(KeyInputs,0);
  for i := 1 to Length(AStr) do KeybdInput(vkKeyScan(AStr[i]),0);
  SendInput(Length(KeyInputs), KeyInputs[0], SizeOf(KeyInputs[0]));
end;

И я ожидал, что результат будет таким:

Но на самом деле это выглядит так:


person Rudi    schedule 21.09.2016    source источник


Ответы (1)


Клавиатурный ввод, который вы отправляете с SendInput, проходит через систему обмена сообщениями Windows и попадает в очередь сообщений ваших приложений. Очередь сообщений не обрабатывается до выхода из Button1Click().

Когда что-то добавляется в очередь, для его выхода требуется время. в начале очереди

Чтобы увидеть события в ожидаемом порядке, вам нужно будет вставлять вызовы Application.Processmessages() после каждого SendInput(). Однако вызывать Application.ProcessMessages() не рекомендуется:

Темная сторона сообщений Application.ProcessMessages в приложениях Delphi

person Tom Brunberg    schedule 21.09.2016
comment
Вызов ProcessMessages() сразу после SendInput() не гарантирует, что нажатия клавиш будут доступны. Как объясняет Рэймонд Чен в этой статье блога, требуется время, чтобы сообщения от SendInput() доходили до приложений. Нажатия клавиш должны проходить через несколько уровней очередей и обработки, поэтому они все еще могут находиться в полете, когда вы вызываете ProcessMessages() (который обрабатывает только сообщения, которые приложение уже уже получило). - person Remy Lebeau; 21.09.2016
comment
@RemyLebeau Спасибо за интересную статью, объясняющую, почему она все еще может выйти из строя. Я попробовал, прежде чем опубликовать свой ответ, и на моей машине в условиях нагрузки он работал, но я понимаю, что могут потребоваться другие меры. - person Tom Brunberg; 21.09.2016
comment
Также возможно, что пока нажатия клавиш находятся в полете, другое приложение может украсть фокус ввода и, таким образом, получить нажатия клавиш вместо приложения, которое отправило их с самого начала. Как писал в блоге Раймонд, SendInput() выполняет инъекцию в ту же очередь, что и фактическая клавиатура, вы не можете контролировать, какие цели будут получать ввод. - person Remy Lebeau; 21.09.2016