Background and motivation
There are plenty of ways to create a process where .NET does not support this or where there is already a process handle from native method.
Currently there is no possibility to create a .NET Process object from the SafeProcessHandle.
API Proposal
namespace System.Runtime.InteropServices;
[SupportedOSPlatform("windows")]
public ref struct WindowsProcessStartArguments
{
public WindowsProcessStartArguments();
public unsafe char* Arguments { get; }
public unsafe char* EnvironmentVariables{ get; }
public ProcessStartInfo ProcessStartInfo { get; }
public nint StandardError { get; }
public nint StandardInput { get; }
public nint StandardOutput { get; }
public static Process Start(ProcessStartInfo startInfo, Func<WindowsProcessStartArguments, SafeProcessHandle> callback);
public static Process Start<TState>(ProcessStartInfo startInfo, Func<WindowsProcessStartArguments, TState, SafeProcessHandle> callback, TState state);
}
[UnsupportedOSPlatform("windows")]
public ref struct UnixProcessStartArguments
{
public UnixProcessStartArguments();
public unsafe byte* ResolvedPath { get; }
public unsafe byte** Arguments { get; }
public unsafe byte** EnvironmentVariables { get; }
public ProcessStartInfo ProcessStartInfo { get; }
public nint StandardError { get; }
public nint StandardInput { get; }
public nint StandardOutput { get; }
[UnsupportedOSPlatform("ios")]
[UnsupportedOSPlatform("tvos")]
[UnsupportedOSPlatform("maccatalyst")]
public static Process Start(ProcessStartInfo startInfo, Func<UnixProcessStartArguments, SafeProcessHandle> callback);
[UnsupportedOSPlatform("ios")]
[UnsupportedOSPlatform("tvos")]
[UnsupportedOSPlatform("maccatalyst")]
public static Process Start<TState>(ProcessStartInfo startInfo, Func<UnixProcessStartArguments, TState, SafeProcessHandle> callback, TState state);
}
API Usage
ProcessStartInfo startInfo = new("cmd")
{
ArgumentList = { "/c", "echo hello && echo error 1>&2" },
RedirectStandardOutput = true,
RedirectStandardError = true
};
using Process process = WindowsProcessStartArguments.Start(startInfo, (WindowsProcessStartArguments args) =>
{
Interop.Kernel32.STARTUPINFOEX startupInfoEx = default;
Interop.Kernel32.PROCESS_INFORMATION processInfo = default;
Interop.Kernel32.SECURITY_ATTRIBUTES unused_SecAttrs = default;
startupInfoEx.StartupInfo.cb = sizeof(Interop.Kernel32.STARTUPINFOEX);
startupInfoEx.StartupInfo.hStdInput = args.StandardInput;
startupInfoEx.StartupInfo.hStdOutput = args.StandardOutput;
startupInfoEx.StartupInfo.hStdError = args.StandardError;
startupInfoEx.StartupInfo.dwFlags = Interop.Advapi32.StartupInfoOptions.STARTF_USESTDHANDLES;
bool retVal = Interop.Kernel32.CreateProcess(
null,
(char*)args.Arguments,
ref unused_SecAttrs,
ref unused_SecAttrs,
bInheritHandles: true,
Interop.Kernel32.EXTENDED_STARTUPINFO_PRESENT,
(char*)args.EnvironmentVariables,
null,
&startupInfoEx,
&processInfo
);
if (!retVal)
{
throw new Win32Exception();
}
Interop.Kernel32.CloseHandle(processInfo.hThread);
return new SafeProcessHandle(processInfo.hProcess, ownsHandle: true);
});
Alternative Designs
The alternative design included a ctor, but it was too fragile. Check #128491 for details
Risks
Can't see any as of now, as BCL takes care of removing all known risks before invoking the callback.
Working prototype: #128862
Background and motivation
There are plenty of ways to create a process where .NET does not support this or where there is already a process handle from native method.
Currently there is no possibility to create a .NET Process object from the SafeProcessHandle.
API Proposal
API Usage
Alternative Designs
The alternative design included a ctor, but it was too fragile. Check #128491 for details
Risks
Can't see any as of now, as BCL takes care of removing all known risks before invoking the callback.
Working prototype: #128862