I am working with C# and having trouble understanding how to read stdout asynchronously from a child process. What I want to do is create a child process that executes an application and then present whatever is received from that process' stdout in a textbox. I need to see every output character from the child process immediately and can not wait for a line to complete, therefore I don't think that the Process.OutputDataReceived
event suits my purpose. Can you tell me a sane way to accomplish this?
I have tried calling Process.StandardOutput.BaseStream.BeginRead()
and passing a call-back function to it, but in this call-back function I receive an exception from Process.StandardOutput.BaseStream.EndRead()
.
My code looks like this (the child process is a script engine - abbreviated "SE" - for verifying the functionality of an external device. Scripts are executed in sequence and each script requires one instance of the SE application)
private bool startScript()
{
// Starts the currently indexed script
if (null != scriptList)
{
if (0 == scriptList.Count || scriptListIndexer == scriptList.Count)
{
// If indexer equals list count then there are no active scripts in
// the list or the last active script in the list has just finished
return false; // ## RETURN ##
}
if (ScriptExecutionState.RUNNING == scriptList[scriptListIndexer].executionState)
{
return false; // ## RETURN ##
}
if (0 == SE_Path.Length)
{
return false; // ## RETURN ##
}
SE_Process = new Process();
SE_Process.StartInfo.FileName = SE_Path;
SE_Process.StartInfo.CreateNoWindow = true;
SE_Process.StartInfo.UseShellExecute = false;
SE_Process.StartInfo.RedirectStandardError = true;
SE_Process.StartInfo.RedirectStandardOutput = true;
SE_Process.EnableRaisingEvents = true;
SE_Process.StartInfo.Arguments = scriptList[scriptListIndexer].getParameterString();
// Subscribe to process exit event
SE_Process.Exited += new EventHandler(SE_Process_Exited);
try
{
if (SE_Process.Start())
{
// Do stuff
}
else
{
// Do stuff
}
}
catch (Exception exc)
{
// Do stuff
}
// Assign 'read_SE_StdOut()' as call-back for the event of stdout-data from SE
SE_Process.StandardOutput.BaseStream.BeginRead(SE_StdOutBuffer, 0, SE_BUFFERSIZE, read_SE_StdOut, null);
return true; // ## RETURN ##
}
else
{
return false; // ## RETURN ##
}
}
private void read_SE_StdOut(IAsyncResult result)
{
try
{
int bytesRead = SE_Process.StandardOutput.BaseStream.EndRead(result); // <-- Throws exceptions
if (0 != bytesRead)
{
// Write the received data in output textbox
...
}
// Reset the callback
SE_Process.StandardOutput.BaseStream.BeginRead(SE_StdOutBuffer, 0, SE_BUFFERSIZE, read_SE_StdOut, null);
}
catch (Exception exc)
{
// Do stuff
}
}
void SE_Process_Exited(object sender, EventArgs e)
{
// Keep track of whether or not the next script shall be started
bool continueSession = false;
switch (SE_Process.ExitCode)
{
case 0: // PASS
{
// Do stuff
}
...
}
SE_Process.Dispose(); // TODO: Is it necessary to dispose of the process?
if (continueSession)
{
ts_incrementScriptListIndexer();
if (scriptListIndexer == scriptList.Count)
{
// Last script has finished, reset the indexer and re-enable
// 'scriptListView'
...
}
else
{
if (!startScript())
{
// Do stuff
}
}
}
else
{
ts_resetScriptListIndexer();
threadSafeEnableScriptListView();
}
}
What happens is that after one SE process finishes I get an exception of type InvalidOperationException
that says
StandardOut has not been redirected or the process hasn't started yet.
from the call to SE_Process.StandardOutput.BaseStream.EndRead()
. I do not understand why because I have set SE_Process.StartInfo.RedirectStandardOutput
before start of every new process. It appears to me as if the stdout stream of an exited process calls my read_SE_StdOut()
function after the process has been disposed, is that possible?
Thank you for reading!
The exception you get is normal. One BeginRead()
call can never succeed: the last one, just after the process terminates. You'd normally avoid calling BeginRead()
again if you know the process is completed so you don't get the exception. However, you'd rarely know. Just catch the exception. Or use BeginOutputReadLine()
instead, it will catch it for you.
I'm guessing that you also redirect stderr and that the tool uses it to output "X". There is no way to keep output on both stderr and stdout synchronized after it is buffered and redirected.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With