A cross-platform .NET library that ensures child processes automatically terminate when the parent process exits unexpectedly.
- Cross-Platform Support: Works on Windows, Linux, and macOS
- Automatic Cleanup: Child processes terminate when the parent exits
- Windows Job Objects: Uses Job Objects for kernel-level process management on Windows
- Process Tree Termination: Terminates all descendant processes via native APIs
- Async/Await Support: Full asynchronous API with cancellation tokens
- Process Monitoring: Real-time statistics and lifecycle events
- Batch Processing: Start multiple processes with concurrency control
- Thread-Safe: Supports concurrent operations
- Graceful Shutdown: Configurable timeout and fallback mechanisms
- Custom Logging: Pluggable log action delegate
- .NET Standard 2.0+ / .NET Framework 4.6.1+ / .NET Core 2.0+ / .NET 5+
- .NET 10.0+
| Target Framework | Status |
|---|---|
netstandard2.0 | Supported |
netstandard2.1 | Supported |
net10.0 | Supported |
Install-Package ChildProcessGuarddotnet add package ChildProcessGuardusingChildProcessGuard;// Simple usage with automatic cleanupusingvarguardian=newProcessGuardian();varprocess=guardian.StartProcess("notepad.exe");Console.WriteLine($"Started process with PID: {process.Id}");// Process will be automatically terminated when guardian is disposedusingChildProcessGuard;// Configure with builder patternusingvarguardian=ProcessGuardianBuilder.Debug().WithKillTimeout(TimeSpan.FromSeconds(10)).WithMaxProcesses(50).WithDetailedLogging(true).WithAutoCleanup(true,TimeSpan.FromMinutes(1)).Build();// Set up event handlersguardian.ProcessError+=(sender,e)=>Console.WriteLine($"Error: {e.Operation} - {e.Exception.Message}");guardian.ProcessLifecycleEvent+=(sender,e)=>Console.WriteLine($"Event: {e.EventType} - {e.ProcessInfo}");varprocess=guardian.StartProcess("myapp.exe","--verbose");usingvarguardian=newProcessGuardian();varenvVars=newDictionary<string,string>{{"DEBUG","true"},{"CONFIG_PATH","/etc/myapp/config.json"},{"LOG_LEVEL","verbose"}};varprocess=guardian.StartProcess("myapp.exe","--config config.json",workingDirectory:"/path/to/working/dir",environmentVariables:envVars);usingvarguardian=newProcessGuardian();varstartInfo=newProcessStartInfo{FileName="powershell.exe",Arguments="-NoProfile -Command \"Get-Process\"",UseShellExecute=false,RedirectStandardOutput=true,CreateNoWindow=true};varprocess=guardian.StartProcessWithStartInfo(startInfo);stringoutput=process.StandardOutput.ReadToEnd();usingvarguardian=ProcessGuardianBuilder.HighPerformance().Build();// Prepare multiple processesvarprocessInfos=Enumerable.Range(1,5).Select(i =>newProcessStartInfo("ping","127.0.0.1 -n 3")).ToList();// Start all processes concurrentlyvarprocesses=awaitguardian.StartProcessesBatchAsync(processInfos,maxConcurrency:3);// Wait for all to completeboolallCompleted=awaitguardian.WaitForAllProcessesAsync(TimeSpan.FromSeconds(30));Console.WriteLine($"All processes completed: {allCompleted}");usingvarguardian=newProcessGuardian();// Start processesguardian.StartProcess("notepad.exe");guardian.StartProcess("calc.exe");// Get statisticsvarstats=guardian.GetStatistics();Console.WriteLine($"Total: {stats.TotalProcesses}, Running: {stats.RunningProcesses}");Console.WriteLine($"Memory Usage: {stats.TotalMemoryUsage/1024/1024:F1} MB");// Get detailed process informationvarrunningProcesses=guardian.GetProcessesByStatus(ProcessStatus.Running);foreach(varprocessInfoinrunningProcesses){Console.WriteLine($"Process: {processInfo}");Console.WriteLine($"Runtime: {processInfo.GetRuntime():hh\\:mm\\:ss}");}usingvarguardian=newProcessGuardian();varcts=newCancellationTokenSource();// Start process asynchronouslyvarprocess=awaitguardian.StartProcessAsync("myapp.exe",cancellationToken:cts.Token);// Terminate all processesintterminated=awaitguardian.KillAllProcessesAsync(TimeSpan.FromSeconds(10));Console.WriteLine($"Terminated {terminated} processes");// Selective terminationintkilled=awaitguardian.TerminateProcessesWhere(
p =>p.GetRuntime()>TimeSpan.FromMinutes(5),TimeSpan.FromSeconds(5));// Route logs to your logging frameworkusingvarguardian=newProcessGuardianBuilder().WithDetailedLogging(true).WithLogAction(message =>logger.LogInformation(message)).Build();// Or use options directlyvaroptions=newProcessGuardianOptions{EnableDetailedLogging=true,LogAction= message =>Debug.WriteLine(message)};usingvarguardian=newProcessGuardian(options);usingvarguardian=newProcessGuardian();stringexecutable,arguments;if(RuntimeInformation.IsOSPlatform(OSPlatform.Windows)){executable="cmd.exe";arguments="/c echo Hello from Windows";}else{executable="/bin/bash";arguments="-c 'echo Hello from Unix'";}varprocess=guardian.StartProcess(executable,arguments);awaitprocess.WaitForExitAsync();varoptions=newProcessGuardianOptions{ProcessKillTimeout=TimeSpan.FromSeconds(30),// Graceful termination timeoutEnableDetailedLogging=false,// Verbose loggingForceKillOnTimeout=true,// Force kill if timeout exceededMaxManagedProcesses=100,// Maximum concurrent processesAutoCleanupDisposedProcesses=true,// Auto cleanup exited processesCleanupInterval=TimeSpan.FromMinutes(5),// Cleanup check intervalThrowOnProcessOperationFailure=false,// Exception handling behaviorLogAction= msg =>Console.WriteLine(msg)// Custom log handler (optional)};usingvarguardian=newProcessGuardian(options);// High performance configurationusingvarhighPerf=ProcessGuardianBuilder.HighPerformance().Build();// Debug configuration with detailed loggingusingvardebug=ProcessGuardianBuilder.Debug().Build();// Custom configurationusingvarcustom=newProcessGuardianBuilder().WithKillTimeout(TimeSpan.FromSeconds(15)).WithMaxProcesses(200).WithDetailedLogging(true).WithLogAction(msg =>myLogger.Log(msg)).Build();- Uses Windows Job Objects with
JOB_OBJECT_LIMIT_KILL_ON_JOB_CLOSEflag - Automatically terminates all child processes when the job handle is closed
- Kernel-level guarantee for process cleanup
- Per-process Job Object assignment tracking with graceful fallback
- Uses manual process tree tracking via
/procfilesystem (Linux) and native APIs - Uses
SIGTERMfor graceful termination,SIGKILLfor force termination - Enumerates and terminates descendant processes using process tree walking
- Hooks into
AppDomain.ProcessExitandConsoleCancelKeyPressevents - Falls back to basic process termination if advanced features fail
- Provides .NET 5+ features (e.g.,
WaitForExitAsync) for .NET Standard 2.0/2.1 compatibility
- Always use
usingstatements or callDispose()explicitly - Configure appropriate timeouts based on process characteristics
- Handle events for production applications to track errors
- Use builder pattern for complex configurations
- Use
LogActionto route logs to your logging framework instead of relying onConsole.WriteLine - Monitor statistics in long-running applications
- Test cross-platform behavior when targeting multiple operating systems
This project is licensed under the MIT License - see the LICENSE file for details.
Contributions are welcome! Please feel free to submit a Pull Request.
For issues, questions, or suggestions, please open an issue on GitHub.