Asmichi.ChildProcess is a .NET library that provides functionality for creating child processes. An alternative to `System.Diagnostics.Process.Start`.
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.
System.Diagnostics.Process
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.
SignalTermination
just forcibly kills the process tree (the same operation as Kill
).
ClosePseudoConsole
does not terminate applications attached to the pseudoconsole.ExitCode
for processes killed by a signal will always be -1
.
waitid
bug where it returns 0
in siginfo_t.si_status
for such processes.ChildProcessCreationContext
or ChildProcessFlags.DisableEnvironmentVariableInheritance
, it is recommended that you include basic environment variables such as SystemRoot
, etc.This library assumes that the underlying runtime has the following characteristics:
SafeFileHandle
is a file handle.SafeWaitHandle
is a handle that WaitForSingleObject
can wait for.SafeProcessHandle
is a process handle.SafeFileHandle
is a file descriptor.SafeProcessHandle
is a process id.Socket.Handle
returns a socket file descriptor.Open ChildProcessExample.sln or see ChildProcess.Example for more examples.
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);
}
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);
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();