问题
I have an application which starts 1-45 processes (job dependent) and communicates with them via named pipes. I'm only interested in the stderr
of the child processes and have no interest in redirecting the stdout
of the processes.
I have written code that does exactly this, I redirect stderr
and not stdout
and everything works as expected. And when I say that I mean, I get the redirected stderr
and I don't see any stdout
appearing on the parent processes console nor do I get any deadlocks due to not redirecting stdout
.
My concern here is, where is the
stdout
of the child processes going? The processes tend to dump a lot of text tostdout
and I'm afraid they're filling up some internalbuffer
which is going to cause the child processes toblock
when writing tostdout
.
In the past I also redirected stdout
and just dumped (did nothing) the data provided from the async event.
But doing this had a severe consequence.
As stated above I communicate via named pipes
with the child
processes. What I noticed is when I redirect stdout
it hammers the performance of the parent processes write
operations over the named pipes
.
When not redirecting stdout
the write times are usually < 1ms
but after redirecting stdout
the times jump to > 4s
...
My question is, is it safe to only redirect
stderr
?
Below is part of the code I use to wrap the Process
. I also use that well established extension
method for await
ing the process to complete. I didn't post that but I can if you're not familiar with it.
public class ChildProcess
{
public async Task StartProcessAsync( string executablePath, CancellationToken token = default )
{
if ( !File.Exists( executablePath ) )
throw new FileNotFoundException( "The specified rip executable path does not exist", executablePath );
using var process = new Process( );
process.StartInfo.FileName = executablePath;
process.StartInfo.UseShellExecute = false;
process.StartInfo.ErrorDialog = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardError = true;
await StartProcess( process, StdErrorOutput, token ).ConfigureAwait( false );
static void StdErrorOutput( object sender, DataReceivedEventArgs args )
{
if ( string.IsNullOrWhiteSpace( args.Data ) ) return;
}
}
private Task StartProcess( Process process,
DataReceivedEventHandler? stderr, CancellationToken token = default )
{
return Task.Run( async ( ) =>
{
try
{
if ( stderr is object )
process.ErrorDataReceived += stderr;
if ( !process.Start( ) )
throw new ApplicationException( $"Failed to start '{process.StartInfo.FileName}'" );
if ( stderr is object )
process.BeginErrorReadLine( );
await process.WaitForExitAsync( token ).ConfigureAwait( false );
}
finally
{
SafeKill( );
if ( stderr is object )
process.ErrorDataReceived -= stderr;
}
void SafeKill( )
{
try
{
if ( !process.HasExited )
{
process.Refresh( );
if ( stderr is object )
process.CancelErrorRead( );
process.Kill( );
}
}
catch ( Exception ex )
{
Console.WriteLine( ex );
}
}
} );
}
}
Edit
After reading the comment left by Eryk Sun I decided to dig into the Process
source code to see if I could figure out what's happening. Mainly I was interested in seeing how the STARTUPINFO
structure was populated when only specifying redirection of one of the std
streams
.
I removed a bunch of code I wasn't interest in. From what I can tell, if I set RedirectStandardError
to true
and specify RedirectStandardOutput
to false
the STARTIPINFO
structures hStdOutput
member is set to stdout
. This results in the StandardOutput
property on the Process
class
to be null
.
If RedirectStandardOutput
was set to true
the output from the child process is redirected through a pipe
. A StreamReader
instance is then created and assigned to StandardOutput
. The StreamReader
then reads from the pipe
created to attain the output from the child process.
So if the
HANDLE
is being set tostdout
whenRedirectStandardOutput
is set tofalse
, why do I not see the output on the parent processes console window? Where is the output going?
System.Diagnostics.Process.StartWithCreateProcess
source
SafeFileHandle? childInputPipeHandle = null;
SafeFileHandle? parentOutputPipeHandle = null;
if (startInfo.RedirectStandardInput || startInfo.RedirectStandardOutput || startInfo.RedirectStandardError)
{
if (startInfo.RedirectStandardOutput)
{
CreatePipe(out parentOutputPipeHandle, out childOutputPipeHandle, false);
}
else
{
childOutputPipeHandle = new SafeFileHandle(Interop.Kernel32.GetStdHandle(Interop.Kernel32.HandleTypes.STD_OUTPUT_HANDLE), false);
}
startupInfo.hStdOutput = childOutputPipeHandle.DangerousGetHandle();
startupInfo.dwFlags = Interop.Advapi32.StartupInfoOptions.STARTF_USESTDHANDLES;
}
/* Code starting the process removed */
if (startInfo.RedirectStandardOutput)
{
Encoding enc = startInfo.StandardOutputEncoding ?? GetEncoding((int)Interop.Kernel32.GetConsoleOutputCP());
_standardOutput = new StreamReader(new FileStream(parentOutputPipeHandle!, FileAccess.Read, 4096, false), enc, true, 4096);
}
来源:https://stackoverflow.com/questions/61891684/ignoring-stdout-of-child-process-dangerous