How to get the output of a System.Diagnostics.Process?
Asked Answered
C

4

41

I run ffmpeg like this:

System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo = new System.Diagnostics.ProcessStartInfo(ffmpegPath, myParams);
p.Start();
p.WaitForExit();

... but the problem is that the console with ffmpeg pops up and disappears right away, so I can't get any feedback. I don't even know if the process ran correctly.

So how can I either:

  • Tell the console to stay opened

  • Retrieve in the C# what the console displayed

Caron answered 7/9, 2009 at 18:45 Comment(0)
K
59

What you need to do is capture the Standard Output stream:

p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
// instead of p.WaitForExit(), do
string q = "";
while ( ! p.HasExited ) {
    q += p.StandardOutput.ReadToEnd();
}

You may also need to do something similar with StandardError. You can then do what you wish with q.

It is a bit finicky, as I discovered in one of my questions

As Jon Skeet has pointed out, it is not smart performance-wise to use string concatenation like this; you should instead use a StringBuilder:

p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
// instead of p.WaitForExit(), do
StringBuilder q = new StringBuilder();
while ( ! p.HasExited ) {
    q.Append(p.StandardOutput.ReadToEnd());
}
string r = q.ToString();
Keely answered 7/9, 2009 at 18:50 Comment(14)
Right basic stuff, but I wouldn't recommend building up the string like that. Use a StringBuilder :)Adnopoz
actually there's an error: StandardOut has not been redirected or the process hasn't started yet. I'm trying to figure out what's going onCaron
Hmmm... I'm not sure about that.Keely
If you still have a problem post your code to pastebin.com and I'll have a look.Keely
I tried changing some stuff but it's still not working. Here's the full code: pastebin.com/m50f26695Caron
I'm supposed to use ffmpegPath to call ffmpeg, but "ffmpeg.exe" is also working for debug.Caron
apparently when I run the debuger the program ends up in the loop while(! p.HasExited ) with p.HasExited == true :\Caron
Ah, sorry for not making that clear - p.StartInfo.RedirectStandardOutput = true needs to go before the p.Start().Keely
Thanks. It fixed it... but now I have "The Process object must have the UseShellExecute property set to false in order to redirect IO streams." Is this something I have control over?Caron
I've edited and accepted your answer. If you could incorporate jon's answer that would be perfect :) But if you don't that was already helpful! thanks a lotCaron
I've added an example of using a StringBuilder at the bottom. :)Keely
There is a race condition with this approach: If the process ends before you enter the while loop, the output will not be read.Politi
@Politi is correct. There is a race condition, and the while(!p.HasExited) loop is never entered when the process is quick to run. E.g., try ipconfig.Skelp
@Skelp - thank you for veryfying. For the reference of any future readers, I have created an alternative answer to fix this.Politi
P
22

Lucas' answer has a race condition: If the process finishes quickly the while loop is left (or never entered) even if there is some output left, that is you might miss out on some data. To prevent that, another ReadToEnd should be done after the process exited.

(Note that in comparison to the old version of my answer, I can no longer see a need for WaitForExit once the process.HasExited flag is true, so this boils down to:)

using (var process = Process.Start(startInfo))
{
    var standardOutput = new StringBuilder();

    // read chunk-wise while process is running.
    while (!process.HasExited)
    {
        standardOutput.Append(process.StandardOutput.ReadToEnd());
    }

    // make sure not to miss out on any remaindings.
    standardOutput.Append(process.StandardOutput.ReadToEnd());

    // ...
}
Politi answered 4/9, 2013 at 14:19 Comment(4)
I believe the first example creates a deadlock situation. If the process writes too much output, it will block waiting for an opportunity to write more output. This will not happen for output below a certain threshold. If it blocks, it is hung forever. [Until killed]Selma
@Selma - I have updated the answer and removed the first code snippet. Furthermore, in the second snippet, I can't see a need for WaitToExit any longer: Once the while loop is left, it means the process has exited. However I am not sure about your claim. That would mean that even in a situation where you are not interested in the processes output you would always have to add a while-ReadToEnd structure wherevere in your code you want to start a process using Process.Start such as to make sure the process does not stand still waiting on "free space".Politi
I verified my claim after the post. Check it and see.Selma
ReadToEnd() is synchronous, and the shell process will stall if you redirect stdout but don't read from it, and the process fills its output buffer. On a related note, if you want both stdout and stderr, you need to use async I/O for one or both. See msdn.microsoft.com/en-us/library/…Gerry
S
5

I know this question is old, but I'll add to it anyway.

If all you wish to do is display the output of a command line process, and you're spawning the process from a console window, you need only redirect the standard input (yes, I know it sounds wrong, but it works).

So:

System.Diagnostics.Process p = new System.Diagnostics.Process();
p.StartInfo = new System.Diagnostics.ProcessStartInfo(ffmpegPath, myParams);
p.UseShellExecute = false;
p.RedirectStandardInput = true;
p.Start();
p.WaitForExit();

Would do just fine.

Scrivens answered 23/7, 2010 at 0:38 Comment(3)
You didn't add much to it, because that was already mentioned in the accepted answer.Flowage
Am I drunk or is Michael Vasquez's name and, really, most of the HTML around his comment all backwards?Lymphatic
This is because after the "wasn't." there are two U+202E (RIGHT-TO-LEFT OVERRIDE) sequences encoded in utf-8, that is 0xe2 0x80 0xae 0xe2 0x80 0xae.Disrate
D
4

For a more specific answer directly related to ffmpeg, passing the "-report" command into ffmpeg will make it dump a log into the current directory with what was said in the display of the process.

‘-report’

Dump full command line and console output to a file named program-YYYYMMDD-HHMMSS.log in the current directory. This file can be useful for bug reports. It also implies -loglevel verbose.

Note: setting the environment variable FFREPORT to any value has the same effect.

From FFMpeg Documentation.

Doane answered 7/5, 2012 at 19:23 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.