import os import msvcrt import ctypes from ctypes import wintypes kernel32 = ctypes.WinDLL('kernel32', use_last_error=True) KEY_EVENT = 0x0001 class KEY_EVENT_RECORD(ctypes.Structure): class UCHAR(ctypes.Union): _fields_ = (('UnicodeChar', wintypes.WCHAR), ('AsciiChar', wintypes.CHAR)) _fields_ = (('bKeyDown', wintypes.BOOL), ('wRepeatCount', wintypes.WORD), ('wVirtualKeyCode', wintypes.WORD), ('wVirtualScanCode', wintypes.WORD), ('uChar', UCHAR), ('dwControlKeyState', wintypes.DWORD)) class INPUT_RECORD(ctypes.Structure): class EVENT(ctypes.Union): _fields_ = (('KeyEvent', KEY_EVENT_RECORD),) _fields_ = (('EventType', wintypes.WORD), ('Event', EVENT)) PINPUT_RECORD = ctypes.POINTER(INPUT_RECORD) def _check_zero(result, func, args): if not result: raise ctypes.WinError(ctypes.get_last_error()) return args kernel32.WriteConsoleInputW.errcheck = _check_zero kernel32.WriteConsoleInputW.argtypes = ( wintypes.HANDLE, # _In_ hConsoleInput PINPUT_RECORD, # _In_ lpBuffer wintypes.DWORD, # _In_ nLength wintypes.LPDWORD) # _Out_ lpNumberOfEventsWritten def write_console_input(s): length = len(s) + sum(1 for c in s if ord(c) > 65535) recs = (INPUT_RECORD * length)() i = 0 for char in s: if ord(char) > 65535: b = char.encode('utf-16le') char = [b[:2].decode('utf-16le', 'surrogatepass'), b[2:].decode('utf-16le', 'surrogatepass')] for c in char: recs[i].EventType = KEY_EVENT recs[i].Event.KeyEvent.bKeyDown = True recs[i].Event.KeyEvent.wRepeatCount = 1 recs[i].Event.KeyEvent.uChar.UnicodeChar = c i = i + 1 fd = os.open('CONIN$', os.O_RDWR | os.O_BINARY) hInput = msvcrt.get_osfhandle(fd) total = 0 precs = ctypes.byref(recs[0]) n = wintypes.DWORD() try: # This loop is probably unnecessary, since the input buffer # expands dynamically to allow writing any number of records. while True: kernel32.WriteConsoleInputW(hInput, precs, len(recs) - total, ctypes.byref(n)) total += n.value if total >= len(recs): break precs = ctypes.byref(recs[total]) finally: os.close(fd) return total if __name__ == '__main__': import sys test_input = 'test input: U00010000\r\n' n = write_console_input(test_input) assert n == len(test_input) line = sys.stdin.buffer.readline() read_input = line.decode(sys.stdin.encoding, sys.stdin.errors) assert read_input == test_input # WideCharToMultiByte requires valid UTF, so thelone surrogate # gets replaced by U+FFFD. Generally the Windows wide-character # API allows lone surrogates, so this should probably be fixed. # Doing that is easier said than done. test_input = 'test input: \udc00\r\n' n = write_console_input(test_input) assert n == len(test_input) line = sys.stdin.buffer.readline() read_input = line.decode(sys.stdin.encoding, sys.stdin.errors) assert read_input[-3] == '\ufffd'