* fix(windows,engine): 提交键回复等待不再因超时重置会话或丢字 服务端按键处理偶发数百毫秒尖峰时,提交键(空格顶首选 / 确定选词 / 标点顶高亮候选) 的回复等待会走到会话内那条 300ms 同步读;一超时就被判成 TransportUnavailable → DeliveryAmbiguous → Transport 完整重置(丢键 + 吞队列 + 清组合 + 轮换 epoch), 用户看到的是整段已输入内容消失。 - 删除 FANY_IME_COMMIT_REPLY_SESSION_WAIT_MS 那条破坏性兜底读。会话侧改为非阻塞探测, 拿不到回复返回「未就绪」,由 session 自身在 DoEditSession 内交回,沿用同一个 FANY_IME_COMMIT_REPLY_TIMEOUT_MS 分片轮询窗口,不重开窗口、不新造等待原语。 - 交回必须发生在 session 内部:RequestEditSession 用 TF_ES_ASYNCDONTCARE,TSF 异步授权 时调用方读不到 session 的 HRESULT;若靠调用方读返回值,该键会既不丢也不提交, deferred 闸门再不打开(后续按键全部只入队)。也不能改用 TF_ES_SYNC(WinWord 上不成立)。 - 真失败(写失败 / 读失败 / 坏帧 / 字节数不符 / 无 request id / 无管道)仍判 Transport 重置。 - 预取覆盖面扩到 candidate 类 finalize(空格顶首选等),堵住原先只能靠那条 300ms 兜底读的路径。 - 新增按键级延迟诊断 bench_key_latency_diag(build 里有同名 exe 但源码已不在树), 以及两份对照测试:commit_reply_not_ready(回复三态与交回状态机)、 candidate_consistency(同配置下候选集合/顺序/分页逐字一致,引擎侧护栏)。 实测(bench_key_latency_diag):稳态逐键 median 0.6~0.8ms / max 12.5ms,尖峰指向内存 压力下的页回收而非稳态算力。在「结果完全不变」的约束下,预热 / 缓存 / 投机预解码均无效 或反向,故本次不改引擎打分路径。 * fix(windows): 直接标点提交会话补齐未就绪交回,并写准窗口沿用边界 审查 `7aca5e5d` 发现的问题: - `CPunctuationCommitEditSession` 在 `FANY_S_COMMIT_REPLY_NOT_READY` 时只抑制了 token 结算(`applied` / `_FailDeferredKey` 都不走),**没有**像 `CKeyHandlerEditSession` 那样把 请求交回 `_PollCommitReply`。一旦走到这条分支——`_HandleCompositionPunctuation` 的高亮候选 分支要求 `_candidateMode != CANDIDATE_NONE`,而派发该会话的条件是 `_candidateMode == CANDIDATE_NONE`,两者在 session 执行时才求值——deferred token 会永久留在 飞行中,后续所有按键只入队不处理,正是本 PR 在别处修掉的失效模式。现在两处都交回, 并把原因写进注释,避免下一个人再照半个写法抄一遍。 - `_HandOffUnreadyCommitReply` 的两处注释与实现不符或不够具体:交回沿用第一片已经开始的窗口; 只有从未开窗的路径(标点处理函数在派发时高亮候选条件为假、session 执行时条件成立)才在此 开窗,那种情况键一点没等过、`waitedMs` 为 0。另一支(无法保活 → `_FailUnansweredCommitKey`) 只在请求不属于协作轮询时到达,实务上即 `commitReplyMessage == 0`:派发没能用消息窗, 没有可交回的对象。 签名相应扩展:`_RequestDirectPunctuationEditSession` 新增 `commitReplyMessage` / `commitReplyDeadlineTick`,`CPunctuationCommitEditSession` 携带它们。没有行为回退,两个新参数 默认 0 时与改动前一致(而那正是「无消息窗」的既有降级路径)。 验证:windows x64 Release 构建通过,`ctest` 17/17 通过,`clang-format 18.1.8 --dry-run --Werror --style=file` 对本次涉及文件全过。 * fix(windows): 把 _HandOffUnreadyCommitReply 移到公共区,修 CI 编译失败 CI 的 Windows x64 与 Windows Win32 两条腿都挂在同一处编译错误: MetasequoiaIME_AsyncRequests.cpp: error C2248: “CMetasequoiaIME::_HandOffUnreadyCommitReply”: 无法访问 private 成员 上一提交给 CPunctuationCommitEditSession 补「未就绪交回」时调用了这个成员,但它在 private 区。CKeyHandlerEditSession 能调是因为它在 MetasequoiaIME.h 里被显式声明为 friend;而 CPunctuationCommitEditSession 定义在 MetasequoiaIME_AsyncRequests.cpp 的匿名命名空间里 (该文件 31-254 行),头文件里的 friend 声明指不到它,加 friend 也无效。 处置:把该声明移到公共区,与同样由 session 调用的 _FailDeferredKey、_CompleteDeferredKeyReplay、 _HandleCompositionPunctuation 并列,并在注释里写明「它必须公共」的原因,免得下次有人顺手挪回私区。 仅可见性与注释,行为零变化。 验证(triplet 与 CI 一致,且这次看退出码、不截断构建输出): - x64-windows-static:cmake --build build --config Release --parallel 退出码 0; ctest --no-tests=error 17/17。 - x86-windows-static:cmake --build build32 --config Release --parallel 退出码 0; ctest --no-tests=error 17/17。 - clang-format 18.1.8 --dry-run --Werror --style=file 对本次改动文件通过。 另:上一轮「构建通过」是我误报——当时用 Select-String 过滤构建输出又加了 -Last 8,错误行被截掉, 且没有检查 $LASTEXITCODE;ctest 过的是测试目标,与 DLL 编译失败并不冲突。 * fix(windows): 提交回复交回后轮询不再停摆,同步兜底保留有界等待 - 轮询定时器按请求 id 判断是否仍在等待:session 交回时换了新 token, 按旧 token 查会误判为已结算并 KillTimer,交回的请求从此无人再分发。 - 没有轮询可交回的 session(同步 _InvokeKeyHandler 路径)恢复 FANY_IME_COMMIT_REPLY_SESSION_WAIT_MS 的单次有界等待,不再一读不到就丢键重置。 - 确定并输入在提交回复为传输失败时不再继续键入字符并返回 S_OK。 - session 交回占用新槽位时同样受 kMaxPendingAsyncKeys 约束。 - 修正注释:未就绪由 session 自己的完成钩子交回,不是消息处理函数。 - 测试:覆盖轮询分发、等待上限与满表交回;SAL 剥离按最长前缀匹配; 引擎一致性用例③不再清缓存,否则测不到跨输入的缓存泄漏。 --------- Co-authored-by: fanlusky <1730976608@qq.com>
250 lines
18 KiB
Markdown
250 lines
18 KiB
Markdown
# AGENTS.md — windows
|
||
|
||
组织级约定见 [组织 AGENTS.md](https://github.com/metasequoiaime/.github/blob/main/AGENTS.md),仓库地图、组件边界和发布流程见[仓库根 AGENTS.md](../AGENTS.md)。本文件只规定 TSF 前端本身:COM / 焦点 / 线程、注册和本目录的构建。路径都相对于 `windows/`。
|
||
|
||
## 本目录地图
|
||
|
||
| 位置 | 职责 |
|
||
|---|---|
|
||
| `src/IME/` | TSF 文本服务主体、激活/停用、消息窗口、后台 IPC 与主题监听生命周期 |
|
||
| `src/Key/` | `ITfKeyEventSink` 按键预判、异步按键队列与 edit session 分发 |
|
||
| `src/Composition/` | 预编辑缓冲、光标、按键表、preserved key、输入模式与标点/全半角状态 |
|
||
| `src/Edit/` | TSF edit session、文本范围和 composition 写入 |
|
||
| `src/UI/`、`src/Candidate/` | TSF 候选 UI element、候选状态、候选提交与布局通知;可见候选窗主要由 Server 绘制 |
|
||
| `src/IPC/` | Main / ToTsf / Worker / Aux Named Pipe 协议、握手、请求配对和重连状态 |
|
||
| `src/Thread/`、`src/Tf/` | 焦点、context stack、文本布局等 TSF sink |
|
||
| `src/Compartment/`、`src/LanguageBar/` | 中英文、标点、全半角 compartment 与语言栏按钮 |
|
||
| `src/Register/`、`src/DisplayAttribute/` | COM/TSF 注册及预编辑显示属性 |
|
||
| `src/Global/`、`src/Header/`、`src/Utils/` | 全局状态、资源/常量、COM 与 Win32 工具 |
|
||
| `src/DllMain.cpp`、`src/Server.cpp` | DLL 入口、class factory、引用计数和注册导出 |
|
||
| `image/`、`src/IME/MetasequoiaIME.rc` | DLL 内嵌图标与资源 |
|
||
| `scripts/` | 环境生成、Debug/Release 双架构构建、注册和签名脚本 |
|
||
|
||
## 核心输入链路
|
||
|
||
```text
|
||
宿主按键
|
||
→ OnTestKeyDown / _IsKeyEaten(同步决定是否吃键)
|
||
→ OnKeyDown / _DispatchKeyDown
|
||
→ Main Pipe 发给 Server
|
||
→ request_id 对应的回复或异步 Worker Pipe 消息
|
||
→ 消息窗口回到 TSF 所属线程
|
||
→ ITfEditSession 修改 composition / 提交文本
|
||
→ TSF UI element 与 Server 候选窗同步
|
||
```
|
||
|
||
查问题时先沿这条链路定位。按键判定主要看 `src/Key/KeyEventSink.cpp`(延迟按键队列与失败处理在
|
||
`KeyEventSink_DeferredKeys.cpp`,失败分类在 `DeferredKeyFailurePolicy.h`,输入模式热键在
|
||
`KeyEventSink_Hotkeys.cpp`),编辑行为看
|
||
`src/Key/KeyHandler.cpp` 与 `src/Key/KeyStateCategory.cpp`,协议与连接状态看 `src/IPC/Ipc.*`,
|
||
激活、焦点和工作线程生命周期看 `src/IME/MetasequoiaIME*`(Worker Pipe 读线程在
|
||
`MetasequoiaIME_IpcWorker.cpp`,消息窗口与主题监听在 `MetasequoiaIME_WindowProc.cpp`,
|
||
按键路径发起的 edit session 与异步按键请求表在 `MetasequoiaIME_AsyncRequests.cpp`)与 `src/Thread/`。
|
||
|
||
## TSF / COM 硬约定
|
||
|
||
- **Test 与实际处理必须一致**:`OnTestKeyDown` 决定宿主是否还能收到按键。一旦返回“吃掉”,后续
|
||
IPC 超时或处理分支不能再把该键交还宿主。新增按键或模式时,同时检查 fresh composition、普通
|
||
composition、候选态、离线 fallback 和 `_DispatchKeyDown` 的实际分支,避免丢键或重复上屏。
|
||
- **文档修改只在 edit session 内完成**:composition 的开始、更新、提交和删除必须通过
|
||
`ITfContext::RequestEditSession` 获得有效 cookie;后台线程和窗口回调不得直接写 TSF range。
|
||
- **TSF 对象遵守 apartment 与线程归属**:不要把 `ITfContext`、`ITfComposition`、presenter 等 COM
|
||
对象交给后台线程直接调用。跨线程结果通过现有隐藏消息窗口、`PostMessage` 和队列回到 owner
|
||
线程;同步 fallback 只能沿用已有且已验证的路径。
|
||
- **异步回调必须验证所属会话**:延迟按键和 Server 回复可能晚于失焦、composition 结束或 DLL
|
||
停用。应用前核对现有的 focus token / generation、composition epoch、local reset token、
|
||
延迟按键的派发 token(代码里仍叫 `deferredReplayToken`);过期结果应丢弃,不能作用到新会话。
|
||
- **引用与 teardown 成对**:新增 COM 接口、sink、HANDLE、线程、窗口或 presenter 时,在所有失败
|
||
分支与停用路径补齐 `Release`、Unadvise、关闭和 join。不要在析构/停用后留下会访问 `this` 的
|
||
posted message 或 watcher。
|
||
- **`DllMain` 保持最小化**:它处于 loader lock 下,禁止在其中连接管道、启动 Server、创建线程、
|
||
等待锁或做复杂初始化。重活放到文本服务激活后的既有生命周期中。
|
||
- **DLL 会注入第三方宿主进程**:保持静态 CRT(`MultiThreaded[Debug]`)与
|
||
`/Zc:threadSafeInit-` 约定,避免和 QQ、浏览器等宿主的 CRT/TLS 冲突;不要随意增加全局构造器、
|
||
进程级 hook 或修改宿主状态。
|
||
|
||
## IPC 协议——单一契约
|
||
|
||
协议定义的唯一来源是 `../engine/contracts/`,Server 读的是同一份——引擎已经是本仓的目录,两侧由同一个提交钉住,不可能不一致。TSF 仅引用头文件,不链接引擎运行库。这里保留已发布的固定宽度 Win32 ABI:
|
||
|
||
- 修改管道名、opcode、字段、容量或对齐时,在 `../engine/contracts/` 中修改并保留 ABI 检查,两侧的改动落在同一个 PR 里。禁止恢复两份手写定义。
|
||
- 主连接的版本化 ClientHello 必须获得关联 request_id 的 ProtocolReady 后才可发键;协议确认包不能作为候选或上屏文本。新 Server 兼容旧 DLL 的未版本化 hello;新 DLL 对旧或不兼容 Server 使用现有原始输入回退。版本与能力规则见共享 contracts/README.md。
|
||
- `WCHAR` 按 16 位 UTF-16 码元传输;不要用 `wchar_t` 在非 Windows 平台上的大小推断协议布局。
|
||
- 已发布 opcode 只追加、不复用、不重排。未知的较新 Worker opcode 应忽略,不能因此拆除连接。
|
||
- `request_id == 0` 表示 unsolicited,`UINT64_MAX` 表示本地无请求;不得当成普通请求回复配对。
|
||
- `PipeReady` 是反向管道握手帧,只在注册阶段消费;Worker handle 在握手完成前不能发布,Main pipe
|
||
也不能抢先进入可用状态。
|
||
- Server 是候选选择、翻页与配置化导航的权威。TSF 本地影子状态只用于预判,不能在回复
|
||
到达后反向覆盖 Server 已更新的候选状态。
|
||
- `DefinitelyNotSent` 与 `DeliveryAmbiguous` 都不重发:后者可能已被 Server 接收,重发会双写;
|
||
前者也没有回放可走,按传输失败丢键。`TransportUnavailable` 是本地控制结果,绝不能当候选文本提交。
|
||
- 改协议后至少构建 TSF 的 x64/x86,并运行 Server 侧的 IPC 协议测试
|
||
`tests/src/test_ipc_protocol_constants.cpp` 所属测试目标。
|
||
|
||
## 按键失败:不回放
|
||
|
||
被吃掉的键要么被处理,要么被有意吞掉;绝不交还宿主,也绝不处理两次。会话在组合中途出错时
|
||
清掉组合、从空开始,失败那一刻已排队的键一并吞掉(它们是按即将丢弃的组合分类的)。不要
|
||
重新引入按键回放或恢复前缀:回放带来过闪烁、双上屏和重试循环(#259、#404、#427)。
|
||
|
||
失败经 `_FailDeferredKey`(有在飞的键)或 `_ResetSessionAfterFailure`(没有)处理,由
|
||
`DeferredKeyFailurePolicy.h` 决定代价。新增失败点时选对原因走这两个入口,不要单独调用
|
||
`MarkNamedpipeSessionDirty*`——那会漏掉清队列,也会把管道没坏的问题升级成完整重置:
|
||
|
||
| 类别 | 原因 | 动作 |
|
||
|---|---|---|
|
||
| Stale | focus token、组合纪元、reset token 或焦点代已被取代 | 只丢这颗键,不再发起重置 |
|
||
| Resync | 宿主拒绝 edit session(S_FALSE、E_NOTIMPL 等)、RequestEditSession 失败、异步投递失败,以及 OnEndEdit 组合属性检查、文档不匹配等非传输问题 | 丢键、吞队列、本地取消组合;不轮换 token、不关管道,闸门重开时在同一 token 上发 HideCandidateWnd 清 Server 组合 |
|
||
| Transport | 写失败、回复读失败/坏帧、`TransportUnavailable`、`DefinitelyNotSent`、`DeliveryAmbiguous`、提交回复 1500 ms 窗口耗尽、已在等回复的提交键在同一焦点下被组合纪元取代、worker 断开 | 丢键、吞队列、完整重置(新 focus token、重开回复管道) |
|
||
| Offline | 离线原始通道里的宿主失败 | 丢键、吞队列,保留本地组合(此时它是唯一权威) |
|
||
|
||
翻页与高亮移动键(PageUp/PageDown/Up/Down/Tab)的回复只是确认,缺失是软错过,不拆管道、不清
|
||
队列;其余候选键可能上屏,保留「歧义即丢键 + 传输重置」。提交回复的总窗口是
|
||
`FANY_IME_COMMIT_REPLY_TIMEOUT_MS`(1500 ms),但等待跑在宿主 UI 线程上,不能一次阻塞读完:
|
||
消息处理函数用 `_PollCommitReply` 做不阻塞的轮询,没到就把请求放回表里、由
|
||
`TIMER_COMMIT_REPLY_POLL` 唤醒再试,并按请求 id 顺序结算。**会进 edit session 的提交路径
|
||
(确定、数字选词、标点顶高亮候选、空格顶首选)一律先经消息处理函数取回复**、按请求 id 缓存,
|
||
session 直接消费;session 自己那次读(`TryReadCommitReplyFromServerPipe`)只剩「缓存被清掉」
|
||
的竞态,它不阻塞、也不再把超时当传输失败——读不到就返回 `FANY_S_COMMIT_REPLY_NOT_READY`,
|
||
并由 **session 自己在完成钩子里**把请求交回 `_PollCommitReply`(窗口仍是同一个 1500 ms,
|
||
`_HandOffUnreadyCommitReply`)。没有轮询可交回的 session(消息窗口不可用、按键直接走同步
|
||
`_InvokeKeyHandler` 的路径)不能让出,仍保留 `FANY_IME_COMMIT_REPLY_SESSION_WAIT_MS`(300 ms)的
|
||
单次有界等待,超时按传输失败处理;由 `_SessionCommitReplyWaitMs` 按 session 记录的轮询消息判定。
|
||
交回必须发生在 session 侧:`RequestEditSession` 用的是
|
||
`TF_ES_ASYNCDONTCARE`,TSF 可能把 session 排到之后执行,那时消息处理函数早已返回、根本看不到
|
||
这个结果,只有 session 自己的钩子在两种情形下都会跑。新增会上屏的异步路径必须接入同一套轮询,
|
||
不要在 session 里阻塞等回复,不要把「结果要靠调用方从 `_InvokeKeyHandler` 的返回值里读」
|
||
当作契约,也不要让 session 的超时触发会话重置。
|
||
|
||
## 焦点、候选与离线回退
|
||
|
||
- TSF 激活/停用与窗口瞬时失焦不是同一事件。切换到别的输入法使用 `ClientDeactivated`;宿主内部
|
||
的临时焦点变化使用 suspended/reset 流程。Excel 单元格编辑、浏览器和 Windows Text Input Host
|
||
对焦点的处理不同,修改时都要回归。
|
||
- 候选 presenter teardown 时要先隐藏并清除**当前 Server 会话**,同时防止旧 cleanup 消息清掉
|
||
新 composition 的候选;保留现有 session-active 与 async-cleanup 防护。
|
||
- Server 不可用时只能提交 TSF 已确定拥有的本地原始输入。没有候选权威时不要猜测候选文本;服务
|
||
恢复后先完成 focus session 激活/reset,再排空 deferred keys。
|
||
- CN→EN、标点和全半角切换同时涉及 preserved key、TSF compartment、语言栏和 Server 状态。
|
||
composition 中切换模式时须保持“先正确提交,再应用 compartment”的既有顺序,避免宿主双提交。
|
||
- `raw` / `pinyin` / `empty` 预编辑样式定义在 `src/Global/FanyDefines.h`;新增样式时同步检查配置读取、
|
||
caret 映射和 Server 返回的 `Preedit` 消息,不能只改显示字符串。
|
||
|
||
## 资源、注册与日志
|
||
|
||
- CLSID、profile GUID、compartment GUID、语言栏 GUID 与注册表路径属于持久身份。除非明确进行迁移,
|
||
不要重新生成或改写;改动时同时核对 `Register/`、`Globals.*`、`.rc` 与卸载路径。
|
||
- 新增/替换图标时同步资源 ID、`.rc`、`CMakeLists.txt` 和 `LanguageBar` 的索引;浅色/深色图标应成对。
|
||
- 语言配置(profile)图标:安装器把 `image/msime.ico` 装成 DLL 上一级目录的 `MetasequoiaImeTip.ico`,
|
||
`RegisterProfiles` 发现它就注册这个固定路径,否则退回 DLL 内嵌的 `IDIS_METASEQUOIAIME`。不要改回总是
|
||
注册 DLL 路径:版本目录每次升级都变,任务栏会在换目录时取图标失败,改显示「简体」直到资源管理器重启。
|
||
- 安装器把 DLL 装到 `System32\IME\MetasequoiaIME\msime_v*\`(x86 在 `SysWOW64` 下同名目录),不要挪回
|
||
Program Files:CS2 等开启 Trusted Mode 的游戏只放行「位于系统目录且有代码签名」的第三方 in-proc DLL,
|
||
否则输入法在游戏里根本切不出来。用 `linstall.ps1` 从构建目录注册的开发版不满足这一条,游戏内不可用。
|
||
- 语言配置的登记名称不总是 `TEXTSERVICE_DESC`:设置页「游戏兼容」会把它换成 CS2 / Dota 2 白名单里的名字
|
||
(游戏按登记名称决定要不要自己处理输入法消息,不在名单里就弹系统默认组合窗,独占全屏下会把游戏踢出全屏)。
|
||
选择记在 `HKLM\Software\Metasequoia\MetasequoiaIME\GameCompatProfileName`,`RegisterProfiles` 注册时以它为准——
|
||
安装/升级重新注册会覆盖名称,不读它用户的选择就静默丢了。开启时 `ActivateEx` 也不再对这些游戏忽略 UI-less 声明。
|
||
- 生产日志不得记录用户原始按键、预编辑、候选词或提交文本。性能与 IPC 诊断记录耗时、请求 ID、
|
||
token、状态和错误码即可;调试日志也应尽量避免持久化用户输入。
|
||
|
||
## 生成文件与配置真源
|
||
|
||
`python .\scripts\prepare_env.py` 生成本机 `.clangd`,并只更新 `CMakePresets.json` 中的 vcpkg 路径。`CMakeLists.txt` 和 preset 结构直接维护;脚本不会用旧模板覆盖它们。`CMakeUserPresets.json` 中的架构配置继续继承这份 preset。
|
||
|
||
- 不提交生成的新个人绝对路径或 `build*` 等产物。
|
||
- 修改 `.clangd` include 列表时,同时检查模板和生成脚本。
|
||
- CI 在临时合仓目录实际运行环境脚本,验证 CMake 源文件、Release preset 和用户字段不会丢失。
|
||
|
||
## 代码风格
|
||
|
||
- 使用 C++17、Windows Unicode API、SAL 标注和项目现有的 4 空格 Microsoft 风格。
|
||
- 格式化遵循**本目录自己的 `.clang-format`**——仓库没有根目录 `.clang-format`,每个组件子树各有一份、规则互不相同;本目录这份是 Microsoft 风格、明确 `SortIncludes: false`,不要自动重排 include。
|
||
- 修改 `.cpp` / `.h` 后对**本次涉及的文件**运行:
|
||
|
||
```powershell
|
||
clang-format -i .\src\Path\Changed.cpp .\src\Path\Changed.h
|
||
```
|
||
|
||
- COM 出参失败时置空;检查 `HRESULT` / Win32 返回值;所有权不明确时优先沿用项目现有
|
||
`SafeRelease`、RAII 或 teardown 模式,不要混用不兼容的智能指针约定。
|
||
- 不做与任务无关的大范围格式化或命名重构。TSF 状态机对时序敏感,小而可验证的改动优先。
|
||
|
||
注意:新增的代码尽量保持这个风格就可以了,不要去尝试格式化原有的代码。不严格要求新增代码保持这个风格。
|
||
|
||
## 构建与验证
|
||
|
||
环境:Windows、Visual Studio 2026、CMake 3.25+、vcpkg、Python 3.10+;首次准备:
|
||
|
||
```powershell
|
||
python .\scripts\prepare_env.py
|
||
```
|
||
|
||
Debug:
|
||
|
||
```powershell
|
||
.\scripts\lcompile.ps1 64
|
||
.\scripts\lcompile.ps1 32
|
||
```
|
||
|
||
Release:
|
||
|
||
```powershell
|
||
.\scripts\lcompile-release.ps1 64
|
||
.\scripts\lcompile-release.ps1 32
|
||
```
|
||
|
||
本目录的 `windows_ipc_contract` 在 x64/x86 上验证协议布局、版本握手与语音分帧;CI 还构建 Server 和 GUI 框架,并在锁定词库上运行 Server 测试。这些检查不能替代真实 TSF 宿主行为回归。按改动范围做手工回归:
|
||
|
||
- 至少验证 x64 宿主;协议、注册、资源或发布改动同时验证 x86 DLL;
|
||
- 输入首键、连续输入、空格/数字键盘选词、翻页、退格、光标移动和标点提交;
|
||
- composition 中与空 composition 下切换中英文、标点、全半角;
|
||
- 失焦/回焦、切换输入法、Server 未启动/重启/断管恢复;
|
||
- 涉及兼容性时覆盖 Win32 EDIT、Excel、Chromium 系浏览器及 UWP/现代 TSF 宿主。
|
||
|
||
本地注册脚本会执行提权后的 `regsvr32`,属于系统状态修改,不应作为普通验证自动运行。只有用户明确
|
||
要求安装/卸载时才执行:
|
||
|
||
```powershell
|
||
.\scripts\linstall.ps1
|
||
.\scripts\luninstall.ps1
|
||
```
|
||
|
||
签名脚本包含本机 Windows SDK 路径和证书占位符;没有对应证书时不要运行或提交真实 thumbprint。
|
||
|
||
需要生成可安装包做本地测试时,从仓库根目录开始,严格按以下顺序执行。任一步失败都先
|
||
停止并修复,不要继续使用旧产物打包:
|
||
|
||
```powershell
|
||
Set-Location .\windows
|
||
.\scripts\lcompile-release-both.ps1
|
||
|
||
Set-Location ..\server
|
||
.\scripts\lcompile-release.ps1
|
||
|
||
Set-Location ..\installer
|
||
.\Prepare-PackageFiles.ps1 -RepoRoot .. -TsfDirectory windows -ServerDirectory server -NoticesDirectory .
|
||
.\Sign-PackageBinaries-Local.ps1
|
||
.\Compile-Installer.ps1
|
||
```
|
||
|
||
`Prepare-PackageFiles.ps1` 会收集 Server Release、x86/x64 TSF DLL、词库和前端资源;因此必须在两个
|
||
C++ 组件编译完成且相关前端/词库产物已准备好之后运行。`Sign-PackageBinaries-Local.ps1` 使用本机
|
||
测试证书,只用于内部安装验证;它可能创建证书并尝试写入本机受信任存储,不得把本机证书指纹或
|
||
私钥提交到仓库。`Compile-Installer.ps1` 会调用 Inno Setup 命令行编译器 `ISCC.exe` 编译同目录的
|
||
`msime_setup.iss`,安装包输出到该版本目录的 `Output\`。机器上须预先安装 Inno Setup 6.6+;若脚本
|
||
无法自动找到编译器,可显式传入:
|
||
|
||
```powershell
|
||
.\Compile-Installer.ps1 -IsccPath 'C:\Program Files\Inno Setup 7\ISCC.exe'
|
||
```
|
||
|
||
## 提交纪律
|
||
|
||
一次改动经常同时落在 `windows/`、`server/` 和 `engine/`,现在它们在同一次提交里。提交前执行
|
||
`git status --short`,只暂存本任务涉及的显式路径,禁止 `git add -A` / `git add .` 把构建产物或其他
|
||
会话改动卷入提交。
|
||
|
||
提交信息沿用项目现有工程风格,推荐 `type(scope): 摘要`。不要添加 `Co-Authored-By`、
|
||
`Generated with` 或其他 AI 生成标记。
|