Background and motivation
We have recently introduced two static Process methods: Run and RunAndCaptureText. Both start the process, wait for exit and return exit status. Run inherits standard handles from the parent, while RunAndCaptureText redirects to pipes and captures both std out and err by reding from these pipes. A very common .NET anti-pattern (example: https://github.com/microsoft/aspire/pull/17079/changes#r3243682797) is to redirect both std out and err, start the process and wait for its exit without capturing the output and error just to ensure that nothing gets printed into terminal. This can lead into deadlocks (output and error are not being drained) like it did in dotnet/aspnetcore#65518.
With the new APIs, the users can open the null file handle, configure it via Standard[Input/Output/Error]Handle properties and just pass such ProcessStartInfo to run:
using SafeFileHandle nullHandle = File.OpenNullHandle();
ProcessStartInfo psi = new("exeName", ["--args"])
{
StandardInputHandle = nullHandle,
StandardOutputHandle = nullHandle,
StandardErrorHandle= nullHandle
};
Process.Run(psi);
Since this anti-pattern is so common, we could introduce a helper method that redirects standard handles to null file and waits for the process to exit. One possible name isProcess.RunWithoutOutput, another could be Process.RunAndDiscardOutput.
API Proposal
namespace System.Diagnostics
{
public class Process : System.ComponentModel.Component, System.IDisposable
{
public static ProcessExitStatus RunAndDiscardOutput(ProcessStartInfo startInfo, TimeSpan? timeout = default);
public static ProcessExitStatus RunAndDiscardOutput(string fileName, IList<string>? arguments = null, TimeSpan? timeout = default);
public static Task<ProcessExitStatus> RunAndDiscardOutputAsync(ProcessStartInfo startInfo, CancellationToken cancellationToken = default);
public static Task<ProcessExitStatus> RunAndDiscardOutputAsync(string fileName, IList<string>? arguments = null, CancellationToken cancellationToken = default);
}
}
API Usage
ProcessExitStatus exitStatus = Process.RunAndDiscardOutput("dotnet", ["restore"]);
if (exitStatus.ExitCode != 0)
{
// Handle error
}
Alternative Designs
An alternative would be to extend the Run method with a boolean parameter called silent or similar.
namespace System.Diagnostics
{
public class Process : System.ComponentModel.Component, System.IDisposable
{
- public static ProcessExitStatus Run(ProcessStartInfo startInfo, TimeSpan? timeout = default);
+ public static ProcessExitStatus Run(ProcessStartInfo startInfo, bool silent = false, TimeSpan? timeout = default);
- public static ProcessExitStatus Run(string fileName, IList<string>? arguments = null, TimeSpan? timeout = default);
+ public static ProcessExitStatus Run(string fileName, IList<string>? arguments = null, bool silent = false, TimeSpan? timeout = default);
- public static Task<ProcessExitStatus> RunAsync(ProcessStartInfo startInfo, CancellationToken cancellationToken = default);
+ public static Task<ProcessExitStatus> RunAsync(ProcessStartInfo startInfo, bool silent = false, CancellationToken cancellationToken = default);
- public static Task<ProcessExitStatus> RunAsync(string fileName, IList<string>? arguments = null, CancellationToken cancellationToken = default);
+ public static Task<ProcessExitStatus> RunAsync(string fileName, IList<string>? arguments = null, bool silent = false, CancellationToken cancellationToken = default);
}
}
When set to true, it would redirect all standard handles to null.
Risks
No response
Background and motivation
We have recently introduced two static
Processmethods:RunandRunAndCaptureText. Both start the process, wait for exit and return exit status.Runinherits standard handles from the parent, whileRunAndCaptureTextredirects to pipes and captures both std out and err by reding from these pipes. A very common .NET anti-pattern (example: https://github.com/microsoft/aspire/pull/17079/changes#r3243682797) is to redirect both std out and err, start the process and wait for its exit without capturing the output and error just to ensure that nothing gets printed into terminal. This can lead into deadlocks (output and error are not being drained) like it did in dotnet/aspnetcore#65518.With the new APIs, the users can open the null file handle, configure it via
Standard[Input/Output/Error]Handleproperties and just pass suchProcessStartInfoto run:Since this anti-pattern is so common, we could introduce a helper method that redirects standard handles to
nullfile and waits for the process to exit. One possible name isProcess.RunWithoutOutput, another could beProcess.RunAndDiscardOutput.API Proposal
API Usage
Alternative Designs
An alternative would be to extend the
Runmethod with a boolean parameter calledsilentor similar.namespace System.Diagnostics { public class Process : System.ComponentModel.Component, System.IDisposable { - public static ProcessExitStatus Run(ProcessStartInfo startInfo, TimeSpan? timeout = default); + public static ProcessExitStatus Run(ProcessStartInfo startInfo, bool silent = false, TimeSpan? timeout = default); - public static ProcessExitStatus Run(string fileName, IList<string>? arguments = null, TimeSpan? timeout = default); + public static ProcessExitStatus Run(string fileName, IList<string>? arguments = null, bool silent = false, TimeSpan? timeout = default); - public static Task<ProcessExitStatus> RunAsync(ProcessStartInfo startInfo, CancellationToken cancellationToken = default); + public static Task<ProcessExitStatus> RunAsync(ProcessStartInfo startInfo, bool silent = false, CancellationToken cancellationToken = default); - public static Task<ProcessExitStatus> RunAsync(string fileName, IList<string>? arguments = null, CancellationToken cancellationToken = default); + public static Task<ProcessExitStatus> RunAsync(string fileName, IList<string>? arguments = null, bool silent = false, CancellationToken cancellationToken = default); } }When set to true, it would redirect all standard handles to
null.Risks
No response