傳統 Windows 管道編程需調用 Win32 API (`CreatePipe`, `CreateProcess` 等),繁瑣且易出錯。.NET `System.Diagnostics.Process` 類封裝了標準輸入/輸出/錯誤重定向,配合異步讀取線程,輕鬆實現「啟動子進程 → 捕獲輸出 → 實時顯示在 UI」的管道通信模式。本文演示核心實現模式。
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = scriptPath, // 可執行文件/腳本路徑
WorkingDirectory = workDir, // 工作目錄
CreateNoWindow = true, // 不創建控制台窗口
UseShellExecute = false, // 必須 false 才能重定向
RedirectStandardOutput = true, // 重定向標準輸出
RedirectStandardError = true, // 可選:重定向標準錯誤
// StandardOutputEncoding = Encoding.UTF8, // 指定編碼避免亂碼
}
};
// 同步事件通知讀取完成
var outputEvent = new AutoResetEvent(false);
var outputThread = new Thread(() => ReadOutput(proc, outputEvent));
outputThread.Start();
// 主線程可繼續做其他事,或 WaitHandle.WaitAll 等待
// WaitHandle.WaitAll(new[] { outputEvent }); // 若需同步等待
private void ReadOutput(Process process, AutoResetEvent completedEvent)
{
try
{
// 使用 StreamReader 逐行讀取 (內部有緩衝,比 ReadLine 高效)
using (var reader = process.StandardOutput)
{
string line;
while ((line = reader.ReadLine()) != null)
{
// 跨線程更新 UI 必須 Invoke
txtOutput.Invoke((Action)(() =>
{
// AppendText 比 += 避免閃爍,自動滾動
txtOutput.AppendText(line + Environment.NewLine);
txtOutput.SelectionStart = txtOutput.Text.Length;
txtOutput.ScrollToCaret();
}));
}
}
}
catch (Exception ex)
{
// 記錄異常
}
finally
{
completedEvent.Set(); // 通知主線程讀取結束
}
}
proc.Start();
// 可選:寫入標準輸入 (若 RedirectStandardInput=true)
// proc.StandardInput.WriteLine("input data");
// proc.StandardInput.Close();
// 等待進程退出 (異步讀取線程同時運行)
proc.WaitForExit();
// 確保讀取線程結束
outputEvent.WaitOne();
outputThread.Join(TimeSpan.FromSeconds(1));
Shell 執行模式 (`true`) 由系統 Shell (explorer) 啟動進程,不支持標準流重定向。`false` 直接由 CLR 創建進程並建立匿名管道連接 `stdin/stdout/stderr`。
// 差:每次重新分配字符串、重繪整個控件 → 閃爍、性能差
txtOutput.Text += line + "\r\n";
// 優:原生追加、僅重繪新增部分、自動滾動支持
txtOutput.AppendText(line + "\r\n");
txtOutput.ScrollToCaret();
子進程輸出編碼可能與當前系統 `Encoding.Default` 不一致。建議顯式指定:
StartInfo.StandardOutputEncoding = Encoding.UTF8; // 或 Encoding.GetEncoding("GBK")
StartInfo.StandardErrorEncoding = Encoding.UTF8;
public async Task RunProcessAsync(string fileName, string args, IProgress outputProgress)
{
var tcs = new TaskCompletionSource();
var proc = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = fileName,
Arguments = args,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
StandardOutputEncoding = Encoding.UTF8
},
EnableRaisingEvents = true // 允許 Exited 事件
};
proc.OutputDataReceived += (sender, e) =>
{
if (e.Data != null) outputProgress.Report(e.Data);
else tcs.TrySetResult(proc.ExitCode); // null 表示流關閉
};
proc.ErrorDataReceived += (sender, e) =>
{
if (e.Data != null) outputProgress.Report($"[ERR] {e.Data}");
};
proc.Start();
proc.BeginOutputReadLine(); // 異步事件讀取
proc.BeginErrorReadLine();
await proc.WaitForExitAsync(); // 需擴展方法或 Task.Run(() => proc.WaitForExit())
return proc.ExitCode;
}
// UI 調用
await RunProcessAsync("cmd.exe", "/c dir", new Progress(line =>
txtOutput.AppendText(line + Environment.NewLine)));
.NET 通過 `Process` 類將複雜的管道創建、進程啟動、流重定向封裝為簡潔 API。核心模式:**`UseShellExecute=false` + `RedirectStandardOutput=true` + 異步讀取線程/事件 + UI `Invoke` 更新**。掌握此模式,可輕鬆實現控制台程序封裝、腳本執行器、構建系統輸出捕獲、自動化測試運行器等場景。現代代碼請優先使用 `async/await` + `OutputDataReceived` 事件模式,代碼更清晰、異常處理更完善。