diff --git a/crates/app-iced/src/platform/win32/console.rs b/crates/app-iced/src/platform/win32/console.rs index 0c2f7b8..8a90ee7 100644 --- a/crates/app-iced/src/platform/win32/console.rs +++ b/crates/app-iced/src/platform/win32/console.rs @@ -104,7 +104,8 @@ pub enum ConsoleOutcome { /// 附着后 `(stdout, stderr)`。 after: (isize, isize), }, - /// 没有可写的控制台(④ / 服务 / 计划任务):附着失败**或**附着成功但句柄仍不可用。 + /// 没有可写的控制台(④ 双击,以及其他没有控制台的宿主):附着失败**或**附着成功但 + /// 句柄仍不可用。服务 / 计划任务只是**近似**形态、未亲测,边界见 `packaging/README.md` §15。 /// /// 两种情况在调用点上是同一件事(都没有输出通道),所以合并成一个结论;`last_error` 与 /// 两对句柄一起进诊断文本,读的人能自己分清是哪一种。**没有新建任何窗口**。 diff --git a/packaging/README.md b/packaging/README.md index 79e9666..ea47f35 100644 --- a/packaging/README.md +++ b/packaging/README.md @@ -166,14 +166,20 @@ CUI(3),`grep -rn windows_subsystem crates/` 当时零命中。同一个修复 `.github/workflows/ci.yml` 的 `build` job(PR 阶段就红,不必等到 tag)。 - 抓不住(如实记):只看那 4 个字节,不看 GUI 双击的实际观感——「不再多一个黑框」在本仓库的验证环境 (RDP / 无干净无控制台桌面)只能由 PE 头 + 进程/窗口枚举间接证明,人眼验收归真机。 -- **既无控制台也不重定向的宿主(快捷方式 / 服务 / 计划任务):输出照旧无处可去,退出码不变** - (M196 r2 实测,覆盖 r1 复审 P2-3 提出的「退出码会变成 `EXIT_INTERNAL`(2)」——那条实测不成立)。 - 两种宿主形态各测两条命令,`child_exit_code` 全是 **0**:①父进程有 std 句柄但子进程不继承; - ②父子都没有 std 句柄(服务形状,子进程实测 `stdin/stdout/stderr = 0x0`、`AttachConsole` 返回 0 / - `GetLastError=6`)。原因在 Rust std 的 Windows 实现:句柄取不到时 `Write`/`flush` 直接返回 `Ok` - (独立探针实测 `SetStdHandle(out)` = `0x0` / `0x1234` / `(HANDLE)-1` 三种取值,`write=Ok(())`、 - `flush=Ok(())` 全成立),所以 `crates/cli/src/runner.rs` 那条「写失败 ⇒ `EXIT_INTERNAL`」的分支不会 - 被触发。结论:本 diff **没有**给 CLI 退出码引入可观察差异;真正的差异只有「没控制台就没人看得见输出」 - 这件事,而 CUI 时代在服务里同样是隐形的。想让这种情形有专属退出码/文案,得在 CLI 侧显式判一次能力 - (`platform::win32::console` 的 `output_visible()` 就是现成的判据),那在 `crates/cli/**`、不在 M196 的 - scope 内——这里只如实记录形态,不改结构。 +- **既无控制台也不重定向的宿主:输出照旧无处可去,退出码不变**(M196 r2 实测,反驳 r1 复审 P2-3 的 + 「退出码会变成 `EXIT_INTERNAL`(2)」——那条是**纯推理、无实测**)。两个**近似**形态 × 两条命令, + `child_exit_code` 全是 **0**:① 父进程有 std 句柄、子进程不继承(`dwFlags=0` + `bInheritHandles=FALSE`); + ② 父进程 `FreeConsole()` 后把 `in/out/err` 三个 std 句柄全置 0,再起子进程(子进程侧实测 + `stdin/stdout/stderr = 0x0`、`AttachConsole` 返回 0 / `GetLastError=6`)。原因在 Rust std 的 Windows + 实现:句柄取不到时 `Write`/`flush` 直接返回 `Ok`(独立探针实测 `SetStdHandle(out)` = `0x0` / `0x1234` / + `(HANDLE)-1` 三种取值,`write=Ok(())`、`flush=Ok(())` 全成立),所以 `crates/cli/src/runner.rs` 那条 + 「写失败 ⇒ `EXIT_INTERNAL`」的分支不会被触发。结论:本 diff **没有**给 CLI 退出码引入可观察差异; + 真正的差异只有「没控制台就没人看得见输出」这件事本身。 + **边界(别把上面读成「真服务也测过」)**:①② 都只是**近似**形态——真 SCM 服务与计划任务(session 0、 + no window station)、`start /b`、Windows Terminal / ConPTY 全部**未测**;对它们的结论只能靠「句柄无效 + 时 std 写返回 `Ok`」这条机制外推,不是在那些形态里亲测。第三方交叉(**复审的读数,不是我跑的**): + r1 复审在 r2 用更干净的形态(父 `FreeConsole()` + 三句柄置 0 + 子进程 + `DETACHED_PROCESS | dwFlags=0 | bInheritHandles=FALSE`)独立复现,产品二进制 `--version` / + `instances list` / `--help` 全 0(`logs list` 是 1,但重定向跑同一条也是 1 ⇒ 用户错误,不是写失败)。 + 想让这种情形有专属退出码/文案,得在 CLI 侧显式判一次能力(`platform::win32::console` 的 + `output_visible()` 就是现成判据),那在 `crates/cli/**`、不在 M196 的 scope 内——这里只如实记录形态。