How to Send Keystrokes to a Window

How do I send key strokes to a window without having to activate it using Windows API?

Alright, this is kind of disappointing I'm sure, but you fundamentally cannot do this with 100% reliability.

Windows assumes that the active window is the one getting keyboard input. The proper way to fake keyboard input is with SendInput, and you'll notice that it sends messages to the active window only.

That being said, you can SendMessage WM_KEYUP, WM_CHAR, and WM_KEYDOWN messages and (depending on the WndProc receiving them) maybe get away with it. But remember, its going to break under some circumstances, period.

How to send keystrokes to a window?

using SendMessage to insert text into the edit buffer (which it sounds like you want):

HWND notepad = FindWindow(_T("Notepad"), NULL);
HWND edit = FindWindowEx(notepad, NULL, _T("Edit"), NULL);
SendMessage(edit, WM_SETTEXT, NULL, (LPARAM)_T("hello"));

if you need keycodes and arbitrary keystrokes, you can use SendInput() (available in 2k/xp and preferred), or keybd_event()` (which will end up calling SendInput in newer OSs) some examples here:

http://www.codeguru.com/forum/showthread.php?t=377393

there's also WM_SYSCOMMAND/WM_KEYDOWN/WM_KEYUP/WM_CHAR events for SendMessage which you might be interested in.

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

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.

How do I send keystrokes to a background window using Python & pywinauto without bringing that window into the foreground?

If you bring the window to the foreground, it handles all keyboard input and it will direct keyboard input to the control that has focus. When the window is not in the foreground, none of its control have active focus and keys sent to the window won't automatically get sent to the control you want to receive the input.

I use Foxit reader, but the same would apply for Acrobat or other applications. I tried this:

from time import sleep
import win32gui
import win32con

def callback(handle, param):
s = win32gui.GetClassName(handle)
try:
print(f'Sending key to {handle}, {s}')
win32gui.SendMessage(handle, win32con.WM_KEYDOWN, win32con.VK_NEXT, 0)
win32gui.SendMessage(handle, win32con.WM_KEYUP, win32con.VK_NEXT, 0)
sleep(2)
except Exception:
print('Exception sending to {handle}, {s}')

window_id = win32gui.FindWindow(None, "my_multipage_doc.pdf - Foxit Reader")
win32gui.EnumChildWindows(window_id, callback, 0)

That produced a lot of output, one line for each object in the window, but this is a relevant bit:

...
Sending key to 594376, ScrollBar
Sending key to 1904808, ScrollBar
Sending key to 397704, ScrollBar
Sending key to 397598, AfxFrameOrView140su
Sending key to 397580, AfxWnd140su
Sending key to 397734, AfxWnd140su
Sending key to 1971214, AfxWnd140su
Sending key to 856494, FoxitDocWnd
Sending key to 986558, Static
...

Both when a VK_NEXT was sent to the first Scrollbar as when it was sent to the FoxitDocWnd, the viewer scrolled down by one page.

So, I rewrote as this:

import win32gui
import win32con

def send_page_down(handle, param):
if win32gui.GetClassName(handle) == param:
win32gui.SendMessage(handle, win32con.WM_KEYDOWN, win32con.VK_NEXT, 0)
win32gui.SendMessage(handle, win32con.WM_KEYUP, win32con.VK_NEXT, 0)

window_id = win32gui.FindWindow(None, "my_multipage_doc.pdf - Foxit Reader")
win32gui.EnumChildWindows(window_id, send_page_down, 'FoxitDocWnd')

That does what you need.

Oddly, something else I tried, didn't work:

import win32gui
import win32con

window_id = win32gui.FindWindow(None, "my_multipage_doc.pdf - Foxit Reader")
viewer_id = win32gui.FindWindowEx(window_id, 0, 'FoxitDocWnd', None)
win32gui.SendMessage(viewer_id , win32con.WM_KEYDOWN, win32con.VK_NEXT, 0)
win32gui.SendMessage(viewer_id , win32con.WM_KEYUP, win32con.VK_NEXT, 0)

But that fails when trying to get viewer_id. So, even though the FoxitDocWnd shows up when enumerating all child windows, it cannot find it explicitly. If you can find what's wrong with that, that would be a nicer solution.

Send combination of keystrokes to background window

Ok I found a workaround, but it doesn't work for all applications. Otherwise, it works with puTTY, the program I wanted to control with keystroke combination. And it works even if the application isn't focused. So I'm done now!

class SendMessage
{
[DllImport("user32.dll")]
public static extern IntPtr PostMessage(IntPtr hWnd, uint Msg, IntPtr wParam, IntPtr lParam);

public static void sendKeystroke()
{
const uint WM_KEYDOWN = 0x100;
const uint WM_KEYUP = 0x0101;

IntPtr hWnd;
string processName = "putty";
Process[] processList = Process.GetProcesses();

foreach (Process P in processList)
{
if (P.ProcessName.Equals(processName))
{
IntPtr edit = P.MainWindowHandle;
PostMessage(edit, WM_KEYDOWN, (IntPtr)(Keys.Control), IntPtr.Zero);
PostMessage(edit, WM_KEYDOWN, (IntPtr)(Keys.A), IntPtr.Zero);
PostMessage(edit, WM_KEYUP, (IntPtr)(Keys.Control), IntPtr.Zero);
}
}
}

}

Sending Windows key using SendKeys

OK turns out what you really want is this: http://inputsimulator.codeplex.com/

Which has done all the hard work of exposing the Win32 SendInput methods to C#. This allows you to directly send the windows key. This is tested and works:

InputSimulator.SimulateModifiedKeyStroke(VirtualKeyCode.LWIN, VirtualKeyCode.VK_E);

Note however that in some cases you want to specifically send the key to the application (such as ALT+F4), in which case use the Form library method. In others, you want to send it to the OS in general, use the above.


Old

Keeping this here for reference, it will not work in all operating systems, and will not always behave how you want. Note that you're trying to send these key strokes to the app, and the OS usually intercepts them early. In the case of Windows 7 and Vista, too early (before the E is sent).

SendWait("^({ESC}E)") or Send("^({ESC}E)")

Note from here: http://msdn.microsoft.com/en-us/library/system.windows.forms.sendkeys.aspx

To specify that any combination of SHIFT, CTRL, and ALT should be held
down while several other keys are pressed, enclose the code for those
keys in parentheses. For example, to specify to hold down SHIFT while
E and C are pressed, use "+(EC)". To specify to hold down SHIFT while
E is pressed, followed by C without SHIFT, use "+EC".

Note that since you want ESC and (say) E pressed at the same time, you need to enclose them in brackets.

How to send key-ins to certain windows

To send key strokes you can use java.awt.Robot

To Choose which window to activate you can look around for ws script (windows script) or old VB6 code and use it to make VB script (simple text file of extn .vbs)

Or you can junk all that and use http://www.autohotkey.com/ which has window activation, sending keys, doing things on press of certain keys (like Windows Key + B) or macros.


To get a window to activate I had made an exe long back, but no longer use it, can get it from http://sourceforge.net/projects/win-utils/files/Window-Position/rel%2001/ (but only get this if the others do not work as need to seperately get COMCTL32.ocx and install that


If you do not want to use autohotkey you can use Jini to call platform specific functions, with a wrapper to call correst OSes functions. Never done it my self, when i had to use it i would make a process to call a exe that made the window come the front.

Sending keystrokes to inactive window

in similar thread i saw this worked for people. and your code as is by changing title name works for me but you need to modify the title input order to this format.

 hwndMain = win32gui.FindWindow( "Google Chrome" , None)

TO

hwndMain = win32gui.FindWindow( None , "Google Chrome")


Related Topics



Leave a reply



Submit