10

I need to programmatically enter one character into a cell of a Delphi grid (in other application).

In order to do this manually, following steps are required:

  1. Press the F3 button.
  2. Press the right-arrow key 3 times.
  3. Press the space button.
  4. Type letter 'E' on the keyboard.
  5. Press the right-arrow key.

     // Press F3 button         
     keybd_event(VK_F3, 0, 0, 0);         
     // Press right arrow key 3 times
     keybd_event(VK_RIGHT, 0, 0, 0);
     keybd_event(VK_RIGHT, 0, 0, 0);
     keybd_event(VK_RIGHT, 0, 0, 0);
    
     // Press the space button
     keybd_event(VK_SPACE, 0, 0, 0);
    
     // Type letter E
     keybd_event(Ord('E'), 0, 0, 0);
    
     // Move to the right
     keybd_event(VK_RIGHT, 0, 0, 0);
    

But it doesn't work. When I run this code, nothing seems to happen.

How should I modify this code so that it actually simulates user input?

Glory to Russia
  • 17,289
  • 56
  • 182
  • 325
  • I've made something about [`virtual keyboard`](http://stackoverflow.com/a/12565963/960757) some time ago (and there will be many others). But to your problem, if you are targeting `TStringGrid`, I think you can't do anything else than focus it and simulate your keystrokes (if I remember that right, `TStringGrid` doesn't respond to any kind of message that would be able to set the text of a cell). – TLama Oct 18 '12 at 09:14
  • In this particular application it is possible to enter values into that grid only via keyboard. I've done this already with AutoHotKey and MS UI Automation, but now need to rewrite this code in Delphi. – Glory to Russia Oct 18 '12 at 09:19
  • I can't rely on the grid being focused because there is a program on the target system, which activates itself every 200 milliseconds. – Glory to Russia Oct 18 '12 at 09:22
  • You can try to use the `WM_KEYDOWN`, `WM_KEYUP` messages, but I'm not sure it will always work between two processes... – TLama Oct 18 '12 at 09:24
  • Here is un example (with source code) of autoclic that simulates keystrokes and mouse clicks and then play them in a sequence. http://delphimagic.blogspot.com.es/2010/03/autoclic-con-delphi.html – Carlos Dec 29 '12 at 13:58

2 Answers2

18

Each key press is a key down and then a key up. So you need two calls to keybd_event per key press. For example, to press F3:

keybd_event(VK_F3, 0, KEYEVENTF_KEYDOWN, 0);
keybd_event(VK_F3, 0, KEYEVENTF_KEYUP, 0);

Note that KEYEVENTF_KEYDOWN isn't actually defined by the Windows header files, or the Delphi translation. Define it to be 0. It makes the code clearer written out explicitly though.

Naturally you would not litter your code with paired calls to keybd_event. But instead you would wrap up the paired calls into a helper function.

It's possible that in some situations you would need to specify the second parameter, the scan code. But it's often not necessary.

David Heffernan
  • 601,492
  • 42
  • 1,072
  • 1,490
  • Thanks. Does `keybd_event` work, when the control in question is NOT focused? – Glory to Russia Oct 18 '12 at 09:12
  • @DmitriPisarenko Not it does not. Input messages go to the control with the input focus. – David Heffernan Oct 18 '12 at 09:15
  • Is it possible to replace `keybd_event` calls with sending windows messages to the grid? – Glory to Russia Oct 18 '12 at 09:20
  • @DmitriPisarenko Probably. Use `PostMessage` with `WM_KEYDOWN` and `WM_KEYUP`. – David Heffernan Oct 18 '12 at 09:24
  • 1
    Regarding `lParam` of `WM_KEYUP` message. According to http://msdn.microsoft.com/en-us/library/windows/desktop/ms646281(v=vs.85).aspx there are 3 1-bits in `lParam`, which gives me a value of `0x8000C`. Reason: https://dl.dropbox.com/u/11776689/2012_10_18_delphi_winapi_bits.png Is this correct? – Glory to Russia Oct 18 '12 at 09:55
  • That's a different question altogether. I think I answered the question that you asked. – David Heffernan Oct 18 '12 at 10:01
  • Ad `lParam` for `WM_KEYUP`: It seems to be `0xC0000001`. – Glory to Russia Oct 18 '12 at 10:20
  • It is, but I wanted to make sure that everything works as it should. In the meantime, I encountered and fixed another problem ( http://stackoverflow.com/questions/12954545/strange-wm-char-behaviour-wrong-chcharcode ). Now the code seems to do what it should and I marked the question as answered. – Glory to Russia Oct 18 '12 at 13:53
0

Use https://github.com/WladiD/SendInputHelper by Mr. Waldemar Derr.

Example:

uses
  ..., SendInputHelper;

procedure TForm1.Button1Click(Sender: TObject);
var
  SIH: TSendInputHelper;
begin
  SIH := TSendInputHelper.Create;
  try
    // Start command shell
    SIH.AddShortCut([ssWin], 'r'); // Win+R
    SIH.AddDelay(100);
    SIH.AddText('cmd', True); // Second parameter True means AppendReturn
    SIH.AddDelay(500);

    SIH.AddText('ping google.de', True); // Perform a ping.

    SIH.Flush; // Isn't it easy?
  finally
    SIH.Free;
  end;
end;
Edwin Yip
  • 4,089
  • 4
  • 40
  • 86