-
Notifications
You must be signed in to change notification settings - Fork 5.3k
Description
Background and motivation
When .NET is starting a new process and not redirecting standard input, output, or error, it uses the handles of the parent process.
As of today, we don't expose any API to get those handles, which makes it impossible to implement advanced scenarios like piping between processes without resorting to P/Invoke.
API Proposal
namespace System;
public static class Console
{
public static Stream OpenStandardInput();
public static Stream OpenStandardOutput();
public static Stream OpenStandardError();
+ public static SafeFileHandle OpenStandardInputHandle();
+ public static SafeFileHandle OpenStandardOutputHandle();
+ public static SafeFileHandle OpenStandardErrorHandle();
}API Usage
using SafeFileHandle inputHandle = Console.OpenStandardInputHandle();
using SafeFileHandle outputHandle = Console.OpenStandardOutputHandle();
using SafeFileHandle errorHandle = Console.OpenStandardErrorHandle();
using var procHandle = SafeChildProcessHandle.Start(options, inputHandle, outputHandle, errorHandle);
procHandle.WaitForExit();Alternative Designs
Console already defines methods like Console.OpenStandardInput(), that is why I've used the Open prefix here as well. But we don't really open anything here, we just get existing handles. So another option would be to use the Get prefix instead:
public static SafeFileHandle GetStandardInputHandle();
public static SafeFileHandle GetStandardOutputHandle();
public static SafeFileHandle GetStandardErrorHandle();And this is exactly how Windows named their sys-call: GetStdHandle.
Risks
SafeFileHandle implements IDisposable, so users typically use using statements to ensure proper cleanup. However, closing STD IN/OUT/ERROR handles of the current process is in 99.99% cases not desired. Therefore, we need to ensure that the returned handles are not closed when the user calls Dispose(). Or at least not by default. So the implementation of the above methods uses the following constructor:
public SafeFileHandle(IntPtr preexistingHandle, bool ownsHandle)With ownsHandle set to false, so that disposing the handle does not close the underlying OS handle.
We could allow the users to specify if they want to "own" the handle. And make it false by default:
public static SafeFileHandle OpenStandardInputHandle(bool ownsHandle = false);
public static SafeFileHandle OpenStandardOutputHandle(bool ownsHandle = false);
public static SafeFileHandle OpenStandardErrorHandle(bool ownsHandle = false);But if we don't, the 0.01% users can just do the following if they really want to close STD IN/OUT/ERR:
SafeFileHandle inputHandle = new(Console.OpenStandardInputHandle().DangerousGetHandle(), ownsHandle: true);
inputHandle.Dispose();