WaitForExit() times out when I try to capture multi-line Stdout

J

John Brock

I have written a short VB.NET function (see below) which captures
StandardOutput from a particular Windows program (myProgram.exe).

The problem is this: if myProgram.exe returns more than a single
line of text then WaitForExit() always times out! The function
works fine if myProgram.exe return only one line of text. It also
works fine with multi-line output -- returning a String with embedded
newlines -- provided I remove the test on WaitForExit(). Note that
if I execute myProgram.exe from the command line it returns quickly,
no matter how many lines of text I have asked for.

My understanding of WaitForExit() is that it returns True when the
process that it is waiting on exits, or False if the process has
not exited after a specified number of milliseconds.

Clearly I must have misunderstood something, but I see nothing in
the online documentation to suggest why it would matter whether
the the output from the program contains one line or many. Can
anyone tell me what I am doing wrong?


Public Function myFunction(ByVal myOptions As String) As String

Dim psInfo As New Diagnostics.ProcessStartInfo
psInfo.FileName = "myProgram.exe"
psInfo.Arguments = myOptions
psInfo.CreateNoWindow = True
psInfo.UseShellExecute = False
psInfo.RedirectStandardOutput = True

Dim newProc As Diagnostics.Process = Diagnostics.Process.Start(psInfo)

Dim myOutput As String = ""

If newProc.WaitForExit(6000) Then myOutput = newProc.StandardOutput.ReadToEnd

newProc.StandardOutput.Close()
newProc.Close()
newProc.Dispose()

Return myOutput
End Function
 
S

SoftLion

You must use a worker thread to read the standart input/ouput
while waiting for the program end in the main thread.
 
J

John Brock

[...]
Clearly I must have misunderstood something, but I see nothing in
the online documentation to suggest why it would matter whether
the the output from the program contains one line or many. Can
anyone tell me what I am doing wrong?
You have redirected the standard output, but you are not reading from it
when you are blocked on the call to WaitForExit(). So, when the other
process fills the relatively small output buffer for standard output, it
blocks. It can't proceed until you read data from the standard output,
freeing up the buffer space so that it can emit more output.

Since you are using ReadToEnd(), there's really no reason for you to call
WaitForExit(). By definition, the standard output stream won't end until
the process has exited. So you can just call ReadToEnd() itself, and it
will block until the other process has exited, just as WaitForExit() would
have had you not redirected the output.

The docs do in fact describe this issue. From the page for
ProcessStartInfo.RedirectStandardOutput:
http://msdn2.microsoft.com/en-us/li....processstartinfo.redirectstandardoutput.aspx

When the child process writes enough data to fill its
redirected stream, it is dependent on the parent. The
child process waits for the next write operation until
the parent reads from the full stream or closes the
stream. The deadlock condition results when the caller
and child process wait for each other to complete an
operation, and neither can continue.

Your code doesn't quite deadlock, because you've included a timeout that
will break the deadlock. But otherwise, that's the exact scenario you're
running into.

Thank you, that is very helpful! I'm still wondering about something
though...

I put the timeout in my function to mimic the timeout in the Shell()
function, which will break if the program it is running hangs.
The document you pointed me to seems to suggest (in the first
example) that I can avoid deadlocks by calling WaitForExit after
ReadToEnd().

The code example avoids a deadlock condition by calling
p.StandardOutput.ReadToEnd before p.WaitForExit. A deadlock
condition can result if the parent process calls p.WaitForExit
before p.StandardOutput.ReadToEnd and the child process
writes enough text to fill the redirected stream.

But what if the program hangs? How can I Read To End if the program
I am running doesn't end, if it just stops writing to Stdout, but
doesn't exit or close the stream? How would I ever even reach the
WaitForExit() statement?

Am I still misunderstanding something? Or is there just no way to
implement a fully functional Shell() style timeout without resorting
to threads?
 
Top