Process.Start() 调用 EXE 时路径错误会导致直接失败;应使用绝对路径或显式设置 WorkingDirectory,启用 UseShellExecute=false 才能重定向输入输出,参数须分离 FileName 和 Arguments,退出需优先发信号而非直接 Kill。

Process.Start() 调用 EXE 时路径不对就直接失败
Windows 下用 Process.Start() 启动外部程序,最常见问题是找不到文件——不是权限问题,是路径解析错了。.NET 默认按当前工作目录(Environment.CurrentDirectory)找 EXE,不是按代码所在目录,也不是按 EXE 相对于项目的位置。
实操建议:
- 绝对路径最稳:
Process.Start(@"C:\Tools\mytool.exe"),注意用 @ 或双反斜杠 - 相对路径必须显式指定工作目录:
Process.Start(new ProcessStartInfo { FileName = "tool.exe", WorkingDirectory = @"C:\MyApp\tools" }) - 别依赖
Application.StartupPath或Assembly.GetExecutingAssembly().Location拼路径——发布成单文件(Single-file)后这些路径会指向临时解压目录,不可靠 - 启动前先用
File.Exists()检查,避免静默失败;捕获Win32Exception(错误码 2 表示“系统找不到指定文件”)
需要读取 EXE 输出?别漏掉 UseShellExecute = false
UseShellExecute 默认为 true,这时你无法重定向标准输出/输入,调用 process.StandardOutput.ReadToEnd() 会直接抛 InvalidOperationException:“此进程未被配置为重定向标准输出”。
实操建议:
- 必须设
UseShellExecute = false才能读写 STDIN/STDOUT/STDERR - 启动后要调用
process.WaitForExit(),否则可能读到空内容(子进程还没写完就结束了) - 如果 EXE 输出量大,用
BeginOutputReadLine()避免阻塞,配合OutputDataReceived事件逐行处理 - 注意编码:有些老工具(如 cmd.exe 下跑的批处理)默认用 GBK,而 .NET 默认 UTF-8,读出来是乱码——可设
process.StartInfo.StandardOutputEncoding = Encoding.GetEncoding("GBK")
传参带空格或特殊字符?参数必须单独拆进 Arguments
把整个命令行字符串(比如 "C:\a b\tool.exe -i \"c:\x y\in.txt\"")直接塞进 FileName,会导致参数解析错乱——空格和引号不会被 shell 解析,而是原样传给 EXE 的 args 数组第一个元素。
实操建议:
-
FileName只放 EXE 路径,所有参数全扔进Arguments字符串(不加引号!) - 路径含空格时,.NET 会自动帮你加引号;你只需写
Arguments = @"-i C:\x y\in.txt",框架内部处理引号转义 - 如果参数本身含双引号(比如 JSON 字符串),用
JsonSerializer.Serialize()或手动转义:两个双引号变成一个"" - 避免拼接字符串传参,尤其来自用户输入——有注入风险(如传入
& calc.exe);必要时用白名单校验参数值
后台长期运行的 EXE 怎么可靠管理?别只靠 Process.Kill()
Process.Kill() 是暴力终止,相当于 Windows 的任务管理器“结束任务”,EXE 来不及清理资源、保存状态、释放句柄,容易留僵尸进程或损坏数据。
实操建议:
- 优先发退出信号:对控制台程序,用
process.StandardInput.WriteLine("exit")或发送 Ctrl+C(process.EnableRaisingEvents = true; process.CloseMainWindow()) - 设置超时:
if (!process.WaitForExit(5000)) { process.Kill(); },避免无限等待 - 用
process.Exited事件监听退出,别轮询HasExited - 记得调用
process.Dispose()或用using语句释放句柄,否则大量短命进程会耗尽系统句柄
真正麻烦的是那些没提供优雅退出机制的老工具,或者启动后自己 fork 出子进程又退出父进程的“假后台”程序——这时候得结合 Process.GetProcessesByName() 或 WMI 查进程树,不能只认最初 Start() 返回的那个 Process 实例。


















