I have to control another application by sending keystrokes to it like CTRLS or CTRLSHIFTC or CTRLF.
I've tried a lot of things, but I can't get it working. So I'm trying to get this right on a simpler case.
This successfully sends Hey
to the notepad:
procedure TForm1.Button1Click(Sender: TObject);
var notepad, edit: HWND;
begin
notepad := FindWindow('notepad', nil);
edit := FindWindowEx(notepad, FindWindow('Edit', nil), nil, nil);
SendMessage(edit, WM_CHAR, dword('H'), 0);
SendMessage(edit, WM_CHAR, dword('e'), 0);
SendMessage(edit, WM_CHAR, dword('y'), 0);
end;
And this successfully sends the F5 key to the notepad, and also works with F3 poping up the Find dialog.
notepad := FindWindow('notepad', nil);
PostMessage(notepad, WM_KEYDOWN, VK_F5, 0);
PostMessage(notepad, WM_KEYUP, VK_F5, 0);
But I don't know why using SendMessage
doesn't work on the exemple above.
The best thing I could came up was something like this, which does nothing.
notepad := FindWindow('notepad', nil);
PostMessage(notepad, WM_KEYDOWN, VK_CONTROL, 0);
PostMessage(notepad, WM_KEYDOWN, VkKeyScan('F'), 0);
PostMessage(notepad, WM_KEYUP, VkKeyScan('F'), 0);
PostMessage(notepad, WM_KEYUP, VK_CONTROL, 0);
I've found somewhere here a library which kinda emulate the VBScript send key functions, but just looking the code, it seems to just broadcast keys to the current application or all applications, since there's no Handle parameter.
Warning: This method depends on implementation details, and should not be used if you need to guarantee the correctness of your program. (On the other hand, you are already on that path. For instance, IIRC, in Windows 95 there isn't even a Go to
dialog.)
I opened notepad.exe
in my favourite Resource Editor, and investigated the menu bar. I noticed that the ID of the Save
menu item is 3
. Hence, the following code executes the Save
menu command in notepad:
var
notepad: HWND;
begin
notepad := FindWindow('notepad', nil);
SendMessage(notepad, WM_COMMAND, 3, 0);
Similarly, Find
is 21
in my version of notepad.exe
. Go to
is 24
.
Update, according to comment: If you need to send Ctrl+Key, you can use SendInput
:
var
notepad: HWND;
inputArray: array[0..3] of TInput;
begin
notepad := FindWindow('notepad', nil);
// TODO: Either exit if notepad isn't focused, or set focus to notepad
FillChar(inputArray, length(inputArray) * sizeof(TInput), 0);
inputArray[0].Itype := INPUT_KEYBOARD;
inputArray[0].ki.wVk := VK_LCONTROL;
inputArray[1].Itype := INPUT_KEYBOARD;
inputArray[1].ki.wVk := VkKeyScan('S');
inputArray[2].Itype := INPUT_KEYBOARD;
inputArray[2].ki.wVk := VkKeyScan('S');
inputArray[2].ki.dwFlags := KEYEVENTF_KEYUP;
inputArray[3].Itype := INPUT_KEYBOARD;
inputArray[3].ki.wVk := VK_LCONTROL;
inputArray[3].ki.dwFlags := KEYEVENTF_KEYUP;
SendInput(length(inputArray), inputArray[0], sizeof(TInput));
I've been using keybd_event for ages. It will always work even if everything else fails because it feeds the input directly into the interface between the keyboard driver and Windows. There's really no difference between typing manually and generating keys using the function below. Only disadvantage is that the target window must always remain in the foreground.
procedure SendKey(Wnd,VK : Cardinal; Ctrl,Alt,Shift : Boolean);
var
MC,MA,MS : Boolean;
begin
// Try to bring target window to foreground
ShowWindow(Wnd,SW_SHOW);
SetForegroundWindow(Wnd);
// Get current state of modifier keys
MC:=Hi(GetAsyncKeyState(VK_CONTROL))>127;
MA:=Hi(GetAsyncKeyState(VK_MENU))>127;
MS:=Hi(GetAsyncKeyState(VK_SHIFT))>127;
// Press modifier keys if necessary (unless already pressed by real user)
if Ctrl<>MC then keybd_event(VK_CONTROL,0,Byte(MC)*KEYEVENTF_KEYUP,0);
if Alt<>MA then keybd_event(VK_MENU,0,Byte(MA)*KEYEVENTF_KEYUP,0);
if Shift<>MS then keybd_event(VK_SHIFT,0,Byte(MS)*KEYEVENTF_KEYUP,0);
// Press key
keybd_event(VK,0,0,0);
keybd_event(VK,0,KEYEVENTF_KEYUP,0);
// Release modifier keys if necessary
if Ctrl<>MC then keybd_event(VK_CONTROL,0,Byte(Ctrl)*KEYEVENTF_KEYUP,0);
if Alt<>MA then keybd_event(VK_MENU,0,Byte(Alt)*KEYEVENTF_KEYUP,0);
if Shift<>MS then keybd_event(VK_SHIFT,0,Byte(Shift)*KEYEVENTF_KEYUP,0);
end;