我需要控制一个Windows程序,它通过从<conio.h>
调用_kbhit
和_getch
从控制台直接读取input。 这样的程序的例子可以在这里find: https : //stackoverflow.com/a/15603102/365492
在Linux上,我可以使用pty.openpty()
来创build新的伪terminal并模拟按键。 看到这个例子: https : //code.google.com/p/lilykde/source/browse/trunk/lilykde/py/runpty.py
在Windows上,我试图写CONIN$
/ CONOUT$
但我能看到的是我的数据出现在控制台上,而subprocess忽略它。
这里是代码:
#!/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()
有没有可能? 我如何模仿用户与subprocess的交互?
谢谢。 亚历克斯
我找到了答案。 不幸的是,没有内置的模块来做到这一点,所以我不得不使用ctypes
和一些Win32 API来实现这一点。 这里是代码:
#!/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()
input.txt
文件每行包含一个字符。 write_key_to_console
函数可以很容易地扩展到一次写入几个字符。
如果调用进程没有控制台或者其控制台与子进程不同,那么在打开CONIN$
文件之前,我们需要调用带有子进程ID的AttachConsole
函数。