You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何向VirtuaNES、ePSXe等游戏应用正确发送Win32窗口消息

问题背景
  • 已提前获取目标窗口句柄hwnd,可基于该句柄拿到对应线程ID、进程ID,也可通过线程ID获取窗口关联的cbSize、flags、hwndActive、hwndFocus、hwndCapture、hwndMenuOwner、hwndMoveSize、hwndCaret、rcCaret等详细信息。
  • 两类常规窗口的消息发送方案已验证可正常运行:
    • 向记事本发送消息:需要先枚举记事本主窗口下类名为EDIT的子窗口句柄,再向子句柄发送消息,实现代码如下:
win32gui.EnumChildWindows(notepadHwnd, callback, hwnds)
# callback用于收集枚举得到的hwnd
# hwnds为存储"child hwnd类名":hwnd键值对的对象
win32api.SendMessage(hwnds["EDIT"], win32con.WM_CHAR, ord('1'), 0)
  • 向Chrome发送消息:可直接向Chrome主窗口句柄发送按键消息,实现代码如下:
win32api.SendMessage(chromeHwnd, win32con.WM_KEYDOWN, 0x31, 0)
time.sleep(0.05)
win32api.SendMessage(chromeHwnd, win32con.WM_KEYUP, 0x31, 0)
问题描述

需要向运行在VirtuaNES上的《超级马里奥》、运行在ePSXe上的《最终幻想》这类模拟器游戏窗口发送按键消息,先后尝试三种发送方式均未生效:

  • 直接向VirtuaNES主窗口句柄发送按键消息
  • 向线程关联的hwndFocus焦点窗口句柄发送按键消息
  • 通过PostThreadMessage直接向窗口所属线程发送按键消息

对应尝试代码如下:

# 向VirtuaNES主Hwnd发送消息
win32api.SendMessage(nesHwnd, win32con.WM_KEYDOWN, 0x31, 0)
time.sleep(0.05)
win32api.SendMessage(nesHwnd, win32con.WM_KEYUP, 0x31, 0)

# 向VirtuaNES hwndFocus发送消息
win32api.SendMessage(hwndFocus, win32con.WM_KEYDOWN, 0x31, 0)
time.sleep(0.05)
win32api.SendMessage(hwndFocus, win32con.WM_KEYUP, 0x31, 0)

# 向VirtuaNES thread id发送消息
win32api.PostThreadMessage(threadId, win32con.WM_KEYDOWN, 0x31, 0)
time.sleep(0.05)
win32api.PostThreadMessage(threadId, win32con.WM_KEYUP, 0x31, 0)
原因说明

SendMessage/PostMessage发送键盘消息失效的核心原因是这类模拟器程序不通过Windows窗口消息队列读取键盘输入:

  • 记事本、Chrome这类基于标准Win32或通用UI框架开发的程序,输入事件由系统转换为WM_KEYDOWN/WM_KEYUP/WM_CHAR类型消息投递到窗口消息队列,程序从队列拉取消息响应输入,因此直接发送对应消息可生效。
  • VirtuaNES、ePSXe这类模拟器为了降低输入延迟,普遍采用DirectInput、Raw Input原始输入甚至直接读取键盘硬件状态的方式获取输入,不会处理窗口消息队列中的模拟按键消息,因此常规发消息的方式完全无效。
可行解决方案

方案按通用性、实现成本从高到低排序:

  1. 首选:SendInput模拟全局键盘输入
    这是微软官方推荐的标准输入模拟接口,生成的输入事件和真实硬件输入的处理路径完全一致,无论程序采用哪种输入读取方式都能识别。使用前需要先将目标模拟器窗口切到前台激活,再调用接口发送对应按键即可。Python环境下可直接使用pywin32库封装的win32api.SendInput接口,也可使用pynput库简化调用逻辑。
    注意:SendInput生成的是全局输入事件,模拟输入期间不要手动操作键盘鼠标,避免输入错乱。
  2. 次选:内存写入模拟按键状态
    针对特定版本的模拟器,可逆向定位到程序存储按键按下状态的内存地址,通过WriteProcessMemory接口直接修改对应内存值实现按键模拟。这种方式不需要窗口保持前台激活,也不会干扰全局输入,但需要针对每个模拟器的不同版本单独寻找内存偏移,通用性差。
  3. 特殊场景:驱动级输入模拟
    通过内核驱动模拟硬件端口的输入信号,兼容性最强但开发成本极高,普通自动化场景完全没有必要采用。
注意事项
  • 不要使用已被微软标记为废弃的keybd_event接口,该接口在新版本Windows系统上存在行为不稳定的问题。
  • 激活目标窗口时,如果窗口处于最小化状态,需要先调用ShowWindow恢复窗口显示,再调用SetForegroundWindow将窗口切到前台,否则SendInput的输入会发送到当前激活的其他窗口。
  • 部分模拟器自带后台输入开关,开启后可支持非激活状态下的输入识别,但该能力属于模拟器自身的配置项,不是通用方案。

内容的提问来源于stack exchange,提问作者Ted

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.30 18:54:28