process-api-net11
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseProcess API Improvements — .NET 11
Process API 改进 — .NET 11
New APIs added to in .NET 11 simplify process management, eliminate boilerplate, and prevent common deadlock patterns when capturing output in .NET 11 or later applications.
System.Diagnostics.Process.NET 11 中为 新增的 API 简化了进程管理,减少了样板代码,并可防止在 .NET 11 或更高版本应用中捕获输出时常见的死锁模式。
System.Diagnostics.ProcessWhen to Use
使用时机
- Running or orchestrating external processes in a .NET 11 (or later) project.
- Needing to start a process, wait for it to exit, and capture its output/error streams without risking deadlocks ().
Process.RunAndCaptureText[Async] - Wanting to ensure child processes are automatically terminated when the parent process exits ().
KillOnParentExit - Requiring lightweight, low-overhead process creation via on supported platforms.
SafeProcessHandle - Requiring fine-grained control over handle inheritance () or starting detached processes (
InheritedHandles).StartDetached
- 在 .NET 11(或更高版本)项目中运行或编排外部进程。
- 需要启动进程、等待其退出并捕获其输出和错误流,同时避免死锁风险()。
Process.RunAndCaptureText[Async] - 希望确保父进程退出时自动终止子进程()。
KillOnParentExit - 需要在受支持的平台上通过 进行轻量级、低开销的进程创建。
SafeProcessHandle - 需要精确控制句柄继承()或启动分离进程(
InheritedHandles)。StartDetached
When Not to Use
不适用场景
- The project targets .NET 10 or earlier — these APIs are not available before .NET 11.
- The default is sufficient and does not require output capturing or advanced lifecycle rules.
Process.Start()
- 项目面向 .NET 10 或更早版本——这些 API 在 .NET 11 之前不可用。
- 默认的 已经足够,且不需要捕获输出或使用高级生命周期规则。
Process.Start()
Target Framework
目标框架
xml
<TargetFramework>net11.0</TargetFramework>xml
<TargetFramework>net11.0</TargetFramework>New APIs
新 API
Types
类型
Before using the new convenience methods, note the following types. Use the framework types directly:
| Type | Kind | Read-only properties |
|---|---|---|
| Sealed class | |
| Sealed class | |
| Readonly struct | |
ProcessExitStatusExitCodeSuccessProcessTextOutputProcessOutputLine使用新的便捷方法之前,请注意以下类型。直接使用框架提供的类型:
| 类型 | 类别 | 只读属性 |
|---|---|---|
| 密封类 | |
| 密封类 | |
| 只读结构体 | |
ProcessExitStatusExitCodeSuccessProcessTextOutputProcessOutputLineHigh-Level Convenience APIs
高级便捷 API
Static Methods
静态方法
The overloads below require , which is the default for .NET.
ProcessStartInfoUseShellExecute = false以下 重载要求 ,这是 .NET 的默认值。
ProcessStartInfoUseShellExecute = falseProcess.Run
/ Process.RunAsync
Process.RunProcess.RunAsyncProcess.Run
/ Process.RunAsync
Process.RunProcess.RunAsyncStarts a process and waits for it to exit, returning the exit status. Does not capture standard output or error. Passing discards standard output and error by internally redirecting standard handles to the device. On timeout or cancellation, the process is killed.
silent: trueNULcsharp
public static ProcessExitStatus Run(string fileName, IEnumerable<string>? arguments = null, bool silent = false, TimeSpan? timeout = null)
public static Task<ProcessExitStatus> RunAsync(string fileName, IEnumerable<string>? arguments = null, bool silent = false, CancellationToken cancellationToken = default)
public static ProcessExitStatus Run(ProcessStartInfo startInfo, TimeSpan? timeout = null)
public static Task<ProcessExitStatus> RunAsync(ProcessStartInfo startInfo, CancellationToken cancellationToken = default)启动进程并等待其退出,返回退出状态。不捕获标准输出或错误输出。传入 时,会在内部将标准句柄重定向到 设备,从而丢弃标准输出和错误输出。发生超时或取消时,进程会被终止。
silent: trueNULcsharp
public static ProcessExitStatus Run(string fileName, IEnumerable<string>? arguments = null, bool silent = false, TimeSpan? timeout = null)
public static Task<ProcessExitStatus> RunAsync(string fileName, IEnumerable<string>? arguments = null, bool silent = false, CancellationToken cancellationToken = default)
public static ProcessExitStatus Run(ProcessStartInfo startInfo, TimeSpan? timeout = null)
public static Task<ProcessExitStatus> RunAsync(ProcessStartInfo startInfo, CancellationToken cancellationToken = default)Process.RunAndCaptureText
/ Process.RunAndCaptureTextAsync
Process.RunAndCaptureTextProcess.RunAndCaptureTextAsyncProcess.RunAndCaptureText
/ Process.RunAndCaptureTextAsync
Process.RunAndCaptureTextProcess.RunAndCaptureTextAsyncStarts a process, captures both standard output and error, and waits for it to exit. Extremely useful for avoiding deadlocks on stream redirection. On timeout or cancellation, the process is killed.
When using the overloads, the caller is responsible for setting and on (because BCL APIs cannot modify the input arguments they were given).
ProcessStartInfoRedirectStandardOutput = trueRedirectStandardError = trueProcessStartInfocsharp
public static ProcessTextOutput RunAndCaptureText(string fileName, IEnumerable<string>? arguments = null, TimeSpan? timeout = null)
public static Task<ProcessTextOutput> RunAndCaptureTextAsync(string fileName, IEnumerable<string>? arguments = null, CancellationToken cancellationToken = default)
public static ProcessTextOutput RunAndCaptureText(ProcessStartInfo startInfo, TimeSpan? timeout = null)
public static Task<ProcessTextOutput> RunAndCaptureTextAsync(ProcessStartInfo startInfo, CancellationToken cancellationToken = default)启动进程、同时捕获标准输出和错误输出,并等待其退出。对于避免流重定向导致的死锁极为有用。发生超时或取消时,进程会被终止。
使用 重载时,调用方负责在 上设置 和 (因为 BCL API 无法修改传入的参数)。
ProcessStartInfoProcessStartInfoRedirectStandardOutput = trueRedirectStandardError = truecsharp
public static ProcessTextOutput RunAndCaptureText(string fileName, IEnumerable<string>? arguments = null, TimeSpan? timeout = null)
public static Task<ProcessTextOutput> RunAndCaptureTextAsync(string fileName, IEnumerable<string>? arguments = null, CancellationToken cancellationToken = default)
public static ProcessTextOutput RunAndCaptureText(ProcessStartInfo startInfo, TimeSpan? timeout = null)
public static Task<ProcessTextOutput> RunAndCaptureTextAsync(ProcessStartInfo startInfo, CancellationToken cancellationToken = default)Process.StartAndForget
Process.StartAndForgetProcess.StartAndForget
Process.StartAndForgetThere is a common misconception that when a process is disposed, it's also being killed. This is not the case, as only releases the resources associated with the process, but does not kill it.
Process.DisposeTo make it easier to start a process without the need to worry about disposing it, was introduced. The method starts an executable, returns its ID, and immediately releases all handle resources associated with it. Standard handles not supplied through , , or go to the null device by default.
Process.StartAndForgetStandardInputHandleStandardOutputHandleStandardErrorHandleStartAndForgetInvalidOperationExceptionUseShellExecuteRedirectStandardInputRedirectStandardOutputRedirectStandardErrorcsharp
public static int StartAndForget(string fileName, IEnumerable<string>? arguments = null)
public static int StartAndForget(ProcessStartInfo startInfo)一种常见误解是,释放进程对象时也会将其终止。实际上并非如此,因为 只会释放与进程关联的资源,而不会终止进程。
Process.Dispose为了更容易地在无需考虑释放进程的情况下启动进程,引入了 。该方法启动可执行文件,返回其 ID,并立即释放与其关联的所有句柄资源。未通过 、 或 提供的标准句柄默认会转到 null 设备。
Process.StartAndForgetStandardInputHandleStandardOutputHandleStandardErrorHandle如果 或任意 、、 标志为 true, 将抛出 。如果需要读取输出,请使用捕获或流式 API。
UseShellExecuteRedirectStandardInputRedirectStandardOutputRedirectStandardErrorStartAndForgetInvalidOperationExceptioncsharp
public static int StartAndForget(string fileName, IEnumerable<string>? arguments = null)
public static int StartAndForget(ProcessStartInfo startInfo)Instance Methods
实例方法
These methods are called on a instance to directly read stdout and stderr, guaranteeing no OS pipe buffer overflow deadlocks.
ProcessNote: Calling these methods requires and on the passed to .
RedirectStandardOutput = trueRedirectStandardError = trueProcessStartInfoProcess.Startcsharp
public (string StandardOutput, string StandardError) ReadAllText(TimeSpan? timeout = null)
public Task<(string StandardOutput, string StandardError)> ReadAllTextAsync(CancellationToken cancellationToken = default)
public (byte[] StandardOutput, byte[] StandardError) ReadAllBytes(TimeSpan? timeout = null)
public Task<(byte[] StandardOutput, byte[] StandardError)> ReadAllBytesAsync(CancellationToken cancellationToken = default)
public IEnumerable<ProcessOutputLine> ReadAllLines(TimeSpan? timeout = null)
public IAsyncEnumerable<ProcessOutputLine> ReadAllLinesAsync(CancellationToken cancellationToken = default)Example of deconstructing the tuple return:
csharp
var (stdout, stderr) = await process.ReadAllTextAsync();这些方法在 实例上调用,用于直接读取 stdout 和 stderr,并保证不会因操作系统管道缓冲区溢出而死锁。
Process注意:调用这些方法时,必须在传给 的 上设置 和 。
Process.StartProcessStartInfoRedirectStandardOutput = trueRedirectStandardError = truecsharp
public (string StandardOutput, string StandardError) ReadAllText(TimeSpan? timeout = null)
public Task<(string StandardOutput, string StandardError)> ReadAllTextAsync(CancellationToken cancellationToken = default)
public (byte[] StandardOutput, byte[] StandardError) ReadAllBytes(TimeSpan? timeout = null)
public Task<(byte[] StandardOutput, byte[] StandardError)> ReadAllBytesAsync(CancellationToken cancellationToken = default)
public IEnumerable<ProcessOutputLine> ReadAllLines(TimeSpan? timeout = null)
public IAsyncEnumerable<ProcessOutputLine> ReadAllLinesAsync(CancellationToken cancellationToken = default)解构元组返回值的示例:
csharp
var (stdout, stderr) = await process.ReadAllTextAsync();ProcessStartInfo Properties
ProcessStartInfo 属性
KillOnParentExit
KillOnParentExitKillOnParentExit
KillOnParentExitEnsures that the spawned child process is terminated when the current (parent) process exits (including fatal crash and being force killed). Works across Windows, Linux, and Android.
csharp
public bool KillOnParentExit { get; set; }In cross-platform code, guard the property assignment with a supported-platform check, not only the assigned value. If automatic teardown is required, fail explicitly on an unsupported platform instead of starting an unprotected child process.
requires .
KillOnParentExitUseShellExecute = false确保生成的子进程在当前(父)进程退出时被终止(包括致命崩溃和被强制终止)。支持 Windows、Linux 和 Android。
csharp
public bool KillOnParentExit { get; set; }在跨平台代码中,应通过受支持的平台检查来保护属性赋值,而不能只检查所赋的值。如果需要自动清理,则应在不支持的平台上显式失败,而不是启动一个未受保护的子进程。
要求 。
KillOnParentExitUseShellExecute = falseInheritedHandles
InheritedHandlesInheritedHandles
InheritedHandlesProvides precise control over which handles (file descriptors) are inherited by the child process, preventing accidental resource leaks.
- Standard handles (,
stdin,stdout) are always included (no need to add them to the list).stderr - Setting the list to an empty list means only standard handles get inherited.
- Only and
SafeFileHandleinstances are allowed as of today.SafePipeHandle - No global lock is used when spawning new processes on Windows (important for tuning projects that spawn multiple processes in parallel).
- Concurrent process starts must not pass the same handle in : the runtime temporarily changes its inheritance flags. Serialize starts that share a handle, or use separate handles for each concurrent start.
InheritedHandles - Do not enable inheritance on the handles before passing them; other process-start APIs could then inherit them unintentionally.
- On Unix systems without native handle-inheritance control, setting this property can severely reduce process-start performance.
- A non-null list requires and cannot be combined with a non-empty
UseShellExecute = false.UserName
csharp
public IList<SafeHandle>? InheritedHandles { get; set; }精确控制由子进程继承哪些句柄(文件描述符),防止意外的资源泄漏。
- 标准句柄(、
stdin、stdout)始终会被继承(无需将其添加到列表中)。stderr - 将列表设置为空列表表示仅继承标准句柄。
- 目前只允许使用 和
SafeFileHandle实例。SafePipeHandle - 在 Windows 上启动新进程时不会使用全局锁(这对于调优并行启动多个进程的项目非常重要)。
- 并发启动进程时,不得在 中传入同一个句柄:运行时会临时更改其继承标志。对于共享句柄的启动操作,应进行串行化,或者为每次并发启动使用单独的句柄。
InheritedHandles - 在传入句柄之前,不要先启用句柄的继承;否则,其他进程启动 API 可能会在无意中继承这些句柄。
- 在不原生支持句柄继承控制的 Unix 系统上,设置此属性可能会显著降低进程启动性能。
- 非 null 列表要求 ,并且不能与非空的
UseShellExecute = false组合使用。UserName
csharp
public IList<SafeHandle>? InheritedHandles { get; set; }StartDetached
StartDetachedStartDetached
StartDetachedStarts the process detached from the parent's terminal or job session, ensuring it survives the parent's exit.
csharp
public bool StartDetached { get; set; }StartDetachedUseShellExecute = false以脱离父进程终端或作业会话的方式启动进程,确保其在父进程退出后仍能继续运行。
csharp
public bool StartDetached { get; set; }StartDetachedUseShellExecute = falseExamples
示例
1. One-Line Run and Capture Output
1. 一行代码运行并捕获输出
Run a process and safely read all output text without stream deadlock risks. For simple CLI operations that don't need cancellation or async scalability, prefer the synchronous overload:
csharp
using System;
using System.Diagnostics;
// Run 'git status' and capture output
ProcessTextOutput result = Process.RunAndCaptureText("git", ["status"]);
if (result.ExitStatus.ExitCode == 0)
{
Console.WriteLine($"Git Output: {result.StandardOutput}");
}
else
{
Console.WriteLine($"Failed with exit code: {result.ExitStatus.ExitCode}");
Console.WriteLine($"Error: {result.StandardError}");
}运行进程并安全地读取所有输出文本,不会产生流死锁风险。对于不需要取消或异步扩展能力的简单 CLI 操作,优先使用同步重载:
csharp
using System;
using System.Diagnostics;
// Run 'git status' and capture output
ProcessTextOutput result = Process.RunAndCaptureText("git", ["status"]);
if (result.ExitStatus.ExitCode == 0)
{
Console.WriteLine($"Git Output: {result.StandardOutput}");
}
else
{
Console.WriteLine($"Failed with exit code: {result.ExitStatus.ExitCode}");
Console.WriteLine($"Error: {result.StandardError}");
}2. Auto-Killing Child Processes on Parent Exit
2. 父进程退出时自动终止子进程
On Windows, Linux, and Android, ensure a long-running background worker process is killed when the main application terminates. On other platforms, this example stops before starting the child process:
csharp
using System;
using System.Diagnostics;
ProcessStartInfo startInfo = new("dotnet", ["run", "--project", "BackgroundWorker.csproj"]);
if (OperatingSystem.IsWindows() || OperatingSystem.IsLinux() || OperatingSystem.IsAndroid())
{
startInfo.KillOnParentExit = true;
}
else
{
throw new PlatformNotSupportedException("This example requires Windows, Linux, or Android.");
}
using Process process = Process.Start(startInfo)!;
// The background worker is now tied to this process's lifecycle在 Windows、Linux 和 Android 上,确保长时间运行的后台工作进程在主应用终止时被终止。在其他平台上,此示例会在启动子进程之前停止执行:
csharp
using System;
using System.Diagnostics;
ProcessStartInfo startInfo = new("dotnet", ["run", "--project", "BackgroundWorker.csproj"]);
if (OperatingSystem.IsWindows() || OperatingSystem.IsLinux() || OperatingSystem.IsAndroid())
{
startInfo.KillOnParentExit = true;
}
else
{
throw new PlatformNotSupportedException("This example requires Windows, Linux, or Android.");
}
using Process process = Process.Start(startInfo)!;
// The background worker is now tied to this process's lifecycle3. Read All Lines From Output
3. 读取所有输出行
Start a process and read its output lines safely:
csharp
using System;
using System.Diagnostics;
using System.Threading.Tasks;
ProcessStartInfo startInfo = new("ping", ["127.0.0.1"])
{
RedirectStandardOutput = true,
RedirectStandardError = true
};
using Process process = Process.Start(startInfo)!;
// Read all output lines safely and asynchronously
await foreach (ProcessOutputLine line in process.ReadAllLinesAsync())
{
string prefix = line.StandardError ? "[Err]" : "[Out]";
Console.WriteLine($"{prefix} > {line.Content}");
}启动进程并安全地读取其输出行:
csharp
using System;
using System.Diagnostics;
using System.Threading.Tasks;
ProcessStartInfo startInfo = new("ping", ["127.0.0.1"])
{
RedirectStandardOutput = true,
RedirectStandardError = true
};
using Process process = Process.Start(startInfo)!;
// Read all output lines safely and asynchronously
await foreach (ProcessOutputLine line in process.ReadAllLinesAsync())
{
string prefix = line.StandardError ? "[Err]" : "[Out]";
Console.WriteLine($"{prefix} > {line.Content}");
}4. Read Entire Output Deadlock-Free (Tuple Return)
4. 无死锁地读取全部输出(元组返回)
Start a process with custom and read both stdout and stderr into a deconstructed tuple without buffer deadlocks:
ProcessStartInfocsharp
using System;
using System.Diagnostics;
using System.Threading.Tasks;
ProcessStartInfo startInfo = new("git", ["diff"])
{
RedirectStandardOutput = true,
RedirectStandardError = true
};
using Process process = Process.Start(startInfo)!;
var (stdout, stderr) = await process.ReadAllTextAsync();
Console.WriteLine($"Diff output: {stdout}");使用自定义 启动进程,并将 stdout 和 stderr 读取到可解构的元组中,避免缓冲区死锁:
ProcessStartInfocsharp
using System;
using System.Diagnostics;
using System.Threading.Tasks;
ProcessStartInfo startInfo = new("git", ["diff"])
{
RedirectStandardOutput = true,
RedirectStandardError = true
};
using Process process = Process.Start(startInfo)!;
var (stdout, stderr) = await process.ReadAllTextAsync();
Console.WriteLine($"Diff output: {stdout}");5. Start and Forget (Fire & Forget)
5. 启动后不再跟踪(Fire & Forget)
Launch a helper executable without holding onto system handle structures:
csharp
using System;
using System.Diagnostics;
// Fire and forget, getting back only the process ID
int pid = Process.StartAndForget("notepad.exe");
Console.WriteLine($"Notepad started with PID: {pid}");启动辅助可执行程序,无需持有系统句柄结构:
csharp
using System;
using System.Diagnostics;
// Fire and forget, getting back only the process ID
int pid = Process.StartAndForget("notepad.exe");
Console.WriteLine($"Notepad started with PID: {pid}");