1

This has been kind of a "criminal research" with very surprising outcome - at least to me.

We are running external commands using a Process object. We are capturing standard output (and standard error - I have removed this from the sample for clarity reasons) asynchronously. This has been answered several times here. The code looks like this and works fine:

var process = new Process
{
    StartInfo =
    {
        UseShellExecute = false,
        RedirectStandardOutput = true,
        FileName = @"...\TestApp.exe"
    }
};
process.OutputDataReceived += Process_OutputDataReceived;
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
process.OutputDataReceived -= Process_OutputDataReceived;

Now, what if I want to add a hard timeout to the WaitForExit()? Easy: there's an overloaded version of WaitForExit(int milliseconds). So a call like this should do:

process.WaitForExit(60000);

Now, this renders the output capture incomplete in some cases.

This is not a secret, at least not to Microsoft's developers. After several hours of research, I came to the conclusion that something must be wrong with WaitForExit's implementation. So I took a look into the code and I found this comment in Microsoft's implementation:

// If we have a hard timeout, we cannot wait for the streams

Here's my question - and I have done quite some effort of evaluation without success:

How can I have a hard timeout on WaitForExit() and at the same time make sure that the console output capture is complete?

freefall
  • 388
  • 1
  • 14
  • 1
    Don't ignore the return value of WaitForExit(). If it is *true* then you know there might still be some unread output. So sleep for a bit to allow it to catch up. – Hans Passant Feb 22 '17 at 15:05
  • Sleeping is exactly what I'm trying to eliminate. [sarcasm on] In my humble opinion, `Thread.Sleep()` should automatically pop up a message box telling "I'm a bad programmer" which has to be acknowledged every time. (No, kidding, there might be some 0.05% of cases where `Thread.Sleep()` is used by good programmers for good reasons, too.) [sarcasm off] – freefall Feb 22 '17 at 15:21
  • This problem got started a long, long time ago, I/O redirection was certainly one of Unix' worst features to make it into Windows. Hacks like that just don't age well. Not dealing with the stderr output hack is another one you'll be likely to regret. You'll have to hack around the hacks, that's what it takes. – Hans Passant Feb 22 '17 at 15:29

3 Answers3

1

Have you tried using the OnExited event in a derived class? See this. That might allow you to use time-out the wait yourself instead of passing it into WaitForExit.

  • Thanks for your quick shot. But this calls `WaitForInputIdle` which is only available for processes which have their own window and an event queue. In my case, I have to run command line utilities. – freefall Feb 22 '17 at 15:17
1

There's actually another (better) solution - you can add an AutoResetEvent and set it when the data received from those two events is null. Then, you check not only if the process has exited, but that the two AutoResetEvent's WaitOne methods are true.

This was answered here: ProcessStartInfo hanging on "WaitForExit"? Why?

benpage
  • 4,418
  • 3
  • 41
  • 39
0

@Hans Passant Your comment directed me into the right direction. Actually, I do not really know how long to Sleep(). But I can add a simple watch dog for this. Since the process has already been finished by the time when I have to start waiting for the missing captures, I can assume that the OutputDataReceived event is fired continuously until the stream is empty. So all I did was adding a Stopwatch and restarting it each time at the beginning of the OutputDataReceived event method. In the main code, I replace the call to WaitForExit(60000) by the following code:

if (process.WaitForExit(60000))
{
   while (outputWatch.ElapsedMilliseconds < 20) Thread.Sleep(20);
}

Okay, there's a Thread.Sleep() inside this code, as well. But it definitely counts to the 0.05%... :)

Community
  • 1
  • 1
freefall
  • 388
  • 1
  • 14