def send_key_to_window(target: Union[str, int], key: str, use_sendinput: bool = True) -> Dict[str, Any]:
"""
Send a keystroke to a specific window.
Args:
target: Window title (string) or HWND handle (integer)
key: Key to send (e.g., "a", "enter", "space", "up", "down", "left", "right")
use_sendinput: If True, use SendInput (requires window focus). If False, use PostMessage.
Returns:
Success status.
"""
if not SCREENSHOT_AVAILABLE:
return {"error": "Window control not available. Install: pip install pywin32"}
# Key code mapping
key_codes = {
"enter": 0x0D, "return": 0x0D,
"space": 0x20,
"up": 0x26, "down": 0x28, "left": 0x25, "right": 0x27,
"escape": 0x1B, "esc": 0x1B,
"tab": 0x09,
"backspace": 0x08,
"w": 0x57, "a": 0x41, "s": 0x53, "d": 0x44,
"r": 0x52, "q": 0x51, "e": 0x45, "h": 0x48, "g": 0x47,
"p": 0x50, "l": 0x4C,
"1": 0x31, "2": 0x32, "3": 0x33, "4": 0x34, "5": 0x35,
}
try:
# Find the window
hwnd = None
if isinstance(target, int):
hwnd = target
else:
def find_window(h, _):
nonlocal hwnd
if win32gui.IsWindowVisible(h):
title = win32gui.GetWindowText(h)
if title and target.lower() in title.lower():
hwnd = h
return False
return True
win32gui.EnumWindows(find_window, None)
if not hwnd:
return {"error": f"Window not found: {target}"}
# Get key code
key_lower = key.lower()
if key_lower in key_codes:
vk_code = key_codes[key_lower]
elif len(key) == 1:
vk_code = ord(key.upper())
else:
return {"error": f"Unknown key: {key}"}
import time
if use_sendinput:
# Use SendInput - requires bringing window to foreground first
# This works better for console windows and games
# Try to bring window to foreground
try:
# Attach to foreground thread to allow SetForegroundWindow
foreground = win32gui.GetForegroundWindow()
foreground_thread = ctypes.windll.user32.GetWindowThreadProcessId(foreground, None)
current_thread = ctypes.windll.kernel32.GetCurrentThreadId()
ctypes.windll.user32.AttachThreadInput(current_thread, foreground_thread, True)
win32gui.SetForegroundWindow(hwnd)
ctypes.windll.user32.AttachThreadInput(current_thread, foreground_thread, False)
except:
pass
time.sleep(0.1)
# SendInput structure
INPUT_KEYBOARD = 1
KEYEVENTF_KEYUP = 0x0002
class KEYBDINPUT(ctypes.Structure):
_fields_ = [
("wVk", wintypes.WORD),
("wScan", wintypes.WORD),
("dwFlags", wintypes.DWORD),
("time", wintypes.DWORD),
("dwExtraInfo", ctypes.POINTER(ctypes.c_ulong))
]
class INPUT(ctypes.Structure):
class _INPUT(ctypes.Union):
_fields_ = [("ki", KEYBDINPUT)]
_anonymous_ = ("_input",)
_fields_ = [
("type", wintypes.DWORD),
("_input", _INPUT)
]
# Key down
inp = INPUT(type=INPUT_KEYBOARD)
inp.ki.wVk = vk_code
inp.ki.wScan = 0
inp.ki.dwFlags = 0
inp.ki.time = 0
inp.ki.dwExtraInfo = None
ctypes.windll.user32.SendInput(1, ctypes.byref(inp), ctypes.sizeof(inp))
time.sleep(0.05)
# Key up
inp.ki.dwFlags = KEYEVENTF_KEYUP
ctypes.windll.user32.SendInput(1, ctypes.byref(inp), ctypes.sizeof(inp))
else:
# Use PostMessage (doesn't require foreground, but may not work for all windows)
WM_KEYDOWN = 0x0100
WM_KEYUP = 0x0101
WM_CHAR = 0x0102
if len(key) == 1 and key.isalpha():
win32gui.PostMessage(hwnd, WM_KEYDOWN, vk_code, 0)
win32gui.PostMessage(hwnd, WM_CHAR, ord(key.lower()), 0)
time.sleep(0.05)
win32gui.PostMessage(hwnd, WM_KEYUP, vk_code, 0)
else:
win32gui.PostMessage(hwnd, WM_KEYDOWN, vk_code, 0)
time.sleep(0.05)
win32gui.PostMessage(hwnd, WM_KEYUP, vk_code, 0)
return {
"success": True,
"window": win32gui.GetWindowText(hwnd),
"key_sent": key,
"method": "SendInput" if use_sendinput else "PostMessage"
}
except Exception as e:
return {"error": f"Failed to send key: {str(e)}"}