C# get process output while running - c#

Is there anyway to redirect standard output of a spawned process and capture it as its happening. Everything I have seen just does a ReadToEnd after the process has finished. I would like to be able to get the output as it is being printed.
Edit:
private void ConvertToMPEG()
{
// Start the child process.
Process p = new Process();
// Redirect the output stream of the child process.
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
//Setup filename and arguments
p.StartInfo.Arguments = String.Format("-y -i \"{0}\" -target ntsc-dvd -sameq -s 720x480 \"{1}\"", tempDir + "out.avi", tempDir + "out.mpg");
p.StartInfo.FileName = "ffmpeg.exe";
//Handle data received
p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
p.Start();
}
void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Debug.WriteLine(e.Data);
}

Use Process.OutputDataReceived event from the process, to recieve the data you need.
Example:
var myProc= new Process();
...
myProc.StartInfo.RedirectStandardOutput = true;
myProc.OutputDataReceived += new DataReceivedEventHandler(MyProcOutputHandler);
...
private static void MyProcOutputHandler(object sendingProcess,
DataReceivedEventArgs outLine)
{
// Collect the sort command output.
if (!String.IsNullOrEmpty(outLine.Data))
{
....
}
}

So after a little more digging I found out that ffmpeg uses stderr for output. Here is my modified code to get the output.
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.Arguments = String.Format("-y -i \"{0}\" -target ntsc-dvd -sameq -s 720x480 \"{1}\"", tempDir + "out.avi", tempDir + "out.mpg");
p.StartInfo.FileName = "ffmpeg.exe";
p.ErrorDataReceived += new DataReceivedEventHandler(p_ErrorDataReceived);
p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
p.Start();
p.BeginErrorReadLine();
p.WaitForExit();

Related

PsExec why don't I get full output of remote command

