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
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();
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());
// ...
}
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.
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.
Related
I am currently working on a C# Program which needs to call a local PHP script and write its output to a file. The problem is, that I need to be able to stop the execution of the script.
First, I tried to call cmd.exe and let cmd write the output to the file which worked fine. But I found out, that killing the cmd process does not stop the php cli.
So I tried to call php directly, redirect its output and write it from the C# code to a file. But here the problem seems to be, that the php cli does not terminate when the script is done. process.WaitForExit() does not return, even when I am sure that the script has been fully executed.
I cannot set a timeout to the WaitForExit(), because depending on the arguments, the script may take 3 minutes or eg. 10 hours.
I do not want to kill just a random php cli, there may be others currently running.
What is the best way to call a local php script from C#, writing its output to a file and beeing able to stop the execution?
Here is my current code:
// Create the process
var process = new System.Diagnostics.Process();
process.EnableRaisingEvents = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = "php.exe";
// CreateExportScriptArgument returns something like "file.php arg1 arg2 ..."
process.StartInfo.Arguments = CreateExportScriptArgument(code, this.content, this.options);
process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
process.StartInfo.RedirectStandardOutput = true;
// Start the process or cancel, if the process should not run
if (!this.isRunning) { return; }
this.currentProcess = process;
process.Start();
// Get the output
var output = process.StandardOutput;
// Wait for the process to finish
process.WaitForExit();
this.currentProcess = null;
To kill the process I am using:
// Mark as not running to prevent starting new
this.isRunning = false;
// Kill the process
if (this.currentProcess != null)
{
this.currentProcess.Kill();
}
Thanks for reading!
EDIT
That the cli does not return seems to be not reproducible. When I test a different script (without arguments) it works, probably its the script or the passing of the arguments.
Running my script from cmd works just fine, so the script should not be the problem
EDIT 2
When disabling RedirectStandardOutput, the cli quits. could it be, that I need to read the output, before the process finishes? Or does the process wait, when some kind of buffer is full?
EDIT 3: Problem solved
Thanks to VolkerK, I / we found a solution. The problem was, that WaitForExit() did not get called, when the output is not read (probably due to a full buffer in the standard output). My script wrote much output.
What works for me:
process.Start();
// Get the output
var output = process.StandardOutput;
// Read the input and write to file, live to avoid reading / writing to much at once
using (var file = new StreamWriter("path\\file", false, new UTF8Encoding()))
{
// Read each line
while (!process.HasExited)
{
file.WriteLine(output.ReadLine());
}
// Read the rest
file.Write(output.ReadToEnd());
// flush to file
file.Flush();
}
Since the problem was that the output buffer was full and therefore the php process stalled while waiting to send its output, asynchronously reading the output in the c# program is the solution.
class Program {
protected static /* yeah, yeah, it's only an example */ StringBuilder output;
static void Main(string[] args)
{
// Create the process
var process = new System.Diagnostics.Process();
process.EnableRaisingEvents = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = "php.exe";
process.StartInfo.Arguments = "-f path\\test.php mu b 0 0 pgsql://user:pass#x.x.x.x:5432/nominatim";
process.StartInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
process.StartInfo.RedirectStandardOutput = true;
output = new StringBuilder();
process.OutputDataReceived += process_OutputDataReceived;
// Start the process
process.Start();
process.BeginOutputReadLine();
// Wait for the process to finish
process.WaitForExit();
Console.WriteLine("test");
// <-- do something with Program.output here -->
Console.ReadKey();
}
static void process_OutputDataReceived(object sender, System.Diagnostics.DataReceivedEventArgs e)
{
if (!String.IsNullOrEmpty(e.Data)) {
// edit: oops the new-line/carriage-return characters are not "in" e.Data.....
// this _might_ be a problem depending on the actual output.
output.Append(e.Data);
output.Append(Environment.NewLine);
}
}
}
see also: https://msdn.microsoft.com/en-us/library/system.diagnostics.process.beginoutputreadline%28v=vs.110%29.aspx
Working on a project. In the code, we need to run powershell script and then get its output. In order to do this, I use the Process():
private int RunProcess(string FileName, string Arguments, out string result)
{
int exitCode = -1;
result = string.Empty;
// Start the child process.
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = FileName;
p.StartInfo.Arguments = Arguments;
p.Start();
// Do not wait for the child process to exit before
// reading to the end of its redirected stream.
// Read the output stream first and then wait.
result = p.StandardOutput.ReadToEnd();
// Wait at most 10 minutes
p.WaitForExit(10 * 60 * 1000);
exitCode = p.ExitCode;
return exitCode;
}
and call it like this:
RunProcess("Powershell.exe", arguments, out sPSResult);
This works fine on most computers. However, on some, for some unknow reason, the RunProcess() never return, even we use p.WaitForExit(10 * 60 * 1000) .
Anyone knows why? or see this before? Is it because somewhere is blocked in the windows even WaitForExit is used?
Thanks
Are you sure your code even reaches WaitForExit and is not hanging on ReadToEnd? My guess it's getting stuck there because it can't read all of the output. Also for details on how to deal with reading standard and error outputs from child processes correctly, see:
ProcessStartInfo hanging on "WaitForExit"? Why?
The specific problem I am seeing when executing a cmd process with something like "del *.txt" where one of the 'txt' files is open and cannot be deleted, the cmd process will output a line of text (saying something like 'file in use, cannot delete file') to the console, but not to the StandardOutput or the StandardError. According to this question [ https://stackoverflow.com/a/320779/832705 ] from 2008, the answer is no, but I am wondering if that might have changed in the past 4 years, or if someone has since found a workaround way. Also, I might be misinterpreting that answer, it might mean CLR exceptions and not cmd exceptions.
here is my process setup/start code:
ProcessStartInfo psi = new ProcessStartInfo("cmd", string.Empty);
psi.CreateNoWindow = true;
psi.ErrorDialog = false;
psi.RedirectStandardError = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = true;
psi.UseShellExecute = false;
Process p = new Process();
p.StartInfo = psi;
p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
p.ErrorDataReceived += new DataReceivedEventHandler(p_ErrorDataReceived);
outputfilesw = new StreamWriter(outputfile, true);
try
{
p.Start();
p.BeginOutputReadLine();
//work code
}
You just have to call p.BeginErrorReadLine() to start the asynchronous read of StandardError. Answer added at suggestion of OP.
You can read the output, and you can process the text returned. So, you should be able to find the text that indicates an error, even if it doesn't land in the error output.
Also, it is important to note that only the process being run can determine which output stream gets a message. So, if the command you're using decides to send errors to the standard stream, no amount of OS or C# work will change that.
I have tried to redirect the command prompt output to a file using Asp.Net C#.
System.Diagnostics.Process si = new System.Diagnostics.Process();
si.StartInfo.WorkingDirectory = "c:\\";
si.StartInfo.UseShellExecute = false;
si.StartInfo.FileName = "cmd.exe";
si.StartInfo.Arguments = #"/c dir" +">" + #"Myval.txt";
si.StartInfo.CreateNoWindow = true;
si.StartInfo.RedirectStandardInput = true;
si.StartInfo.RedirectStandardOutput = true;
si.StartInfo.RedirectStandardError = true;
si.Start();
string output = si.StandardOutput.ReadToEnd();
Response.Write(output);
si.Close();
The file is getting created successfully but no content present in it.
Even the variable Output returns nothing.
Help me to resolve this issue.
EDIT after being corrected:
I just tested on my machine and the code works perfectly. I apologize for not reading and testing carefully myself. Myval.txt is created and the DIR output is written into it.
The output variable is empty because you are rerouting any output by the DIR command into the txt file, so that's by design.
Please see if there are any locks on the txt file preventing it from being overwritten. Further than that, I can only guess that there is a security issue preventing the DIR command from running.
IIS7 - I tested this various ways including using a Batch file but the application isn't available on desktop. I can see the worker process and the exe running under my user name but with session id value of zero.
The following has worked for me through command prompt:
// Start the child process.
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "YOURBATCHFILE.bat";
p.Start();
// Do not wait for the child process to exit before
// reading to the end of its redirected stream.
// p.WaitForExit();
// Read the output stream first and then wait.
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
When working with a command line program, via a c# class method.
How do you determine if the commandline program was successfully executed and the operation it has performed is ok or has failed?
Also how do you get the screen commandline output into the c# class method?
You can use the Process class to execute a command line command.
The following code captures the standard output to output, and assigns the processes exit code to exitCode.
using (Process p = new Process())
{
p.StartInfo.FileName = exeName;
p.StartInfo.Arguments = args;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
int exitCode = p.ExitCode;
}
Something like:
Process mycommand = new Process();
mycommand.StartInfo.FileName = "myexe.exe";
mycommand.StartInfo.Arguments = "param1";
mycommand.StartInfo.UseShellExecute = false;
mycommand.StartInfo.RedirectStandardOutput = true;
mycommand.Start();
Console.WriteLine(mycommand.StandardOutput.ReadToEnd());
mycommand.WaitForExit();
You usually determine an exe's state wether the exit code is 0, but that is arguably down to the writer of the exe
I assume you're using the Process class to call the command line app.
You can find the exit code of the process using Process.ExitCode. You can redirect its standard output by setting ProcessStartInfo.RedirectStandardOutput before starting it, and then either using Process.StandardOutput or the Process.OutputDataReceived event.
Take a look at this questionenter link description here.
The additional information you might need is process.ExitCode to see if it was sucessful. Of course, the Main method of the console app must return an exit code when it is unsuccessful, which many do not.
For this, you use the Process.Start method. You can control how the process runs with the passed in ProcessStartInfo:
var myProcess = Process.Start(new ProcessStartInfo {
FileName = "process.exe",
UseShellExecute = false,
RedirectStandardOutput = true,
CreateNoWindow = true
});
if (!myProcess.WaitForExit(5000)) { // give it 5 seconds to exit
myProcess.Kill();
}
if (myProcess.ExitCode != 0) {
// error!
}
var output = myProcess.StandardOutput.ReadToEnd(); // access output