4

I need to control a Windows program, which reads input directly from console by calling _kbhit and _getch from <conio.h>. An example of such program can be found here: https://stackoverflow.com/a/15603102/365492

On Linux I can use pty.openpty() to create new pseudo-terminal and to emulate key presses. See this example: https://code.google.com/p/lilykde/source/browse/trunk/lilykde/py/runpty.py

On Windows I tried to write to CONIN$/CONOUT$ but all I can see is that my data is appearing on the console, while child process ignores it.

Here is the code:

#!/usr/bin/env python import subprocess import time TEST_EXECUTABLE = 'C:\\dev\\test.exe' TEST_INPUT = 'C:\\dev\\input.txt' def main(): with open(TEST_INPUT, mode='r') as test_input, open('CONOUT$', mode='wb') as conout: test_exec = subprocess.Popen([TEST_EXECUTABLE], bufsize=0, stdin=None, stdout=None, stderr=None) for cmd in test_input: cmd = cmd.strip('\r\n') conout.write(cmd) conout.flush() time.sleep(1) ret = test_exec.wait() print '%s (%d): %d' % (TEST_EXECUTABLE, test_exec.pid, ret) pass if __name__ == "__main__": main() 

Is it possible at all? How can I emulate user interaction with the child process?

Thanks. Alex

1 Answer 1

3

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.

Sign up to request clarification or add additional context in comments.

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.