Files
qiming/qiming-mcp-proxy/docs/analysis/WINDOWS_CREATE_NO_WINDOW_VERIFICATION.md
2026-06-01 13:03:20 +08:00

6.6 KiB
Raw Blame History

Windows CREATE_NO_WINDOW 处理确认报告

检查日期

2026-02-12

检查范围

mcp-proxy 代码库中所有启动子进程的地方


检查结果:全部已正确处理

文件 1: mcp-proxy/src/client/core/command.rs

  • 状态: 已处理
  • 场景: 本地命令模式stdio CLI
  • 实现方式: process-wrap + CreationFlags
#[cfg(windows)]
{
    use process_wrap::CreationFlags;
    // CREATE_NO_WINDOW = 0x08000000
    // 隐藏控制台窗口,避免在 GUI 应用(如 Tauri中显示 CMD 窗口
    wrapped_cmd.wrap(CreationFlags(0x08000000));
    wrapped_cmd.wrap(JobObject);
}

优点:

  • 使用 process-wrap 统一 API
  • 同时使用 JobObject 管理进程树
  • 位置正确(在 CommandWrap::with_new 闭包之后)
  • 跨平台条件编译

文件 2: mcp-sse-proxy/src/server_builder.rs

  • 状态: 已处理
  • 场景: SSE 协议代理启动 MCP 服务子进程
  • 实现方式: process-wrap::tokio + CreationFlags
#[cfg(windows)]
{
    use process_wrap::tokio::CreationFlags;
    // CREATE_NO_WINDOW = 0x08000000
    // 隐藏控制台窗口,避免在 GUI 应用(如 Tauri中显示 CMD 窗口
    wrapped_cmd.wrap(CreationFlags(0x08000000));
    wrapped_cmd.wrap(JobObject);
}

优点:

  • 使用 async 版本的 process-wrap::tokio
  • 同时使用 JobObject 管理进程树
  • 位置正确(在 TokioCommandWrap::with_new 闭包之后)
  • 详细的中英文注释

文件 3: mcp-streamable-proxy/src/server_builder.rs

  • 状态: 已处理v0.1.39 修复)
  • 场景: Streamable HTTP 协议代理启动 MCP 服务子进程
  • 实现方式: 标准库 CommandExt + creation_flags
// Windows: 隐藏控制台窗口,避免在 GUI 应用(如 Tauri中显示 CMD 窗口
// 注意:必须在所有 env/args 配置之后设置,确保不被覆盖
#[cfg(windows)]
{
    use std::os::windows::process::CommandExt;
    // CREATE_NO_WINDOW = 0x08000000
    const CREATE_NO_WINDOW: u32 = 0x08000000;
    cmd.creation_flags(CREATE_NO_WINDOW);
}

优点:

  • 使用标准库 API无需额外依赖
  • 位置正确(在所有 env/args 之后)
  • 明确的警告注释说明顺序重要性
  • 定义为局部常量,代码清晰

修复历史:

  • v0.1.38 之前: CREATE_NO_WINDOW 设置在 env/args 之前(可能失效)
  • v0.1.39: 修复为在所有配置之后设置

代码覆盖率

mcp-proxy 相关子进程启动点

组件 文件 启动场景 Windows 处理
mcp-proxy CLI client/core/command.rs 启动本地 MCP 服务stdio 模式) CreationFlags
mcp-sse-proxy mcp-sse-proxy/src/server_builder.rs 启动 MCP 服务子进程SSE 模式) CreationFlags
mcp-streamable-proxy mcp-streamable-proxy/src/server_builder.rs 启动 MCP 服务子进程HTTP 模式) creation_flags

其他组件(不在检查范围)

组件 说明 是否需要处理
document-parser Python 子进程uv、MinerU ⚠️ 建议检查
voice-cli Python 子进程TTS、Whisper ⚠️ 建议检查

实现模式对比

