process.standardoutput.ReadToEnd() always empty? - c#

I'm starting a console application, but when I redirect the standard output I always get nothing!
When I don't redirect it, and set CreateNoWindow to false, I see everything correctly in the console, but when I redirect it, StandardOutput.ReadToEnd() always returns an empty string.
Process cproc = new Process();
cproc.StartInfo.CreateNoWindow = true;
cproc.StartInfo.FileName = Dest;
cproc.StartInfo.RedirectStandardOutput = true;
cproc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
cproc.StartInfo.UseShellExecute = false;
cproc.EnableRaisingEvents = true;
cproc.Start();
cproc.Exited += new EventHandler(cproc_Exited);
while(!stop)
{
result += cproc.StandardOutput.ReadToEnd();
}
The EventHandler cproc_exited just sets stop to true. Can someone explain why result is always string.Empty?

Best way for this is to redirect the output and wait for the events:
// not sure if all this flags are needed
process.StartInfo.CreateNoWindow = true;
process.StartInfo.ErrorDialog = false;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.RedirectStandardOutput = true;
process.EnableRaisingEvents = true;
process.OutputDataReceived += process_OutputDataReceived;
process.ErrorDataReceived += process_ErrorDataReceived;
process.Exited += process_Exited;
process.Start();
void process_Exited(object sender, System.EventArgs e)
{
// do something when process terminates;
}
void process_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
// a line is writen to the out stream. you can use it like:
string s = e.Data;
}
void process_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
// a line is writen to the out stream. you can use it like:
string s = e.Data;
}

Why are you looping? Once it's read to the end, it's not going to be able to read any more data, is it?
Are you sure the text is actually being written to StandardOutput rather than StandardError?
(And yes, obviously you want to set RedirectStandardOutput to true rather than false. I assumed that was just a case of you copying the wrong version of your code.)
EDIT: As I've advised in the comments, you should read from standard output and standard error in separate threads. Do not wait until the process has exited - this can end up with a deadlock, where you're waiting for the process to exit, but the process is blocking trying to write to stderr/stdout because you haven't read from the buffer.
Alternatively you can subscribe to the OutputDataReceived and ErrorDataReceived events, to avoid using extra threads.

You have redirection of standard out disabled. Try changing
cproc.StartInfo.RedirectStandardOutput = false;
into
cproc.StartInfo.RedirectStandardOutput = true;
Does the following sample from MSDN work for you?
// 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 = "Write500Lines.exe";
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();

Get rid of the loop and move the call to ReadToEnd to cproc_Exited.

Related

Process.Start StandardOutput always empty

I tried lot's of samples posted here and elsewhere that should capture and output the StandardOutput of a process.start command. But no matter if synchronous or asynchronous the output is always empty.
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 = "-ss 00:00:50 -t 240 -i Input.MOV -to 00:00:02 -qscale 0 _OUTPUT.MOV";
processStartInfo.FileName = "ffmpeg.exe";
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();
Process is executed just as intended but there is not output captured nevertheless it's shown in the VS IDE output pane.
Any idea whats going wrong here?
Best Regards.
Thanks Amy, it's correct, the normal output is returned by
BeginErrorReadLine

C# Do I need to read stdout on separate thread?

In C#, I am trying to run a command line program that gets input from stdin and then it returns the output to stdout . I need to keep cmd.exe running (using /k) and in a for loop, send in text and then wait for the output before sending the next text. This works if I don't redirect stdout but not after I redirect it. Initially I got data back from stdout (although much later) and now that is no longer working other than the initial call to start the program. This link says "Alternately, you can avoid the deadlock condition by creating two threads and reading the output of each stream on a separate thread."
Would that fix my issue and if so, how would I do that?
The code is set up as follows:
StringBuilde sb = new StringBuilder();
Process process = new Process();
process.StartInfo.RedirectStandardError = true;
process.StartInfo.WorkingDirectory = workingdirectory;
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = "cmd.exe";
process.StartInfo.Arguments = "/k";
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardInput = true;
process.EnableRaisingEvents = true;
process.OutputDataReceived += (sender, args) => AppendData(args.Data); //this appends the output to a stringbuilder
process.ErrorDataReceived += (sender, args) => AppendError(args.Data);
process.Start();
//sw is a streamwriter
sw= process.StandardInput;
//now call the command line code
sw.WriteLine(" some.exe some.arg ");
process.BeginOutputReadLine();
foreach(DataRow row in dtMydata.Rows)
{
mytext=row["Text"].toString();
sw.WriteLine(mytext);
sw.WriteLine(Environment.NewLine); //This redirects the text to the program,
}

Is there any cmd callback?

Obviously I can execute something with cmd console using Process.Start();
Is there any way to get output of that process? For example, I could have something like...
Process.Start("sample.bat");
... in my C# winforms application and sample.bat will contain something like:
echo sample loaded
First Question: is there any way to retrieve that sample loaded, after bat execution?
Second question: is there a way to use it without popped up console window?
There is an example of exactly how to do this in the Process documentation:
// 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 = "Write500Lines.exe";
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();
Yes, you can use
Process.Start(ProcessStartInfo)
There are a few ways to hook into I/O including ProcessStartInfo.RedirectStandardOutput available. You can use these overloads to read output from your batch files. You can also hook into the Exited event to know when execution is complete.
Use CreateNoWindow for no window.
Set process.StartInfo.RedirectStandardOutput to true and subscribe to process.OutputDataReceived
using (var process = new Process())
{
process.StartInfo = new ProcessStartInfo("exename");
process.StartInfo.RedirectStandardOutput = true;
process.OutputDataReceived += (s, ev) =>
{
string output = ev.Data;
};
process.Start();
process.BeginOutputReadLine();
process.WaitForExit();
}

