process not outputting it's last row C# - c#

I'm trying To run Plink via C#
using this code
public void RunProcess(string FileName, string Arguments, bool EventWhenExit , bool IsWaitBeforeStart = true)
{
process = new Process();
process.OutputDataReceived += new DataReceivedEventHandler(OnDataReceivedEvent);
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardInput = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.FileName = FileName; // Gets or sets the application or document to start.
process.StartInfo.Arguments = Arguments;//Gets or sets the set of command-line arguments to use when starting the application
if (IsWaitBeforeStart) Thread.Sleep(5000);
if (EventWhenExit)
{
process.EnableRaisingEvents = true;
process.Exited += new EventHandler(myprocess_Exited);
}
process.Start();
process.BeginOutputReadLine();
PID = process.Id;
ProcessInputStream = process.StandardInput;
}
When I run Plink (using this arg : -telnet #IPAddr)
I notice that last row only fires when I close the process .
My guess is that it keeps the last row until last row signal or something like that fired
How can make the process (not only Plink of-course ) to fire every thing it has in it's buffer and not when special signal like fired (like process exit or new line )

BeginOutputReadLine() waits for a newline character or the end of the stream. Unfortunately there is no BeginOutputRead() method that provides the behavior you desire. You do have access to process.StandardOutput though (a StreamReader), on which Read() operations do return whenever data is available.
Currently a line in a console application is "finished" as soon as it is terminated by a newline character. Since for this last line no newline character was outputted yet, you'd have to somehow determine whether it has finished or not. Consider the following example:
Console.Write("This is the ");
Console.Write(" plink output ");
Console.Write(" that I'm trying to read");
LongRunningActivity();
Console.Write(".");
Console.WriteLine();
You could receive these segments of data separately. When is the line finished? Before LongRunningActivity()?
So when attempting to read data before a newline character, you'll have to think of some rules to determine whether the message has completed.
Example of performing this task in a separate thread:
...
process.Start();
Task.Factory.StartNew(new Action<object>(ReadFromStreamReader), process.StandardOutput);
void ReadFromStreamReader(object state)
{
StreamReader reader = state as StreamReader;
char[] buffer = new char[1024];
int chars;
while ((chars = reader.Read(buffer, 0, buffer.Length)) > 0)
{
string data = new string(buffer, 0, chars);
OnDataReceived(data);
}
// You arrive here when process is terminated.
}
void OnDataReceived(string data)
{
// Process the data here. It might contain only a chunk of a full line
// remember to use Invoke() if you want to update something in your form GUI
}

Related

Read process output as it is written into CMD (not flushed?)

Is there a way to get process output the same way It’s written when the process is launched through shell? Basically I need to launch some external processes (A&B). When I launch them through cmd or with UseShellExecute = true the output is printed continuously.
Process A prints its progress line by line. Process B displays and updates a text-based progress bar.
For process A, OutputDataReceived does not fire after each output line that is normally printed if the process is launched through shell. It fires after each 20 lines approximately (it fires 20 times so I have the whole output, but I can’t capture the output in real time). I guess that it fires after stdOut is flushed, but then how cmd does it to print partial output and what can I do to read it?
For process B, OutputDataReceived fires only once the process is done.
My code:
public static bool ExecuteProcess(string Path, string CommandLine, Action<string> OutputLineDelegate)
{
using (Process ChildProcess = new Process())
{
ChildProcess.StartInfo.FileName = Path;
ChildProcess.StartInfo.Arguments = CommandLine;
ChildProcess.StartInfo.UseShellExecute = false;
ChildProcess.StartInfo.CreateNoWindow = true;
ChildProcess.StartInfo.RedirectStandardOutput = true;
ChildProcess.OutputDataReceived += delegate (object sender, DataReceivedEventArgs e)
{
OutputLineDelegate(e.Data);
};
ChildProcess.Start();
ChildProcess.WaitForExit();
var ExitCode = ChildProcess.ExitCode;
OutputLineDelegate(string.Format("Exit code {0}", ExitCode));
return ExitCode == 0 ? true : false;
}
}
I’ve tried to reading stdOut char by char, but both StreamReader.Read() and StreamReader.Peek() wait until anything is in the output.

C#: Calling a php script and beeing able to stop it

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

command redirection issue

here is my code
//Create process
System.Diagnostics.Process pProcess = new System.Diagnostics.Process();
//strCommand is path and file name of command to run
pProcess.StartInfo.FileName = "ffmpeg.exe";
//strCommandParameters are parameters to pass to program
pProcess.StartInfo.Arguments = "-i " + videoName;
pProcess.StartInfo.UseShellExecute = false;
//Set output of program to be written to process output stream
pProcess.StartInfo.RedirectStandardOutput = true;
//Start the process
pProcess.Start();
//Get program output
string strOutput = pProcess.StandardOutput.ReadToEnd();
//Wait for process to finish
pProcess.WaitForExit();
The command works, but strOutput string is empty, results are shown within the console. Am I missing something here?
It's possible the program is writing its output to StandardError instead of StandardOutput. Try using .RedirectStandardError = true and then .pProcess.StandardError.ReadToEnd() to capture that output.
If you need the possibility of capturing both standard error and standard out in (roughly) the proper interleave, you will likely need to use the async versions with callbacks on OutputDataReceived and ErrorDataReceived and using BeginOutput/ErrorReadLine.
Try to capture Std Error too as on any event of error, it will be used instead.
//Set output of program to be written to process output stream
pProcess.StartInfo.RedirectStandardError = true;
pProcess.StartInfo.RedirectStandardOutput = true;
//Start the process
pProcess.Start();
//Wait for process to finish
pProcess.WaitForExit();
//Get program output
string strError = pProcess.StandardError.ReadToEnd();
string strOutput = pProcess.StandardOutput.ReadToEnd();
I just wonder why you wait for exit WaitForExit after reading the output, it should be in reversed order as your app may dump more until it finally completes the ops

C#: get external shell command result line by line

I am writing a C# winform application that starts a second process to execute shell commands like "dir" and "ping". I redirect the second process's output so my app can receive the command result. It roughly works fine.
The only problem is my winform app receives the command line output as a whole instead of line by line. For example, it has to wait for the external "ping" command to finish (which takes many seconds or longer) and then receives the whole output (many lines) at once.
What I want is the app receives the cmdline output in real-time, i.e. by lines not by block. Is this doable?
I am using this code to read the output:
while ((result = proc.StandardOutput.ReadLine()) != null)
But it does not work the way I expected.
Thanks in advance.
EDIT: here is the code I am using:
System.Diagnostics.ProcessStartInfo procStartInfo = new
System.Diagnostics.ProcessStartInfo("cmd", "/c " + command);
procStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
// The following commands are needed to redirect the standard output.
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
procStartInfo.CreateNoWindow = true;
// Now we create a process, assign its ProcessStartInfo and start it
System.Diagnostics.Process proc = new System.Diagnostics.Process();
proc.StartInfo = procStartInfo;
proc.Start();
// Get the output into a string
string result;
try {
while ((result = proc.StandardOutput.ReadLine()) != null)
{
AppendRtfText(result+"\n", Brushes.Black);
}
} // here I expect it to update the text box line by line in real time
// but it does not.
Have a look at the example in this msdn article on how to do the reading completly async.
Beyond that I expect your code does to read line by line now but the UI doesn't get any time to repaint (missing Application.DoEvents(); after updating the RTFTextBox
Instead of loop using while ((result = proc.StandardOutput.ReadLine()) != null) you should of using:
...
proc.OutputDataReceived += proc_DataReceived;
proc.Start();
proc.BeginOutputReadLine();
proc.WaitForExit();
This will start asynchronous reading the lines when they arrives, you then handle the lines read by e.Data in proc_DataReceived handler, since you are use BeginOutputReadline the e.Data will be a string lines.
This could be usefull:
http://social.msdn.microsoft.com/Forums/en-US/csharplanguage/thread/8d6cebfc-9b8b-4667-85b5-2b92105cd0b7/
http://www.dotnetperls.com/redirectstandardoutput
I had the same issue and got around it with the following. I found that if I had an error in the external app I was getting no output at all using the ReadToEnd() method, so switched to use the line by line streamreader. Will be switching over to use the answer provided by Saa'd though as that looks like the proper way to handle it.
Also found this solution: c# coding convention public/private contexts which provides for error handling at the same time and giving a fuller explanation to the use of externalApp.OutputDataReceived += (sender, args) => Console.WriteLine(args.Data);
Process externalApp = new Process();
externalApp.StartInfo.FileName = config.ExternalApps + #"\location\DeleteApp.exe";
externalApp.StartInfo.Arguments = Directory.GetCurrentDirectory() + #"\..\..\..\project\argumentsForDeleteApp.xml";
externalApp.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
externalApp.StartInfo.UseShellExecute = false;
externalApp.StartInfo.RedirectStandardOutput = true;
Console.Out.WriteLine(DateTime.UtcNow.ToLocalTime().ToString() +
":###### External app: " + externalApp.StartInfo.FileName + " - START");
externalApp.Start();
using (StreamReader reader = externalApp.StandardOutput)
{
while (!reader.EndOfStream)
{
string result = reader.ReadLine();
Console.Out.WriteLine(result);
}
}
externalApp.WaitForExit();

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