docs: 输入系统 v0.7 设计与 0A 探测规格

This commit is contained in:
Codex
2026-10-05 01:14:58 +08:00
parent c218c25310
commit 3d08bac1b9
5 changed files with 2445 additions and 0 deletions
@@ -0,0 +1,57 @@
# BUG-20261004-0001
> 适用场景:记录问题本身,不展开完整修复方案。修复内容写入对应的 Fix 文档。
## 基本信息
- ID: BUG-20261004-0001
- 标题: EasyX 原生窗口销毁后宿主事件循环未退出
- 状态:已修复
- 严重性:S2
- 优先级:P1
- 模块: Window / Dialog / EasyX 事件循环 / 输入探测
- 版本 / 分支: `master`,生产修复基线 `c0eca1d`
- 环境: Windows / EasyX / Visual Studio 2022 / Debug x64
- 发现人: 用户与 Codex 协作探测
- 关联 Fix ID: Fix-BUG-20261004-0001
## 问题描述
- 现象: 真实微软拼音探测完成后关闭 EasyX 窗口,窗口从桌面消失,但 `StellarX.UiFixture.exe` 和调用它的人工探测脚本继续运行,脚本无法进入隐私安全摘要阶段。
- 影响范围: `Window::runEventLoop()` 及 `Dialog::Show()` 自建轮询循环;当 EasyX 内部窗口线程完成原生 HWND 销毁,而宿主 `peekmessage` 线程没有收到对应 `WM_CLOSE` 时均可能挂住。
- 期望结果: 原生窗口生命周期结束后,普通事件循环和模态事件循环都应在安全点自行退出,探测脚本继续分析已落盘日志。
- 实际结果: 事件循环仅以宿主侧 `WM_CLOSE` 为退出条件;窗口已经销毁时仍继续轮询。
## 复现信息
- 前置条件: 构建开启 `StellarXInputProbe` 的 `StellarX.UiFixture`,通过 `scripts/run-input-probe-manual.ps1 -ImeName MicrosoftPinyin` 启动。
- 复现步骤:
1. 按人工清单完成微软拼音提交、重复字符、取消、候选选择和中英文切换操作。
2. 关闭 EasyX 原生窗口。
3. 观察窗口消失后脚本仍未输出摘要,fixture 进程仍存在。
- 复现概率:修复前真实人工会话稳定复现
- 最小复现 Demo:`StellarX.UiFixture.exe input-probe <result-dir>`;普通循环和真实 `MessageBox::showModal()` 均可构造关闭回归。
- 证据:
- 会话目录:`TestResults/InputProbe/manual-20261004-194521-908-MicrosoftPinyin`。
- 日志观察到 `WM_CLOSE -> WM_DESTROY -> WM_NCDESTROY`,但宿主 EasyX 消息批次没有取得 `WM_CLOSE`。
- 关闭后原挂起进程 PID 32948 仍存在,结束该进程后脚本才能退出。
## 初步分析
- 疑似位置: `Window::runEventLoop()`、`Dialog::Show()` 与 `Window::WndProcThunk()` 之间的原生窗口生命周期同步。
- 触发条件: EasyX 的 HWND/WndProc 所在线程与调用 `peekmessage` 的宿主线程不同,且原生关闭消息没有映射为宿主侧 `EX_WINDOW/WM_CLOSE`。
- 相关线索:
- 独立 `EasyX.ThreadProbe` 已确认双线程是 EasyX 固有模型,不是 UiFixture 夹具造成。
- 旧事件循环只在 `peekmessage` 返回的消息为 `WM_CLOSE` 时停止。
- HWND 已失效后继续尺寸轮询或绘制还会扩大为失效句柄访问风险。
- 最近相关改动: 阶段 0A 探测器首次把 WndProc 与宿主批次放进同一时间线,暴露了此前被单线程假设遮蔽的生命周期缺口。
## 跟踪信息
- 首次发现时间: 2026-10-04
- 最后更新时间: 2026-10-05
- 修复版本: `c0eca1d`(`fix: 原生窗口销毁后宿主事件循环未退出`)
- 验证版本: Debug x64;单元测试 15/15;普通与模态原生关闭自动回归通过
- 备注: EasyX `EX_SHOWCONSOLE` fixture 由 `Start-Process` 托管时,原生关闭可能返回 `0xC000013A`;该状态与“窗口关闭后进程仍挂住”是不同问题,回归以有界时间内自行退出为核心断言。阶段 0A 诊断实现与线程归属复现见 `c218c25`。