Process RedirectStandardOutput empty and DataReceivedEventArgs.Data is null

When running an application in console mode there are lines written to the console.
Now I want to do this programmatically. Here is some example code that I used: MSDN on OutputDataReceived Event
private static StringBuilder _sortOutput = null;
var proc = new Process();
var info = new ProcessStartInfo();
info.FileName = #"C:\SomeApp.exe";
info.UseShellExecute = false;
info.WindowStyle = ProcessWindowStyle.Normal;
info.CreateNoWindow = false;
proc.StartInfo = info;
info.RedirectStandardOutput = true;
info.RedirectStandardError = true;
proc.OutputDataReceived += HandleOutputData;
proc.ErrorDataReceived += HandleOutputData;
proc.Start();
proc.BeginOutputReadLine();
proc.BeginErrorReadLine();
proc.WaitForExit();
var exitCode = proc.ExitCode;
var output = _sortOutput.ToString();
private void HandleOutputData(object sender, DataReceivedEventArgs e)
{
_sortOutput.Append(e.Data);
}
But the DataReceivedEventArgs.Data is always null and I do not get back the result which I can see in the console window that opens.
How can I receive the output? Is there a way to achieve that?
UPDATE
I also tried to read the proc.StandardOutput directly, but it does not yield any data.
As someone else has spotted, there is a bug where _sortOutput will be null. But regardless of that, you are completely correct: e.Data can and will be null!
In fact, your handler should always get called with e.Data equal to null when the AsyncStreamReader that is doing the calling reaches EOF of the standard output that is being redirected - which will happen when the process you are running exits. Unless, of course, you deregister your OutputDataReceived event handler first, or cancel asynchronous output redirection.
It's null, because you read the data in var outPut
Try following:
a) Change the way you handle it
proc.OutputDataReceived += new DataReceivedEventHandler(HandleOutputData);
b)Comment out the line
//var outPut = proc.StandardOutput.ReadToEnd();
Null signifies the end of the asynchronous stream.
You must both WaitForExit and wait for the terminating null to come through e.Data

Redirect console output to textbox in separate program

I'm developing an Windows Forms application that requires me to call a separate program to perform a task. The program is a console application and I need to redirect standard output from the console to a TextBox in my program.
I have no problem executing the program from my application, but I don't know how to redirect the output to my application. I need to capture output while the program is running using events.
The console program isn't meant to stop running until my application stops and the text changes constantly at random intervals. What I'm attempting to do is simply hook output from the console to trigger an event handler which can then be used to update the TextBox.
I am using C# to code the program and using the .NET framework for development. The original application is not a .NET program.
EDIT:
Here's example code of what I'm trying to do. In my final app, I'll replace Console.WriteLine with code to update the TextBox. I tried to set a breakpoint in my event handler, and it isn't even reached.
void Method()
{
var p = new Process();
var path = #"C:\ConsoleApp.exe";
p.StartInfo.FileName = path;
p.StartInfo.UseShellExecute = false;
p.OutputDataReceived += p_OutputDataReceived;
p.Start();
}
static void p_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine(">>> {0}", e.Data);
}
This works for me:
void RunWithRedirect(string cmdPath)
{
var proc = new Process();
proc.StartInfo.FileName = cmdPath;
// set up output redirection
proc.StartInfo.RedirectStandardOutput = true;
proc.StartInfo.RedirectStandardError = true;
proc.EnableRaisingEvents = true;
proc.StartInfo.CreateNoWindow = true;
// see below for output handler
proc.ErrorDataReceived += proc_DataReceived;
proc.OutputDataReceived += proc_DataReceived;
proc.Start();
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();
proc.WaitForExit();
}
void proc_DataReceived(object sender, DataReceivedEventArgs e)
{
// output will be in string e.Data
}
You can use the following code
MemoryStream mem = new MemoryStream(1000);
StreamWriter writer = new StreamWriter(mem);
Console.SetOut(writer);
Assembly assembly = Assembly.LoadFrom(#"C:\ConsoleApp.exe");
assembly.EntryPoint.Invoke(null, null);
writer.Close();
string s = Encoding.Default.GetString(mem.ToArray());
mem.Close();
I've added a number of helper methods to the O2 Platform (Open Source project) which allow you easily script an interaction with another process via the console output and input (see http://code.google.com/p/o2platform/source/browse/trunk/O2_Scripts/APIs/Windows/CmdExe/CmdExeAPI.cs)
Also useful for you might be the API that allows the viewing of the console output of the current process (in an existing control or popup window). See this blog post for more details: http://o2platform.wordpress.com/2011/11/26/api_consoleout-cs-inprocess-capture-of-the-console-output/ (this blog also contains details of how to consume the console output of new processes)
Thanks to Marc Maxham for his answer that save me time !
As Jon of All Trades notice it, UseShellExecute must be set to false in order to redirect IO streams, otherwise the Start() call throws an InvalidOperationException.
Here is my modification of the code where txtOut is a WPF readonly Textbox
void RunWithRedirect(string cmdargs)
{
var proc = new Process()
{
StartInfo = new ProcessStartInfo("cmd.exe", "/k " + cmdargs)
{
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
},
EnableRaisingEvents = true
};
// see below for output handler
proc.ErrorDataReceived += proc_DataReceived;
proc.OutputDataReceived += proc_DataReceived;
proc.Start();
proc.BeginErrorReadLine();
proc.BeginOutputReadLine();
proc.WaitForExit();
}
void proc_DataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
Dispatcher.BeginInvoke(new Action( () => txtOut.Text += (Environment.NewLine + e.Data) ));
}

Categories

Resources