修复 Windows 下 resident 进程继承管道句柄导致的挂起(#207)#210
Closed
hqy2435662352 wants to merge 1 commit into
Closed
Conversation
hqy2435662352
force-pushed
the
fix-windows-resident-hang
branch
2 times, most recently
from
July 14, 2026 08:30
c402903 to
9e6c534
Compare
On Windows, spawn the resident with UseShellExecute=true so it does not inherit the caller's stdout/stderr pipe handles. This eliminates the ~60s (or ~12min) hang seen when officecli is invoked through PowerShell or any shell whose stdout is a pipe. The idle timeout is now passed via --idle-seconds because ProcessStartInfo.Environment cannot be used with UseShellExecute=true. Unix keeps the existing UseShellExecute=false + redirected streams behavior.
hqy2435662352
force-pushed
the
fix-windows-resident-hang
branch
from
July 14, 2026 08:49
9e6c534 to
a75e29b
Compare
Contributor
|
@hqy2435662352 感谢反馈和这么完整的复现脚本! 我们定位下来:这个卡顿很可能是 Windows 上某些第三方软件(例如安全/杀毒类) 在进程启动时对管道句柄留了额外引用,导致后台常驻进程被动持有了你命令行的 stdout 管道,一直到它空闲退出(约 60s)才释放——所以你会看到命令输出早就回来了、进程却迟迟不退。 我们没有继续用之前的环境变量绕过,而是换了一种方式从根上处理:让后台进程启动时只继承它自己需要的句柄,这样任何第三方软件留下的多余句柄都不会再泄漏进去。改动已合入 main,会在下个版本发布。 麻烦你更新到带此修复的版本后再测一下是否恢复正常;如果还卡,顺便告诉我们你装的是哪款安全软件,方便进一步确认。再次感谢! |
Author
|
@goworm 感谢处理,经测试 v1.0.137(commit 991bea6)中通过 PROC_THREAD_ATTRIBUTE_HANDLE_LIST 句柄白名单的方式已经修复了该PR指向的问题:resident 子进程只继承自身需要的 std 句柄,不再继承调用方多余的管道句柄,从而避免了 60 秒挂起。 我已从最新 main 分支构建 v1.0.137 并在 Windows 11 上验证:
因此本 PR 不再需要合并,关闭。 |
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.
摘要
修复 #207。
在 Windows 上,当使用
UseShellExecute=false启动自动 resident 子进程时,子进程会继承调用方的 stdout/stderr 管道句柄。当通过 PowerShell(或任何将 stdout 重定向为管道的 shell,例如 CI agent / SDK 包装器)调用 officecli 时,管道会一直保持打开状态,直到 resident 的空闲超时到期(约 60 秒),因此每条命令看起来都会卡住一分钟,即使实际工作早已完成。根因
.NET 的
ProcessStartInfo在UseShellExecute=false时会调用 Win32CreateProcess并传入bInheritHandles=TRUE,导致 resident 子进程继承父进程的控制台/管道句柄。之前尝试的SetHandleInformation方案只能标记父进程自身句柄表的继承标志,但运行时仍会为子进程创建可继承的句柄副本,因此调用方的 stdout 管道仍然保持打开。修复方案
UseShellExecute=true启动 resident,通过 shell 启动不会继承句柄。由于UseShellExecute=true时不能使用ProcessStartInfo.Environment,空闲超时改为通过命令行参数--idle-seconds传递。UseShellExecute=false+RedirectStandardOutput/Error=true),该方案已经能阻止调用方管道被继承。EscapeWindowsArgument辅助函数,确保带空格、引号或尾部反斜杠的文件路径能被CommandLineToArgvW正确解析。ResidentServer构造函数新增可选的initialIdleTimeout参数,隐藏命令__resident-serve__新增--idle-seconds选项。环境变量OFFICECLI_RESIDENT_IDLE_SECONDS仍作为测试/CI 的后备方案被保留。OFFICECLI_NO_AUTO_RESIDENT=1回退或手动启动 resident;同时将完整异常写入可选的 CLI 日志(officecli config log true)以便运维诊断。改动文件
src/officecli/CommandBuilder.cssrc/officecli/ResidentServer.cs审计结果
在仓库中搜索了
ProcessStartInfo.Environment、RedirectStandardOutput/RedirectStandardError、Process.StandardError/Output、OFFICECLI_RESIDENT_IDLE_SECONDS、new ResidentServer等关键字,确认主要影响范围如下:ProcessStartInfo.Environment的写入点仅在CommandBuilder.TryStartResidentProcess中;PR 已将该处对OFFICECLI_RESIDENT_IDLE_SECONDS的注入改为命令行参数传递。Process.StandardError.ReadToEnd()的读取已在 PR 中改为仅在startInfo.RedirectStandardError为 true 时才执行,避免UseShellExecute=true下抛异常。未发现仓库中其它依赖子进程 stdout/stderr 的代码路径。ResidentServer仍从OFFICECLI_RESIDENT_IDLE_SECONDS环境变量读取作为后备;其它日志/刷新相关变量未受此次更改破坏。空闲超时优先级
ResidentServer.ResolveIdleTimeout的解析顺序已在代码注释中明确:__resident-serve__ --idle-seconds命令行参数(Windows 生产路径)OFFICECLI_RESIDENT_IDLE_SECONDS环境变量(测试/CI 后备)验证
所有测试均在 Windows 11 上针对从源码构建的
officecli.exe执行。复现
修复前,以下每条命令都会因为 PowerShell 等待被继承的管道关闭而耗时约 62 秒:
修复后,每条自动 resident 命令均在约 1 秒内完成:
边界测试
针对
EscapeWindowsArgument和 Windows 启动路径进行了额外验证:__resident-serve__并设置OFFICECLI_RESIDENT_IDLE_SECONDS=3:命令正常,resident 3.0 s 后退出__resident-serve__并传入--idle-seconds 3:命令正常,resident 3.0 s 后退出这些测试覆盖了
EscapeWindowsArgument对空格、单引号等常见特殊字符的处理;对于尾部反斜杠、内嵌双引号、制表符、极长路径、特殊 Unicode 等更复杂的 Windows 路径场景,建议后续在 CI 中补充 round-trip 测试(构造命令行 ->CommandLineToArgvW解析 -> 验证与原 args 一致)。启动失败回退提示
Windows 启动失败时的错误信息现在包含:
完整异常会同时写入 CLI 日志(启用后),便于在受限/沙箱环境中定位问题。
构建
dotnet build src\officecli\officecli.csproj -c Release构建成功,0 错误(1 个与本次改动无关的预先存在的空引用警告)。
已知限制与后续建议(非阻塞)
UseShellExecute=true被阻止时会给用户明确回退提示并记录日志,但不会自动回退到旧方案。若后续收到相关反馈,可考虑增加更智能的回退策略。CliLogger.LogError写入可选日志文件作为替代诊断通道。tests/目录不存在),本次 PR 未包含自动化测试。强烈建议维护者在建立测试基础设施后,尽快为EscapeWindowsArgument添加CommandLineToArgvWround-trip 测试,覆盖空串、空格、制表符、内嵌引号、尾部多重反斜杠、极长路径、非 BMP Unicode 等边界。