模式 A: process-wrap + CreationFlags

使用位置: mcp-proxy, mcp-sse-proxy

use process_wrap::tokio::{TokioCommandWrap, CreationFlags, JobObject, KillOnDrop};

let mut wrapped_cmd = TokioCommandWrap::with_new(command, |cmd| {
    cmd.args(&args);
    cmd.envs(&env);
});

#[cfg(windows)]
{
    wrapped_cmd.wrap(CreationFlags(0x08000000));
    wrapped_cmd.wrap(JobObject);
}

wrapped_cmd.wrap(KillOnDrop);
let process = TokioChildProcess::new(wrapped_cmd)?;

优点:

  • 统一的 API 风格
  • 自动进程树管理JobObject
  • 自动清理KillOnDrop

模式 B: 标准库 CommandExt

使用位置: mcp-streamable-proxy

use tokio::process::Command;

let mut cmd = Command::new(command);
cmd.args(&args);
cmd.envs(&env);

#[cfg(windows)]
{
    use std::os::windows::process::CommandExt;
    const CREATE_NO_WINDOW: u32 = 0x08000000;
    cmd.creation_flags(CREATE_NO_WINDOW);
}

let child = cmd.spawn()?;

优点:

  • 无需额外依赖
  • 标准库稳定性
  • 更直接的控制

测试建议

手动测试步骤Windows

  1. 编译最新版本:

    cargo build --release -p mcp-stdio-proxy
    
  2. 测试场景 A - CLI 模式:

    # 启动一个本地 MCP 服务
    mcp-proxy convert <local-mcp-service> --verbose
    

    预期: 无 CMD 窗口弹出

  3. 测试场景 B - Server 模式:

    # 启动 mcp-proxy 服务器
    mcp-proxy server --port 18099
    

    预期: 启动 MCP 服务插件时无 CMD 窗口弹出

  4. 测试场景 C - Tauri 集成:

    • 从 nuwax-agent 启动 mcp-proxy 预期: 无 CMD 窗口(这个需要 nuwax-agent 也正确设置)

自动化测试(建议添加)

#[cfg(all(test, windows))]
mod windows_tests {
    use super::*;
    use std::os::windows::process::CommandExt;
    
    #[tokio::test]
    async fn test_no_window_flag_is_set() {
        // 测试 CREATE_NO_WINDOW 标志是否生效
        // 可以通过检查进程树中是否有 conhost.exe 来验证
    }
}

结论

所有子进程启动点已正确处理

  1. mcp-proxy/client/core/command.rs - 已处理
  2. mcp-sse-proxy/src/server_builder.rs - 已处理
  3. mcp-streamable-proxy/src/server_builder.rs - 已处理v0.1.39

关键要点

  1. CREATE_NO_WINDOW 值: 所有实现都使用 0x08000000
  2. 设置顺序: 所有实现都在 env/args 之后设置
  3. 条件编译: 所有实现都使用 #[cfg(windows)]
  4. 注释完整: 所有实现都包含说明目的的注释

仍需关注

  1. nuwax-agent 侧: 启动 mcp-proxy.cmd 本身时需要设置 CREATE_NO_WINDOW
  2. document-parser: Python 子进程可能需要相同处理
  3. voice-cli: Python 子进程可能需要相同处理
  4. 测试验证: 需要在 Windows 环境实际测试确认效果

版本信息

  • 检查版本: v0.1.39
  • 修复文件: mcp-streamable-proxy/src/server_builder.rs
  • 修复内容: 将 CREATE_NO_WINDOW 移到最后设置
  • 向后兼容: 是(仅影响 Windows 平台行为)

签署

检查人员: Claude (Sonnet 4.5)
检查日期: 2026-02-12
检查方法: 代码审查 + 全局搜索 + Agent 验证
结论: 所有 mcp-proxy 相关子进程启动点已正确处理 Windows CREATE_NO_WINDOW