4

I have written a Process which reads data from the file given as an argument. I have read the StandardOutput asynchronously and StandardError synchronously.

public static string ProcessScript(string command, string arguments)
{
        Process proc = new Process();
        proc.StartInfo.UseShellExecute = false;
        proc.StartInfo.RedirectStandardOutput = true;
        proc.StartInfo.RedirectStandardError = true;
        proc.StartInfo.FileName = command;
        proc.StartInfo.Arguments = arguments;
        proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden; 
        proc.Start();
        string error = null;
        string output = null;
        proc.OutputDataReceived += (sender, outputLine) => 
        { 
            if (outputLine.Data != null) 
            {
                output += outputLine.Data;
            }
        };
        proc.BeginOutputReadLine();
        error = proc.StandardError.ReadToEnd();
        proc.WaitForExit();
        proc.Close();

        //I have not got entire Output
        return output;
} 

After the process has been finished I am getting output. But not entirely. I am getting only partial data. The asynchronous reading is not over even after the process completes its task, So only I am getting partial data. I need complete string which is given.

Edit:

I am using .Net 3.5. I can't use ReadToEndAsync Method

Any ideas?

BinaryMee
  • 2,102
  • 5
  • 27
  • 46
  • 1
    Using *both* OutputDataReceived and BeginOutputReadLine() is a mistake. – Hans Passant Jun 06 '14 at 13:36
  • @HansPassant: Thanks for pointing it out. But in this example http://msdn.microsoft.com/en-us/library/vstudio/system.diagnostics.process.beginoutputreadline, I found both were used. – BinaryMee Jun 06 '14 at 13:42
  • I have seen this issue as well and I solved it using a thread.sleep. Maybe the output event is raised on a separate thread which implies arbitrary delays. This API design seems to force this error and make it unavoidable. – usr Jun 06 '14 at 13:47
  • 2
    @HansPassant can you elaborate? BeginOutputReadLine is used to make the event "live". It does not return anything. The Process class is misdesigned. – usr Jun 06 '14 at 13:48
  • @usr: Yes. I also did some thread delay to read the full output. But the data may vary and for some process that Thread delay looked like an unnecessary delay. That's why I was looking for some other options. – BinaryMee Jun 06 '14 at 13:52
  • Process.WaitForExit() is interlocked with BeginOutputReadLine(), it won't return until end-of-file is detected. Also using OutputDataReceived is just a randomizer for problems. – Hans Passant Jun 06 '14 at 13:55
  • @HansPassant: Then how it can be handled? – BinaryMee Jun 06 '14 at 14:00

3 Answers3

6

Rather than handling the event, and dealing with the problems that arise with it, you can simply read from the actual output stream directly (assuming you're using .NET 4.5, thanks to its added asynchronous functionality).

public static string ProcessScript(string command, string arguments)
{
    Process proc = new Process();
    proc.StartInfo.UseShellExecute = false;
    proc.StartInfo.RedirectStandardOutput = true;
    proc.StartInfo.RedirectStandardError = true;
    proc.StartInfo.FileName = command;
    proc.StartInfo.Arguments = arguments;
    proc.Start();

    var output = proc.StandardOutput.ReadToEndAsync();
    var error = proc.StandardError.ReadToEndAsync();
    proc.WaitForExit();
    proc.Close();
    var errorContent = error.Result;
    return output.Result;
}

Here the Task represented by ReadToEndAsync won't actually complete until it has the entirety of the data its result represents. This means that you're waiting until you have all of the data rather than waiting for the process to finish, as those two may not be at exactly the same time.

Servy
  • 202,030
  • 26
  • 332
  • 449
0

According to this post it is possible that the event handlers fire after WaitForExit has completed. I tried to find out how using Reflector but I couldn't see it. Anyway, I have experienced that happen myself.

This post also tells the secret of how to deal with this: Apparently, the event handlers are called with null data when no more input will be coming. So you'd need to wait for this condition.

I think Servy's way of dealing with this is superior. I'm just documenting this behavior here.

usr
  • 168,620
  • 35
  • 240
  • 369
0

Duplicate of https://stackoverflow.com/a/25772586/2065863

Just call WaitForExit() (no arguments) after WaitForExit(timeout) returned true:

if (process.WaitForExit(timeout) && process.WaitForExit())
{
    // do stuff: async read will be completed here
}

For details read the remarks here: https://msdn.microsoft.com/en-us/library/ty0d8k56(v=vs.110).aspx

Erunehtar
  • 1,583
  • 1
  • 19
  • 38