ChildProcess
                                
                                 ChildProcess copied to clipboard
                                
                                    ChildProcess copied to clipboard
                            
                            
                            
                        Asmichi.ChildProcess is a .NET library that provides functionality for creating child processes. An alternative to `System.Diagnostics.Process.Start`.
日本語
Asmichi.ChildProcess
A .NET library that provides functionality for creating child processes. Easier, less error-prone, more flexible than System.Diagnostics.Process at creating and interacting with child processes.
This library can be obtained via NuGet.
See the Wiki for the goals and the roadmap.
Comparison with System.Diagnostics.Process
- Concentrates on creating a child process and obtaining its output.
- Cannot query status of a process.
- Cannot create a resident process.
 
- More destinations of redirection:
- NUL
- File (optionally appended)
- Pipe
- Handle
 
- Less error-prone default values for redirection:
- stdin to NUL
- stdout to the current stdout
- stderr to the current stderr
 
- Pipes are asynchronous; asynchronous reads and writes will be handled by IO completion ports.
- Ensures termination of child processes
License
The MIT License
Supported Runtimes
- .NET Core 3.1 or later
RIDs:
- win10-x86(not tested)
- win10-x64(1809 or later; tested on 1809)
- win10-arm(not tested)
- win10-arm64(not tested)
- linux-x64(tested on Ubuntu 18.04)
- linux-arm(not tested)
- linux-arm64(not tested)
- linux-musl-arm64(not tested)
- linux-musl-x64(tested on Alpine 3.13)
- osx-x64(macOS 10.15 Catalina or later; tested on 11)
- osx-arm64(macOS 11.0 Big Sur or later; not tested)
NOTE: On glibc-based Linux, the system must have glibc 2.27 or later and libstdc++ 3.4.25 or later.
Known Issues
- On Windows 10 1809 (including Windows Server 2019), SignalTerminationjust forcibly kills the process tree (the same operation asKill).- This is due to a Windows pseudoconsole bug where ClosePseudoConsoledoes not terminate applications attached to the pseudoconsole.
 
- This is due to a Windows pseudoconsole bug where 
- On macOS prior to 11.0, ExitCodefor processes killed by a signal will always be-1.- This is due to a waitidbug where it returns0insiginfo_t.si_statusfor such processes.
 
- This is due to a 
Notes
- When completely rewriting environment variables with ChildProcessCreationContextorChildProcessFlags.DisableEnvironmentVariableInheritance, it is recommended that you include basic environment variables such asSystemRoot, etc.
Limitations
- More than 2^63 processes cannot be created.
Assumptions on Runtimes
This library assumes that the underlying runtime has the following characteristics:
- Windows
- The inner value of a SafeFileHandleis a file handle.
- The inner value of a SafeWaitHandleis a handle thatWaitForSingleObjectcan wait for.
- The inner value of a SafeProcessHandleis a process handle.
 
- The inner value of a 
- *nix
- The inner value of a SafeFileHandleis a file descriptor.
- The inner value of a SafeProcessHandleis a process id.
- Socket.Handlereturns a socket file descriptor.
 
- The inner value of a 
Examples
Open ChildProcessExample.sln or see ChildProcess.Example for more examples.
Basic
You can read the output of a child, optionally combining stdout and stderr.
var si = new ChildProcessStartInfo("cmd", "/C", "echo", "foo")
{
    StdOutputRedirection = OutputRedirection.OutputPipe,
    // Works like 2>&1
    StdErrorRedirection = OutputRedirection.OutputPipe,
    // DisableArgumentQuoting: See ChildProcessExamplesWindows.cs for details
    Flags = ChildProcessFlags.DisableArgumentQuoting,
};
using (var p = ChildProcess.Start(si))
{
    using (var sr = new StreamReader(p.StandardOutput))
    {
        // "foo"
        Console.Write(await sr.ReadToEndAsync());
    }
    await p.WaitForExitAsync();
    // ExitCode: 0
    Console.WriteLine("ExitCode: {0}", p.ExitCode);
}
Redirection to File
You can redirect the output of a child into a file without ever reading the output.
var tempFile = Path.GetTempFileName();
var si = new ChildProcessStartInfo("cmd", "/C", "set")
{
    ExtraEnvironmentVariables = new Dictionary<string, string> { { "A", "A" } },
    StdOutputRedirection = OutputRedirection.File,
    StdErrorRedirection = OutputRedirection.File,
    StdOutputFile = tempFile,
    StdErrorFile = tempFile,
    // DisableArgumentQuoting: See ChildProcessExamplesWindows.cs for details
    Flags = ChildProcessFlags.UseCustomCodePage | ChildProcessFlags.DisableArgumentQuoting,
    CodePage = Encoding.Default.CodePage, // UTF-8 on .NET Core
};
using (var p = ChildProcess.Start(si))
{
    await p.WaitForExitAsync();
}
// A=A
// ALLUSERSPROFILE=C:\ProgramData
// ...
Console.WriteLine(File.ReadAllText(tempFile));
File.Delete(tempFile);
True piping
You can pipe the output of a child into another child without ever reading the output.
// Create an anonymous pipe.
using var inPipe = new AnonymousPipeServerStream(PipeDirection.In);
var si1 = new ChildProcessStartInfo("cmd", "/C", "set")
{
    // Connect the output to writer side of the pipe.
    StdOutputRedirection = OutputRedirection.Handle,
    StdErrorRedirection = OutputRedirection.Handle,
    StdOutputHandle = inPipe.ClientSafePipeHandle,
    StdErrorHandle = inPipe.ClientSafePipeHandle,
    // DisableArgumentQuoting: See ChildProcessExamplesWindows.cs for details
    Flags = ChildProcessFlags.UseCustomCodePage | ChildProcessFlags.DisableArgumentQuoting,
    CodePage = Encoding.Default.CodePage, // UTF-8 on .NET Core
};
var si2 = new ChildProcessStartInfo("findstr", "Windows")
{
    // Connect the input to the reader side of the pipe.
    StdInputRedirection = InputRedirection.Handle,
    StdInputHandle = inPipe.SafePipeHandle,
    StdOutputRedirection = OutputRedirection.OutputPipe,
    StdErrorRedirection = OutputRedirection.OutputPipe,
    Flags = ChildProcessFlags.UseCustomCodePage,
    CodePage = Encoding.Default.CodePage, // UTF-8 on .NET Core
};
using var p1 = ChildProcess.Start(si1);
using var p2 = ChildProcess.Start(si2);
// Close our copy of the pipe handles. (Otherwise p2 will get stuck while reading from the pipe.)
inPipe.DisposeLocalCopyOfClientHandle();
inPipe.Close();
using (var sr = new StreamReader(p2.StandardOutput))
{
    // ...
    // OS=Windows_NT
    // ...
    Console.Write(await sr.ReadToEndAsync());
}
await p1.WaitForExitAsync();
await p2.WaitForExitAsync();