This guide demonstrates how to use the built-in .NET System.Diagnostics.Process class to achieve the same functionality as the C++ PipedProcess library.
The .NET Process class provides:
- Redirection of standard input/output/error streams
- Synchronous and asynchronous operation
- Process window visibility control
- Process termination support
- User impersonation (RunAs)
- Error handling and exit codes
- Cross-platform support
using System.Diagnostics;
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
Arguments = "/c echo Hello World",
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
process.Start();
string output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
Console.WriteLine($"Output: {output}");
Console.WriteLine($"Exit code: {process.ExitCode}");var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "sort.exe",
RedirectStandardInput = true,
RedirectStandardOutput = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
process.Start();
// Send data to the process
string input = "line2\nline1\nline3";
process.StandardInput.WriteLine(input);
process.StandardInput.Close(); // Important: close the input stream
// Read the sorted output
string sortedOutput = process.StandardOutput.ReadToEnd();
process.WaitForExit();
Console.WriteLine($"Sorted output: {sortedOutput}");async Task RunProcessAsync(string filename, string arguments, string input = null)
{
using var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = filename,
Arguments = arguments,
RedirectStandardInput = input != null,
RedirectStandardOutput = true,
RedirectStandardError = true,
UseShellExecute = false,
CreateNoWindow = true
}
};
process.Start();
// Create tasks for reading output and error streams
var readOutput = process.StandardOutput.ReadToEndAsync();
var readError = process.StandardError.ReadToEndAsync();
// Write input if provided
if (input != null)
{
await process.StandardInput.WriteAsync(input);
process.StandardInput.Close();
}
// Wait for process to complete and collect all output
await process.WaitForExitAsync();
string output = await readOutput;
string error = await readError;
Console.WriteLine($"Output: {output}");
if (!string.IsNullOrEmpty(error))
Console.WriteLine($"Error: {error}");
Console.WriteLine($"Exit code: {process.ExitCode}");
}try
{
var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "nonexistent.exe",
RedirectStandardError = true,
UseShellExecute = false
}
};
process.Start();
}
catch (System.ComponentModel.Win32Exception ex)
{
Console.WriteLine($"Failed to start process: {ex.Message}");
}async Task RunWithCancellationAsync(CancellationToken cancellationToken)
{
using var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "long-running-process.exe",
RedirectStandardOutput = true,
UseShellExecute = false
}
};
// Register cancellation
cancellationToken.Register(() =>
{
try
{
if (!process.HasExited)
process.Kill();
}
catch (InvalidOperationException) { } // Process already exited
});
process.Start();
await process.WaitForExitAsync(cancellationToken);
}var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "notepad.exe",
Arguments = "test.txt",
UseShellExecute = true, // Required for window manipulation
WindowStyle = ProcessWindowStyle.Hidden // or Normal, Minimized, Maximized
}
};
process.Start();var process = new Process
{
StartInfo = new ProcessStartInfo
{
FileName = "cmd.exe",
UseShellExecute = false,
RedirectStandardOutput = true,
Domain = "DOMAIN",
UserName = "username",
Password = secureString, // System.Security.SecureString
}
};
process.Start();- Validate all input parameters, especially when handling user-provided values
- Be careful with shell execution (
UseShellExecute = true) as it can be vulnerable to command injection - Use proper error handling and timeout mechanisms
- Consider using allowlists for executable paths
- Handle process output streams to prevent memory issues with large outputs
- Use secure strings for sensitive data like passwords
- Implement proper cleanup of process resources using
usingstatements or explicit Dispose calls
-
Always dispose of Process objects:
using var process = new Process();
-
Close redirected streams when done:
process.StandardInput.Close();
-
Use async methods for better scalability:
await process.WaitForExitAsync();
-
Set appropriate timeouts:
if (!process.WaitForExit(timeoutMs)) process.Kill();
-
Handle both output and error streams:
var outputTask = process.StandardOutput.ReadToEndAsync(); var errorTask = process.StandardError.ReadToEndAsync(); await Task.WhenAll(outputTask, errorTask);
The .NET implementation offers several advantages:
- Native async/await support for non-blocking operations
- Stream-based I/O for more flexible data handling
- Built-in cross-platform support
- More comprehensive process management capabilities
- Integration with .NET security features
- Exception-based error handling (can be wrapped if exception-free API is preferred)