Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Send keystrokes to a specific window (in background), but do something else in the meantime [duplicate]

This code (inspired from Which is the easiest way to simulate keyboard and mouse on Python?) opens a Notepad and send the keys A, B, C, D, ..., Z every second:

import win32com.client, time
shell = win32com.client.Dispatch("WScript.Shell")
shell.Run('Notepad')
time.sleep(1)
shell.AppActivate("Notepad")
for i in range(65,91):
    shell.SendKeys(chr(i))
    time.sleep(1)

I would like to let this operation continue in background, continue my work on the computer, and have the keystrokes sent to Notepad (in background).

Problem: if I open another application (example: browser) in the meantime, the keystrokes are sent ... to the currently active window, which I dont't want!

Question: how to have Python send the keystrokes to notepad.exe only, even if this application is not in foreground?

Context: I'm automating some long task requiring that my Python script sends keystrokes to app.exe (in background) during maybe 15 minutes, but I'd like to do something else with the computer in the meantime.

Note: More generally, the use case I'm interested in is the case where the process app.exe might open dialogs, close dialogs, open other windows, so the solution should be able to send the keystrokes to the active window of the process. Thus a solution with a fixed hWnd like here doesn't work directly.

like image 612
Basj Avatar asked Feb 26 '19 09:02

Basj


1 Answers

In order to send Keystroke to any application window, without activating the application to get input focus. We must get the windows handler first. This requires Windows API FindWindow and FindWindowsEx. First, the handle of Top Level window of application is obtained by FindWindow. Then use FindWindowsEx to get the handle of the child window or control to receive keys. Because the top window of the application is not always the window that accepts Keystroke (such as notepad.exe, the window that actually accepts Keystroke is the Edit control under the main window of Notepad), it can be found by ClassID or Caption.

Assuming that the handle of the target window has been got(hwnd), the key message will be sent to the window with PostMessage.

For normal character keys, it is simplest to use WM_CHAR message directly, as follows:

PostMessage(hwnd, WM_CHAR, 'a', 0);

For un-normal character keys, such as function keys, direction keys, etc., WM_KEYDOWN and WM_KEYUP messages should be used as follows:

VirtualKey = MapVirtualKeyA(VK_RIGHT, 0);
PostMessage(hwnd, WM_KEYDOWN, VK_RIGHT, 0x0001|VirtualKey<<16);
PostMessage(hwnd, WM_KEYUP, VK_RIGHT, 0x0001|VirtualKey<<16|0xC0<<24);

The details for last parameter (lParam) you can reference to msdn.

For the keys "Shift/Ctrl", sample:

keybd_event(VK_SHIFT, 0, 0, 0);
PostMessage(hwnd, WM_KEYDOWN, 0x41, 0x001E0001);
PostMessage(hwnd, WM_KEYUP, 0x41, 0xC01E0001);
keybd_event(VK_SHIFT, 0, KEYEVENTF_KEYUP, 0);

For the keys "Alt", It belongs to the system button, using WM_SYSKEYDOWN/WM_SYSKEYUP message. sample:

PostMessage(hwnd, WM_SYSKEYDOWN, VK_F4, 0x003E0001 |0x20000000);
PostMessage(hwnd, WM_SYSKEYUP, VK_F4, 0xC03E0001 | 0x20000000);

0x20000000 means context code, the value is 1 if the "Alt" key is down.

like image 96
Drake Wu Avatar answered Oct 22 '22 04:10

Drake Wu