Paul
Paul

Reputation: 105

why does a PostMessage to external app acts different from the keyboard

I'm writing a delphi app (myApp) to input keyboard strokes to an external app (extApp).

When extApp launches in windows 7 and I press {tab}{tab}{down} i have the appropriate item in the list box selected. The down arrow selects the second item in the TListBox (WinSpy++ tells me it's a TListBox).

Why then does myApp using this code not have the same functionality as the keyboard? This code will select the TListBox (a result of {tab}{tab} and then NOT move the ListBox selection!

    procedure TfrmMain.run;
    begin
      setForegroundWindow(extApp);
      sendTab(extApp);
      sendTab(extApp);
      sendDown(extApp);
    end

    procedure TfrmMain.sendTab(h: HWND);
    begin
      PostMessage(h, WM_KEYDOWN, VK_TAB, 0);
      PostMessage(h, WM_KEYUP,   VK_TAB, 0);
    end;
    procedure TfrmMain.sendDown(h:HWND);
    begin
      PostMessage(h, WM_KEYDOWN, VK_DOWN, 0);
      PostMessage(h, WM_KEYUP,   VK_DOWN  , 0);
    end;

Is there a different way I can send a down arrow key stroke to my external app that will mimic the physical keyboard?

Upvotes: 1

Views: 426

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 595827

You are not setting any of the appropriate state flags in the lParam parameter of the messages. At a minimum, you should be setting the bits for scan codes, previous key states, and transition states, eg:

procedure TfrmMain.sendTab(h: HWND);
var
  ScanCode: UINT:
begin
  ScanCode := MapVirtualKey(VK_TAB, 0) shl 16;
  PostMessage(h, WM_KEYDOWN, VK_TAB, ScanCode);
  Sleep(50);
  PostMessage(h, WM_KEYUP,   VK_TAB, ScanCode or $C0000001);
end;

procedure TfrmMain.sendDown(h:HWND);
var
  ScanCode: UINT:
begin
  ScanCode := MapVirtualKey(VK_DOWN, 0) shl 16;
  PostMessage(h, WM_KEYDOWN, VK_DOWN, ScanCode);
  Sleep(50);
  PostMessage(h, WM_KEYUP,   VK_DOWN, ScanCode or $C0000001);
end;

That being said, you can't simulate keyboard input with PostMessage, so use SendInput() instead, like Ken White suggested (though you lose the ability to direct the keyboard input to a specific window, it will always go to the foreground window - but then, so does real keyboard input).

Upvotes: 1

Ken White
Ken White

Reputation: 125671

The proper way to do so is using SendInput. Something like this should get you started:

var
  Inputs: array of TInput;
  ScanCode: Word;
begin
  ScanCode := MapVirtualKey(VK_TAB, 0);

  SetLength(Inputs, 2);
  Inputs[0].iType := INPUT_KEYBOARD;
  Inputs[0].ki.wVk := VK_TAB;
  Inputs[0].ki.wScan := ScanCode;

  Inputs[1].iType := INPUT_KEYBOARD;
  Inputs[1].ki.wVk := VK_TAB;
  Inputs[1].ki.wScan := ScanCode;
  Inputs[i].ki.dwFlags := KEYEVENTF_KEYUP;

  SendInput(2, Inputs[0], SizeOf(Inputs[0]));
end;

Upvotes: 3

Related Questions