I found the answer. Unfortunately, there is no built-in modules to do this, so I had to use ctypes and some Win32 API to accomplish this. Here is the code:
#!/usr/bin/env python from ctypes import * import msvcrt import os import subprocess import time TEST_EXECUTABLE = 'C:\\dev\\test.exe' TEST_INPUT = 'C:\\dev\\input.txt' # input event types KEY_EVENT = 0x0001 # constants, flags MAPVK_VK_TO_VSC = 0 # structures class CHAR_UNION(Union): _fields_ = [("UnicodeChar", c_wchar), ("AsciiChar", c_char)] def to_str(self): return '' class KEY_EVENT_RECORD(Structure): _fields_ = [("bKeyDown", c_byte), ("pad2", c_byte), ("pad1", c_short), ("wRepeatCount", c_short), ("wVirtualKeyCode", c_short), ("wVirtualScanCode", c_short), ("uChar", CHAR_UNION), ("dwControlKeyState", c_int)] def to_str(self): return '' class INPUT_UNION(Union): _fields_ = [("KeyEvent", KEY_EVENT_RECORD)] def to_str(self): return '' class INPUT_RECORD(Structure): _fields_ = [("EventType", c_short), ("Event", INPUT_UNION)] def to_str(self): return '' def write_key_to_console(hcon, key): li = INPUT_RECORD * 2 list_input = li() ke = KEY_EVENT_RECORD() ke.bKeyDown = c_byte(1) ke.wRepeatCount = c_short(1) cnum = ord(key) ke.wVirtualKeyCode = windll.user32.VkKeyScanW(cnum) ke.wVirtualScanCode = c_short(windll.user32.MapVirtualKeyW(int(cnum), MAPVK_VK_TO_VSC)) ke.uChar.UnicodeChar = unichr(cnum) kc = INPUT_RECORD(KEY_EVENT) kc.Event.KeyEvent = ke list_input[0] = kc list_input[1] = list_input[0] list_input[1].Event.KeyEvent.bKeyDown = c_byte(0) events_written = c_int() ret = windll.kernel32.WriteConsoleInputW(hcon, list_input, 2, byref(events_written)) return ret def main(): with open(TEST_INPUT, mode='r') as test_input: fdcon = os.open('CONIN$', os.O_RDWR | os.O_BINARY) hconin = msvcrt.get_osfhandle(fdcon) test_exec = subprocess.Popen([TEST_EXECUTABLE]) for cmd in test_input: cmd = cmd.strip('\r\n') write_key_to_console(hconin, cmd) time.sleep(1) os.close(fdcon) ret = test_exec.wait() print '%s (%d): %d' % (TEST_EXECUTABLE, test_exec.pid, ret) pass if __name__ == "__main__": main()
The input.txt file contains one character per line. write_key_to_console function can be easily extended to write several characters at once.
If the calling process doesn't have a console or its console is different from the one of the child process, then we need to call AttachConsole function with child process ID as parameter before we open CONIN$ file.