fix(single-instance): 修复 Windows 重复启动会开出第二个窗口 - #239
Merged
feigeCode merged 3 commits intoSep 19, 2026
Merged
Conversation
Windows 单实例此前有三层叠加缺陷,任何一层单独存在都足以让「双击两次」得到两个 GUI:
1. 占用判定用错了错误码。claim_or_forward 按 io::ErrorKind::AddrInUse 判断「名称已被
占用」,但 interprocess 首次建实例带 FILE_FLAG_FIRST_PIPE_INSTANCE,名称冲突时 OS
返回 ERROR_ACCESS_DENIED(5),Rust std 把它映射为 PermissionDenied;而全 crate 唯一
的 AddrInUse 处理在 Unix UDS 路径上。于是这个分支在 Windows 上永远不会命中,转发
逻辑一次都没有被执行过。
2. 转发链路在 Windows 上不可用。接收端与发送端都调用 interprocess 的 set_recv_timeout /
set_send_timeout,而它在 Windows 命名管道上恒返回 ErrorKind::Unsupported
("named pipes do not support I/O timeouts")。这是被第 1 条挡住的潜伏缺陷,第 1 条
一修就会立刻暴露出来。
3. 启动门禁把「建不了单实例」解释成「可以再开一个」:main.rs 只有转发成功才 return,
Err 分支只记一条 warn 就继续初始化 GUI。
本次按 tokio 命名管道重写 IPC 层,并收紧启动门禁与 ACK 语义:
- 互斥只由 ServerOptions::first_pipe_instance(true) 提供,且只给首个实例;服务循环补建
的实例一律不带该标志,否则会把自己也挡在门外。
- 占用与连接错误全部按原始 Win32 错误码分类:ERROR_ACCESS_DENIED(5) 表示名称已被占用;
客户端可重试码为 2/5/231(231 在 std 里落到 Uncategorized,按 kind 判会漏掉)。
- 所有 I/O 用 tokio::time::timeout 加时间边界,每条连接独立 task:一个半开连接不会拖住
后续启动请求。接受循环先补建下一个实例、再移交当前连接,名称不会出现无主窗口;循环
异常退出时保持名称被占用并停驻(fail-closed)——宁可让后续启动转发失败后明确报错退出,
也不允许第二个 GUI 进来。
- ACK 收紧为「启动请求已入队」:入队失败回 [0],不再假成功。
- 门禁区分「转发失败」与「监听失败」并给出各自的提示文案,Err 一律提示后 exit(1)。
release 是 windows_subsystem = "windows"、没有控制台,用户看不到 eprintln,提示走
MessageBoxW。
- 顺带修掉一个空转断言:windows_single_instance_gate_precedes_application_creation 里
assert!(!source.contains("continuing startup")) 的字面量本身就在 include_str! 的范围内,
会自己满足自己。现在断言只看 production_source()(截掉测试模块的生产代码),并补上
exit(1) 与提示函数的断言。
依赖侧:main 不再使用 interprocess,从 main 的依赖表中摘除(工作区声明保留,因为
extension-driver 与 extension-host 仍在使用,Cargo.lock 相应少一行)。tokio 增加
io-util / net 两个 feature,二者已由 public_mcp、sftp 启用,不引入重编。
验证:
- cargo test --offline:9 passed。测试放在一个独立 crate 里,用 #[path] 直接引用
main/src/windows_single_instance.rs 本体(不是副本),依赖按 Cargo.lock 精确版本锁定。
其中 5 个是真实 Windows 命名管道传输测试:转发成功且主实例确实收到请求、入队失败时
ACK 必须为拒绝、半个请求超时断开且不阻塞后续请求、4 线程并发抢占只有 1 个取得主实例
资格、不同配置目录各自独立。
- cargo clippy --offline --all-targets -- -D warnings:0 warning。
- rustfmt --edition 2024:windows_single_instance.rs 无格式差异,main.rs 新增片段无差异。
- 原始故障的真机探针复现:第 2 次 create_sync 返回 PermissionDenied(raw 5)、
kind() == AddrInUse 命中为 false、set_recv_timeout 返回 Unsupported。
- 未验证:main crate 本体的编译,以及真机双击验收(第二次启动只唤起已有窗口)。原因:
依赖图含 git 依赖 quickjs-jit,其 rev 需联网获取,而本地构建缓存被清空后正在做全量
编译,尚未跑完;托管 CI 在 Windows 平台会编译 main 本体,可作为该缺口的补充验证。
解决与 PR feigeCode#228(feat(tray): 关闭按钮询问「最小化到托盘 / 退出应用」,并修复 Windows 重复启动)的冲突,让本 PR 重新可合。 - main/src/windows_single_instance.rs:采用 PR 侧重写(SingleInstanceError 枚举、 worker + 决策通道、Claim/ForwardAttempt、create_pipe_instance)。main 侧 bd34cc4 新增的 instance_name_taken 语义已由 PR 的 is_name_in_use(Windows 专用, 认 ERROR_ACCESS_DENIED=5)与 is_connect_retryable(认 2/231/5)覆盖;其被自动合并 保留在非冲突区的定义一并删除,否则留下 3 个 dead_code 警告 - Cargo.lock:按合并后的 Cargo.toml 由 cargo 同步(保留 main 侧新增依赖, 去掉 PR 已移除的 interprocess) - main/src/main.rs、main/Cargo.toml:自动合并无冲突 验证:cargo check -p main --all-targets(0 警告)、cargo test -p main --bin navop (634 passed, 0 failed)。Windows 侧属 CI 覆盖范围(本机无 MSVC 工具链,跨编译 在 libz-sys 处止步)。
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Windows 单实例此前有三层叠加缺陷,任何一层单独存在都足以让「双击两次」得到两个 GUI:
本次按 tokio 命名管道重写 IPC 层,并收紧启动门禁与 ACK 语义:
依赖侧:main 不再使用 interprocess,从 main 的依赖表中摘除(工作区声明保留,因为 extension-driver 与 extension-host 仍在使用,Cargo.lock 相应少一行)。tokio 增加 io-util / net 两个 feature,二者已由 public_mcp、sftp 启用,不引入重编。
验证:
Closes #[issue number]
Description
Describe in English for the changes made in this pull request and the problem it solves.
Please keep 1 PR to solve 1 problem, and keep Small improvements should be small modifications to make PR easier to review and to merge.
Screenshot
Break Changes
Describe any breaking changes introduced by this pull request. If none, remove this section.
How to Test
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce.
Checklist
cargo runfor story tests related to the changes.