2

I am experimenting with SendInput by sending strings to a memo. I mix the SendInput commands with calls to Memo.Lines.Add('....'). To my surprise all the Memo.Lines.Add commands execute before any of the SendInput routines. Why? How can I get the memo to display the information in the correct order?

My code looks like this:

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;

And I expected the result to look like this:

But it actually looks like this:

Rudi
  • 203
  • 2
  • 12

1 Answers1

3

The keyboard inputs that you send with SendInput goes through the Windows messaging system and end up in your applications message queue. The message queue is not processed before you exit from Button1Click().

When something gets added to a queue, it takes time for it to come out the front of the queue

To see the events in the order you are expecting, you would need to insert calls to Application.Processmessages() after each SendInput(). Calling Application.ProcessMessages() is not generally advisable, though:

The Dark Side of Application.ProcessMessages in Delphi Applications

Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Tom Brunberg
  • 20,312
  • 8
  • 37
  • 54
  • 2
    Calling `ProcessMessages()` *immediately* after `SendInput()` does not guarantee the keystrokes will be available. As Raymond Chen explains in [this blog article](https://blogs.msdn.microsoft.com/oldnewthing/20140213-00/?p=1773), it takes time for messages from `SendInput()` to reach applications. The keystrokes have to go through several layers of queues and processing, so they could still be in flight when you call `ProcessMessages()` (which only handles messages the app has *already* received). – Remy Lebeau Sep 21 '16 at 18:19
  • @RemyLebeau Thanks for the interesting article explaining why it could still fail. I did try before I posted my answer and in my machine under its load conditions it worked, but I acknowledge that other measures might be necessary. – Tom Brunberg Sep 21 '16 at 19:24
  • It is also possible that while the keystrokes are in flight, another app could steal the input focus and thus receive the keystrokes, instead of the app that sent them to begin with. As Raymond blogged, `SendInput()` injects into the same queue as the actual keyboard, you have no control over which target(s) will receive the input. – Remy Lebeau Sep 21 '16 at 19:53