Why do I NOT get 3 lines of output from running TestApp remotely? In string 'output' I get license info plus "Start#" line, BUT not the next two lines:
public static string RunPsExec()
{
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.FileName = #"C:\Users\Vence\Downloads\PSTools\PsExec.exe";
p.StartInfo.Arguments = #"\\10.215.yy.yy -u xxxxxx -p ""xxxxxx"" C:\Projects\TestApp\TestApp\bin\Debug\TestApp.exe";
p.Start();
string output = p.StandardOutput.ReadToEnd();
string errormessage = p.StandardError.ReadToEnd();
p.WaitForExit();
return output;
}
TestApp:
static void Main(string[] args)
{
Console.WriteLine("Start# " + DateTime.Now.ToShortTimeString());
System.Threading.Thread.Sleep(5000);
Console.WriteLine("Middle# " + DateTime.Now.ToShortTimeString());
System.Threading.Thread.Sleep(5000);
Console.WriteLine("End# " + DateTime.Now.ToShortTimeString());
}
One solution that I see is set RedirectStandardInput to false, since it is not needed in this example.
However, if you would like to pass the input to the TestApp in a future (which requires RedirectStandardInput=true), one solution might be to use PAexec (https://www.poweradmin.com/paexec/) as an alternative to PsExec. According to these question (Can't receive asynchronous output from psexec when launching application remotely), PAExec writes directly to console buffer, instead of stout/sterr. To make it work, you would have to read the output asynchronously, like this:
public static string RunPsExec()
{
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.FileName = #"C:\<path_to>\PaExec.exe";
p.StartInfo.Arguments = #\\10.215.yy.yy -u xxxxxx -p ""xxxxxx"" ""C:\Projects\TestApp\TestApp\bin\Debug\TestApp.exe"" ";
p.OutputDataReceived += (sender, args) => Display(args.Data);
p.Start();
p.BeginOutputReadLine();
p.WaitForExit(10000);
return;
}
static void Display(string output)
{
Console.WriteLine(output);
}

Capture output from Powershell version 2

I am having trouble capturing the output from Powershell version 2.
I use the regular Process class to run the program and handle input and output and everything works great. Working code below:
using (Process p = new Process())
{
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.FileName = "powershell";
//p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.OutputDataReceived += (sender, args) => AddRow(args.Data);
p.ErrorDataReceived += (sender, args) => AddRow("ERR: " + args.Data);
p.Start();
ps = p.StandardInput;
p.BeginOutputReadLine();
p.BeginErrorReadLine();
Thread.Sleep(1000);
ps.WriteLine("dir");
Thread.Sleep(100);
while (true)
{
Thread.Sleep(10);
}
}
This version starts powershell and sends "dir" to stdinput of the process and the result is sent back to stdout. I can see the cmd-window but i cant write or see any output there as expected. (I know i can hide it).
But then i add this row:
p.StartInfo.Arguments = "-version 2";
This is to start powershell in version 2 and now it just prints 2 rows that tells me powershell is started and then the connection is lost.
I see in the console window that the cursor is blinking and now i can interact with powershell in the command window since the connection to my process class is lost. When i enter "exit" in the window i am back again to my process.
How can i solve this?
Full code (when using version 2 that is not working) below:
using (Process p = new Process())
{
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.FileName = "powershell";
p.StartInfo.Arguments = "-version 2";
//p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.OutputDataReceived += (sender, args) => AddRow(args.Data);
p.ErrorDataReceived += (sender, args) => AddRow("ERR: " + args.Data);
p.Start();
ps = p.StandardInput;
p.BeginOutputReadLine();
p.BeginErrorReadLine();
Thread.Sleep(1000);
ps.WriteLine("dir");
Thread.Sleep(100);
while (true)
{
Thread.Sleep(10);
}
}
I hope someone can help me with this! Thanks for your time!

Run command line command passing STDIN [duplicate]

This question already has answers here:
Capturing console output from a .NET application (C#)
(8 answers)
Closed 6 years ago.
I need to spawn a child process that is a console application, and capture its output.
I wrote up the following code for a method:
string retMessage = String.Empty;
ProcessStartInfo startInfo = new ProcessStartInfo();
Process p = new Process();
startInfo.CreateNoWindow = true;
startInfo.RedirectStandardOutput = true;
startInfo.RedirectStandardInput = true;
startInfo.UseShellExecute = false;
startInfo.Arguments = command;
startInfo.FileName = exec;
p.StartInfo = startInfo;
p.Start();
p.OutputDataReceived += new DataReceivedEventHandler
(
delegate(object sender, DataReceivedEventArgs e)
{
using (StreamReader output = p.StandardOutput)
{
retMessage = output.ReadToEnd();
}
}
);
p.WaitForExit();
return retMessage;
However, this does not return anything. I don't believe the OutputDataReceived event is being called back, or the WaitForExit() command may be blocking the thread so it will never callback.
Any advice?
EDIT: Looks like I was trying too hard with the callback. Doing:
return p.StandardOutput.ReadToEnd();
Appears to work fine.
Here's code that I've verified to work. I use it for spawning MSBuild and listening to its output:
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += (sender, args) => Console.WriteLine("received output: {0}", args.Data);
process.Start();
process.BeginOutputReadLine();
I just tried this very thing and the following worked for me:
StringBuilder outputBuilder;
ProcessStartInfo processStartInfo;
Process process;
outputBuilder = new StringBuilder();
processStartInfo = new ProcessStartInfo();
processStartInfo.CreateNoWindow = true;
processStartInfo.RedirectStandardOutput = true;
processStartInfo.RedirectStandardInput = true;
processStartInfo.UseShellExecute = false;
processStartInfo.Arguments = "<insert command line arguments here>";
processStartInfo.FileName = "<insert tool path here>";
process = new Process();
process.StartInfo = processStartInfo;
// enable raising events because Process does not raise events by default
process.EnableRaisingEvents = true;
// attach the event handler for OutputDataReceived before starting the process
process.OutputDataReceived += new DataReceivedEventHandler
(
delegate(object sender, DataReceivedEventArgs e)
{
// append the new data to the data already read-in
outputBuilder.Append(e.Data);
}
);
// start the process
// then begin asynchronously reading the output
// then wait for the process to exit
// then cancel asynchronously reading the output
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
process.CancelOutputRead();
// use the output
string output = outputBuilder.ToString();
Here's some full and simple code to do this. This worked fine when I used it.
var processStartInfo = new ProcessStartInfo
{
FileName = #"C:\SomeProgram",
Arguments = "Arguments",
RedirectStandardOutput = true,
UseShellExecute = false
};
var process = Process.Start(processStartInfo);
var output = process.StandardOutput.ReadToEnd();
process.WaitForExit();
Note that this only captures standard output; it doesn't capture standard error. If you want both, use this technique for each stream.
I needed to capture both stdout and stderr and have it timeout if the process didn't exit when expected. I came up with this:
Process process = new Process();
StringBuilder outputStringBuilder = new StringBuilder();
try
{
process.StartInfo.FileName = exeFileName;
process.StartInfo.WorkingDirectory = args.ExeDirectory;
process.StartInfo.Arguments = args;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.EnableRaisingEvents = false;
process.OutputDataReceived += (sender, eventArgs) => outputStringBuilder.AppendLine(eventArgs.Data);
process.ErrorDataReceived += (sender, eventArgs) => outputStringBuilder.AppendLine(eventArgs.Data);
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
var processExited = process.WaitForExit(PROCESS_TIMEOUT);
if (processExited == false) // we timed out...
{
process.Kill();
throw new Exception("ERROR: Process took too long to finish");
}
else if (process.ExitCode != 0)
{
var output = outputStringBuilder.ToString();
var prefixMessage = "";
throw new Exception("Process exited with non-zero exit code of: " + process.ExitCode + Environment.NewLine +
"Output from process: " + outputStringBuilder.ToString());
}
}
finally
{
process.Close();
}
I am piping the stdout and stderr into the same string, but you could keep it separate if needed. It uses events, so it should handle them as they come (I believe). I have run this successfully, and will be volume testing it soon.
It looks like two of your lines are out of order. You start the process before setting up an event handler to capture the output. It's possible the process is just finishing before the event handler is added.
Switch the lines like so.
p.OutputDataReceived += ...
p.Start();
Redirecting the stream is asynchronous and will potentially continue after the process has terminated. It is mentioned by Umar to cancel after process termination process.CancelOutputRead(). However that has data loss potential.
This is working reliably for me:
process.WaitForExit(...);
...
while (process.StandardOutput.EndOfStream == false)
{
Thread.Sleep(100);
}
I didn't try this approach but I like the suggestion from Sly:
if (process.WaitForExit(timeout))
{
process.WaitForExit();
}
You need to call p.Start() to actually run the process after you set the StartInfo. As it is, your function is probably hanging on the WaitForExit() call because the process was never actually started.
The answer from Judah did not work for me (or is not complete) as the application was exiting after the first BeginOutputReadLine();
This works for me as a complete snippet, reading the constant output of a ping:
var process = new Process();
process.StartInfo.FileName = "ping";
process.StartInfo.Arguments = "google.com -t";
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.UseShellExecute = false;
process.OutputDataReceived += (sender, a) => Console.WriteLine(a.Data);
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
Here's a method that I use to run a process and gets its output and errors :
public static string ShellExecute(this string path, string command, TextWriter writer, params string[] arguments)
{
using (var process = Process.Start(new ProcessStartInfo { WorkingDirectory = path, FileName = command, Arguments = string.Join(" ", arguments), UseShellExecute = false, RedirectStandardOutput = true, RedirectStandardError = true }))
{
using (process.StandardOutput)
{
writer.WriteLine(process.StandardOutput.ReadToEnd());
}
using (process.StandardError)
{
writer.WriteLine(process.StandardError.ReadToEnd());
}
}
return path;
}
For example :
#"E:\Temp\MyWorkingDirectory".ShellExecute(#"C:\Program Files\Microsoft SDKs\Windows\v6.0A\Bin\svcutil.exe", Console.Out);

Reading cmd output real time

I'm writing a program that reads python script output and shows the results in textbox.
Since the script runnning for a long time, I want to be able to see the output every 1 second (or after each line is writen).
Now i can see the output only when the process ends.
Does someone know what is the problem?
snippet of my code:
Process p = new Process();
p.StartInfo.CreateNoWindow = true;
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.OutputDataReceived += new DataReceivedEventHandler (p_OutputDataReceived);
p.ErrorDataReceived += new DataReceivedEventHandler (p_ErrorDataReceived);
p.Exited += new EventHandler (p_Exited);
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = "python.exe";
p.StartInfo.Arguments = "path " + commandline;
p.Start();
StreamReader s = p.StandardOutput;
String output = s.ReadToEnd();
textBox3.Text = output;
p.WaitForExit();
I'm doing it the following way in my own programs:
private static void startProgram(
string commandLine )
{
var fileName = commandLine;
var arguments = string.Empty;
checkSplitFileName( ref fileName, ref arguments );
var info = new ProcessStartInfo();
info.FileName = fileName;
info.Arguments = arguments;
info.UseShellExecute = false;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
using ( var p = new Process() )
{
p.StartInfo = info;
p.EnableRaisingEvents = true;
p.OutputDataReceived += (s,o) => {
Console.WriteLine(o.Data);
};
p.Start();
p.BeginOutputReadLine();
p.WaitForExit();
}
}
I.e. I'm subscribing to the OutputDataReceived event and calling BeginOutputReadLine method. See also this similar Stack Overflow question.
(The method checkSplitFileName in my above source code can be found here)
I had this same problem running my Python script from C#. The problem is that Python buffers the output from stdout (print()).
You could do one of two things here.
1.
Add the following to your Python script, after every print() line to flush the output.
import sys
print('Hello World!')
sys.stdout.flush()
2.
Run the Python compiler with the -u command line parameter. This way you don't need to add the above flush line after every print.
...
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.Arguments = "python.exe -u path " + commandline;
...
Python, by default, buffers its output.
The way to go is to pass a "-u" command line argument to python.
so if you want to execute say hello.py, you would do :
python.exe -u hello.py
Heres the C# code that works for me.
Process p = new Process();
string op = "";
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.FileName = "c:\\python27\\python.exe";
StreamReader outputStream = p.StandardOutput;
StreamReader errorStream = p.StandardError;
p.StartInfo.Arguments = #"-u hello.py";
p.Start();
string output = "";
int offset = 0, readBytes = 0;
char[] buffer = new char[512];
do
{
output = outputStream.ReadLine();
if (!string.IsNullOrEmpty(output))
{
txtOutput.AppendText(output);
txtOutput.AppendText(Environment.NewLine);
offset += readBytes;
Application.DoEvents();
}
Thread.Sleep(3);
} while (!p.HasExited);

Sox returns some value but StandardOutput.ReadToEnd() returns empty

try
{
string filename = "E:\\sox-14-4-0\\mysamplevoice.wav";
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.FileName = "E:\\sox-14-4-0\\sox.exe ";
p.StartInfo.Arguments = filename + " -n stat";
p.Start();
string output = p.StandardOutput.ReadToEnd();
p.WaitForExit();
}
catch(Exception Ex)
{
Console.WriteLine(Ex.Message);
}
The output is always empty. When I run that sox command in command prompt I could get a response like:
E:\sox-14-4-0>sox mysamplevoice.wav -n stat
Samples read: 26640
Length (seconds): 3.330000
Scaled by: 2147483647.0
Maximum amplitude: 0.515625
Minimum amplitude: -0.734375
Midline amplitude: -0.109375
Mean norm: 0.058691
Mean amplitude: 0.000122
RMS amplitude: 0.101146
Maximum delta: 0.550781
Minimum delta: 0.000000
Mean delta: 0.021387
RMS delta: 0.041831
Rough frequency: 526
Volume adjustment: 1.362
When running the same command in C# I get the same result but value of "output" is empty.
Are you sure sox.exe writes to STDOUT and not to STDERR ?
you could try reading the data using the OutputDataReceived event instead.
string filename = "E:\\sox-14-4-0\\mysamplevoice.wav";
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.FileName = "E:\\sox-14-4-0\\sox.exe ";
p.StartInfo.Arguments = filename + " -n stat";
p.OutputDataReceived += process_OutputDataReceived;
p.ErrorDataReceived += process_ErrorDataReceived;
p.Start();
p.BeginErrorReadLine();
p.BeginOutputReadLine();
void process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
string s = e.Data;
}
void process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
string s = e.Data;
}
I just ran into this problem too. Why does SoX write to StandardError?!
In case someone else runs into this problem too, the solution to the original question could just be adding 2 lines
p.StartInfo.RedirectStandardError = true; // <-- this
...
string output = p.StandardOutput.ReadToEnd();
if(output == "") output = p.StandardError.ReadToEnd(); // <-- and this

Categories

Resources