WaitForExit 永久等待。HKLM\Software\Microsoft\PCHealth\ErrorReporting\DoReport=0, ShowUI=0。
public class RunProcess
{
public string Output { get; private set; }
public string Error { get; private set; }
public bool HasError => !string.IsNullOrEmpty(Error);
public void Run(string fileName, string arguments)
{
// 1. 抑制錯誤報告對話框
SuppressErrorReporting();
var outputSb = new StringBuilder();
Error = Output = "";
using (var proc = new Process())
{
proc.StartInfo.FileName = fileName;
proc.StartInfo.Arguments = arguments;
proc.StartInfo.UseShellExecute = false;
proc.StartInfo.RedirectStandardInput = true;
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardError = true;
proc.StartInfo.CreateNoWindow = true;
proc.Start();
// 2. 啟動錯誤讀取執行緒
var errReader = new ReadErrorThread(proc);
// 3. 主執行緒讀取標準輸出 (逐行避免大記憶體)
while (!proc.HasExited)
outputSb.AppendLine(proc.StandardOutput.ReadLine());
outputSb.Append(proc.StandardOutput.ReadToEnd());
// 4. 等待錯誤讀取完成
while (!errReader.HasExisted) Thread.Sleep(1);
Error = errReader.Error;
Output = outputSb.ToString();
}
}
private void SuppressErrorReporting()
{
try
{
using (var key = Registry.LocalMachine.OpenSubKey(
@"Software\Microsoft\PCHealth\ErrorReporting", true))
{
if ((int)key.GetValue("DoReport") != 0) key.SetValue("DoReport", 0);
if ((int)key.GetValue("ShowUI") != 0) key.SetValue("ShowUI", 0);
}
}
catch { /* 忽略權限不足 */ }
}
}
class ReadErrorThread
{
public string Error { get; private set; }
public bool HasExisted { get; private set; }
private readonly Process _proc;
public ReadErrorThread(Process proc) { _proc = proc; new Thread(Run).Start(); }
private void Run()
{
var sb = new StringBuilder();
while (!_proc.HasExited) sb.AppendLine(_proc.StandardError.ReadLine());
sb.Append(_proc.StandardError.ReadToEnd());
Error = sb.ToString();
HasExisted = true;
}
}
var runner = new RunProcess();
runner.Run("myconsole.exe", "arg1 arg2");
if (runner.HasError) Log.Error(runner.Error);
else Console.WriteLine(runner.Output);
// 內建非同步事件,無需手寫執行緒
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = "/c dir",
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
},
EnableRaisingEvents = true
};
proc.OutputDataReceived += (s, e) => { if (e.Data != null) Console.WriteLine(e.Data); };
proc.ErrorDataReceived += (s, e) => { if (e.Data != null) Console.Error.WriteLine(e.Data); };
proc.Start();
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
await proc.WaitForExitAsync(); // .NET 7+ 或自行 Task 包裝
外部進程呼叫核心在於**非同步雙管道讀取**與**錯誤報告抑制**。.NET Framework 時代需手寫執行緒;.NET Core 內建事件模型大幅簡化程式碼。