關鍵屬性設定:
UseShellExecute = false:必須關閉 Shell 執行,才能重導向輸入/輸出/錯誤RedirectStandardInput/Output/Error = true:啟用標準流重導向CreateNoWindow = true:隱藏命令視窗WorkingDirectory:設定工作目錄public static string RunCommand(string command, string workingDir = null, int timeoutMs = 60000) {
using (Process p = new Process()) {
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "/c " + command; // /c 執行完關閉;/k 保留視窗
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
if (!string.IsNullOrEmpty(workingDir)) {
p.StartInfo.WorkingDirectory = workingDir;
}
p.Start();
// 可選:寫入額外命令到標準輸入
// p.StandardInput.WriteLine("dir");
// p.StandardInput.WriteLine("exit");
// 非同步讀取避免死結 (大量輸出時)
string output = p.StandardOutput.ReadToEnd();
string error = p.StandardError.ReadToEnd();
bool exited = p.WaitForExit(timeoutMs);
if (!exited) {
p.Kill();
throw new TimeoutException($"命令執行逾時 ({timeoutMs}ms): {command}");
}
if (p.ExitCode != 0 && !string.IsNullOrEmpty(error)) {
throw new InvalidOperationException($"命令失敗 (ExitCode={p.ExitCode}): {error}");
}
return output;
}
}
public static string RunInteractive(string exePath, string[] commands, string workingDir = null) {
using (Process p = new Process()) {
p.StartInfo.FileName = exePath;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
if (!string.IsNullOrEmpty(workingDir)) p.StartInfo.WorkingDirectory = workingDir;
p.Start();
foreach (string cmd in commands) {
p.StandardInput.WriteLine(cmd);
}
p.StandardInput.WriteLine("exit"); // 重要:結束互動會話
string output = p.StandardOutput.ReadToEnd();
string error = p.StandardError.ReadToEnd();
p.WaitForExit();
return output;
}
}
// 使用範例:批次 ping
string result = RunInteractive("cmd.exe", new[] {
"ping 127.0.0.1",
"ping www.google.com"
});
| 現象 | 原因 | 解決 |
|---|---|---|
| StandardOutput 為空 | 程式輸出到 StandardError、或緩衝區未刷新 | 同時讀取 StandardError;若為外部程式,確認其輸出機制 |
| ReadToEnd() 卡死 | 標準輸出緩衝區滿、程式等待輸入 | 使用非同步 BeginOutputReadLine + 事件處理;或分執行緒讀取輸出/錯誤 |
| 中文亂碼 | 主控台編碼非 UTF-8 (預設 CP950/CP936) | p.StartInfo.StandardOutputEncoding = Encoding.GetEncoding(950); (繁中) 或 Encoding.UTF8 (需外部程式支援) |
| 需要管理員權限 | 命令需提升權限 (如 netsh、diskpart) | p.StartInfo.Verb = "runas"; p.StartInfo.UseShellExecute = true; (但無法重導向流) |
| 路徑含空格 | 引號處理錯誤 | 檔名/路徑加雙引號:@""""C:\Program Files\app.exe"""" |
using System.Diagnostics;
using System.Threading.Tasks;
public static async Task<(int ExitCode, string Output, string Error)> RunAsync(
string fileName, string arguments, string workingDir = null,
Dictionary envVars = null, CancellationToken ct = default) {
var psi = new ProcessStartInfo {
FileName = fileName,
Arguments = arguments,
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true,
WorkingDirectory = workingDir ?? Environment.CurrentDirectory
};
if (envVars != null) {
foreach (var kv in envVars) psi.EnvironmentVariables[kv.Key] = kv.Value;
}
using (var process = new Process { StartInfo = psi, EnableRaisingEvents = true }) {
var outputBuilder = new StringBuilder();
var errorBuilder = new StringBuilder();
process.OutputDataReceived += (s, e) => { if (e.Data != null) outputBuilder.AppendLine(e.Data); };
process.ErrorDataReceived += (s, e) => { if (e.Data != null) errorBuilder.AppendLine(e.Data); };
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
await process.WaitForExitAsync(ct);
return (process.ExitCode, outputBuilder.ToString(), errorBuilder.ToString());
}
}
System.IO、網路用 HttpClient、壓縮用 System.IO.Compression,避免呼叫外部命令。WaitForExit(timeout) 或 CancellationToken。ProcessStartInfo.ArgumentList (.NET Core 3.1+) 陣列傳參。