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

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,
}

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

Capture ALL output when running batch file in c#

There are numerous examples out there which describe how to capture the standard and error output when running a batch file in c#.
I am following those instructions and while it captures MOST of the output it doesn't capture all.
And I can't find any explanation or help in dealing with this.
For example I have to run a command via a batch file to generate a PDF from Tableau. All the resulting outputs are captured correctly including confirmation of login to the server, confirmation of which PDF will be generated etc. However when an error occurs the error is not captured. However if I run the batch file manually (ie not via code) I can see the error message in the console.
See screenshot below....you can see the command line calls and the server responses. But the most important information, the error message (highlighted) is not being captured when running via code.
Can anyone see anything to add to the following code snippet that would capture this information? Or a reason why its not being captured??
System.Diagnostics.ProcessStartInfo procStartInfo = new
System.Diagnostics.ProcessStartInfo(batchFilePath + batchFileName);
procStartInfo.RedirectStandardOutput = true;
procStartInfo.RedirectStandardError = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
Process p = new Process();
p.EnableRaisingEvents = true;
p.StartInfo = procStartInfo;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.UseShellExecute = false;
StringBuilder sb = new StringBuilder();
p.OutputDataReceived += (sender, args) => sb.AppendLine(args.Data);
p.ErrorDataReceived += (sender, args) => sb.AppendLine(args.Data); ;
p.Exited += Exited;
p.Start();
p.BeginOutputReadLine();
Appreciate any help on this!
Thanks
Probably missing:
p.BeginErrorReadLine();
And note that sb.AppendLine(args.Data); is not thread safe, consider synchronizing the access

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();
}

Showing output of a running cmd process

I have a C# code that uses command prompt to call a python script. The script currently takes about 25 seconds to run. When run in command prompt, the python script has several outputs until finally outputting "done". My C# code runs the script, but never closes the command prompt if I use "WaitForExit". So my thought is I need some kind of logic to check to see if "done" has been outputted, but the internet has not been very helpful with methodology.
Here's what I have, it currently only outputs the first line, "Microsoft Windows [Version 6.1.7601]". Obviously, no good.
var p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.WorkingDirectory = #"C:\Optimization";
p.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardInput = true;
p.Start();
p.StandardInput.WriteLine(#"ipy spot_for_labview.py");
StreamReader mySR = p.StandardOutput;
string mystr = mySR.ReadLine();
Debug.WriteLine(mystr);
p.WaitForExit(25000); //25000 is a placeholder until better method found.
p.Close();
If there's anyway to close the process after it finishes, or to get all the cmd output I'm all ears.
Any help is appreciated.
did you try this event ? Process.OutputDataReceived Event
or Process.ErrorDataReceived Event
here is the code from MSDN
Process sortProcess;
sortProcess = new Process();
sortProcess.StartInfo.FileName = "Sort.exe";
sortProcess.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
...
....
...
private static void SortOutputHandler(object sendingProcess,
DataReceivedEventArgs outLine)
{
}

process.standardoutput.ReadToEnd() always empty?

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.

Categories

Resources