Print standard output of Process in event handler - c#

I have a console application which I am running as a process from my C# program.
I have made an event handler to be called when this process terminates.
How do I print the Standard output of this process inside the event handler.
Basically, how do I access the properties of a process inside the event handler ?
My code looks like below.
public void myFunc()
{
.
.
Process p = new Process();
p.StartInfo.FileName = "myProgram.exe";
p.StartInfo.RedirectStandardOutput = true;
p.EnableRaisingEvents = true;
p.Exited += new EventHandler(myProcess_Exited);
p.Start();
.
.
}
private void myProcess_Exited(object sender, System.EventArgs e)
{
Console.WriteLine("log: {0}", <what should be here?>);
}
I do not want to make the process object p as a field of the class.
Also, what is the use of System.EventArgs e field ? How can this be used ?

In your event handler
object sender
is the Process object (that is a pretty common pattern by the way throughout the .NET Framework)
Process originalProcess = sender as Process;
Console.WriteLine("log: {0}", originalProcess.StandardOutput.ReadToEnd());
Note also that you have to set:
p.StartInfo.UseShellExecute = false;
to use IO redirection in your Process.

Use like this:
private void myProcess_Exited(object sender, System.EventArgs e)
{
Process pro = sender as Process;
string output = pro.StandardOutput.ReadToEnd()
Console.WriteLine("log: {0}", output);
}
Standart output is nothing else then StreamReader.

One option would be to capture it in a closure:
public void myFunc()
{
Process p = new Process();
p.StartInfo.FileName = "myProgram.exe";
p.StartInfo.RedirectStandardOutput = true;
p.EnableRaisingEvents = true;
p.Exited += new EventHandler((sender, args) => processExited(p));
p.Start();
}
private void processExited(Process p)
{
Console.WriteLine(p.ExitTime);
}

Related

C# append command line output to a Text Box

I'm writing a program (a C# winforms application) to print the console output of a process to a Textbox as follows.
private void button21_Click(object sender, EventArgs e)
{
Process p = new Process();
p.StartInfo.FileName = "node";
p.StartInfo.Arguments = "server.js";
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.WorkingDirectory = "D:/Dev/Workspace-JavaScript/p5js/SoftProject1/";
p.OutputDataReceived += new DataReceivedEventHandler(handler);
p.Start();
p.BeginOutputReadLine();
}
private void handler(object sender, DataReceivedEventArgs e)
{
Trace.WriteLine(e.Data);
this.BeginInvoke(new MethodInvoker(() =>
{
textBox1.Text += (e.Data ?? string.Empty)+Environment.NewLine;
}));
}
But when the program is running, It only prints the first line of the console output and nothing more. I have no idea about whats wrong with this code. Can anyone explain what's happening here?

Sending DOS commands using Process - missing data on OutputDataReceived callback

I have been looking for a solution before posting but I gave up!
I just want to interactively send DOS command using Standard input. It works well but I always don't get the last line (the prompt line) on the OutputDataReceived callback.
Any ideas?
Process p = null;
private void Start_Click(object sender, EventArgs e)
{
p = new Process();
p.StartInfo = new ProcessStartInfo();
p.EnableRaisingEvents = true;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.FileName = "cmd.exe";
p.ErrorDataReceived += ErrorDataReceived;
p.OutputDataReceived += OutputDataReceived;
p.Start();
p.BeginErrorReadLine();
p.BeginOutputReadLine();
p.StandardInput.WriteLine("dir");
}
private void OutputDataReceived(object sender, DataReceivedEventArgs e)
{
Console.WriteLine(e.Data + "\n");
}
private void WriteToStandardInput_Click(object sender, EventArgs e)
{
p.StandardInput.WriteLine(txt_command.Text); //can be "dir" or "cd temp"
}
Adding p.StandardInput.Close() solves the problem, reason is when you close the input stream it actually terminates the process (which you start using 'p.start'). So as I said, you need start separate process for each command.
~Nilesh
OK, I have found a solution... I have created 2 tasks as shown below which are reading constantly from the output and error stream and print it to a rich text box. The trick was not to use BeginErrorReadLine and BeginOutputReadLine.
I hope I was able to help others...
public partial class Form1 : Form
{
private Process p = new Process();
private SynchronizationContext context;
public Form1()
{
InitializeComponent();
context = SynchronizationContext.Current;
}
private void Start_Click (object sender, EventArgs e)
{
p.StartInfo = new ProcessStartInfo();
p.EnableRaisingEvents = true;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.UseShellExecute = false;
p.Start();
Task.Run(() => ReadFromStreamLoop(p.StandardOutput));
Task.Run(() => ReadFromStreamLoop(p.StandardError));
}
private void ReadFromStreamLoop (StreamReader s)
{
int count = 0;
char[] buffer = new char[1024];
do
{
StringBuilder builder = new StringBuilder();
count = s.Read(buffer, 0, 1024);
builder.Append(buffer, 0, count);
context.Post(new SendOrPostCallback(delegate (object state)
{
richTextBox1.AppendText(builder.ToString());
}), null);
} while (count > 0);
}
private void WriteToStandardInput_Click (object sender, EventArgs e)
{
p.StandardInput.WriteLine(txt_command.Text); //can be "dir" or "cd temp"
}
}
If you are looking for event end of command execution, then every time spawn a new process. At the end of processing you can show the prompt on main process. Manage your environment variable through invoking(main) process.
See the example on - https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.outputdatareceived?view=netframework-4.7.2

Stopping a continuous Event in C# with (f.ex.) a button

This is probably simple, and the question might not be very good, but I'm looking for the best or most efficient way to accomplish this:
A button click starts an Event, which then runs a method that continiously pings an IP address. The ping output is displayed in a text box.
A click on the same button stops the ping task.
Here's the (I think) relevant code:
The method run by the Event connected to the Ping button:
private void pingClicked (object sender, EventArgs e) {
pinger();
}
The pinger() method:
private void pinger() {
string command = "/c ping " + ipadrtextbox.Text;
if (contchkbox.Checked) {
command += " -t";
}
ProcessStartInfo procStartInfo = new ProcessStartInfo("CMD", command);
Process proc = new Process();
proc.StartInfo = procStartInfo;
proc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
proc.Start();
procStartInfo.CreateNoWindow = true;
procStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
procStartInfo.RedirectStandardOutput = true;
procStartInfo.UseShellExecute = false;
proc.OutputDataReceived += new
DataReceivedEventHandler(proc_OutputDataReceived);
proc.Start();
proc.BeginOutputReadLine();
procStartInfo.CreateNoWindow = true;
procStartInfo.WindowStyle = ProcessWindowStyle.Hidden;
}
void proc_OutputDataReceived(object sender, DataReceivedEventArgs e) {
if (e.Data != null) {
string newLine = e.Data.Trim() + Environment.NewLine;
MethodInvoker append = () => pingoutput.AppendText(newLine);
pingoutput.BeginInvoke(append);
}
}
A while-loop in the pinger method results in a complaint that "An async read operation has already been started on the stream.", so that's apparently not the way to go.
Also, I haven't found a way for the method to listen for a buttonpress elsewhere in the application, and then stop the task with roc.CancelOutputRead(). But I expect that's the way the task should be stopped?
Keep the running process as a private member of your class then:
private void pingClicked (object sender, EventArgs e) {
if( process != null && !process.HasExited )
{
process.CancelOutputRead()
process.Kill();
process=null;
} else {
pinger();
}
}

Redirecting cmd Output to textBox

I'm Re-Creating the "command prompt" into a Windows Form.
The application is not working properly; and i can't figure exactly why.
When the form Loads it is suposed to run the cmd.exe (Load the cmd info into "TextBox_Receive"), but it doesnt; and also after writing any command in the "textBox_send" (that sends input); it will only show input after pressing "Enter" key 2 or 3 times.
Any idea what i am missing here?
public partial class Form1 : Form
{
// Global Variables:
private static StringBuilder cmdOutput = null;
Process p;
StreamWriter SW;
public Form1()
{
InitializeComponent();
textBox1.Text = Directory.GetCurrentDirectory();
// TextBox1 Gets the Current Directory
}
private void Form1_Load(object sender, EventArgs e)
{
checkBox1.Checked = true;
// This checkBox activates / deactivates writing commands into the "textBox_Send"
cmdOutput = new StringBuilder("");
p = new Process();
p.StartInfo.FileName = "cmd.exe";
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.RedirectStandardInput = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.EnableRaisingEvents = true;
p.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
p.Start();
SW = p.StandardInput;
p.BeginOutputReadLine();
p.BeginErrorReadLine();
}
private static void SortOutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
// I dont actually understand this part of the code; as this is a "copy" of a snippet i found somewhere. Although it fixed one of my issues to redirect.
{
if (!String.IsNullOrEmpty(outLine.Data))
{
cmdOutput.Append(Environment.NewLine + outLine.Data);
}
}
private void textBox1_KeyPress(object sender, KeyPressEventArgs e)
{
// Send "Enter Key" - Send Command
if (e.KeyChar == 13)
{
SW.WriteLine(txtbox_send.Text);
txtbox_receive.Text = cmdOutput.ToString();
txtbox_send.Clear();
}
}
private void checkBox1_CheckedChanged(object sender, EventArgs e)
{
// Enable / Disable Sending Commands
if (checkBox1.Checked)
txtbox_send.Enabled = true;
else
txtbox_send.Enabled = false;
}
}
}
You might also try capturing the error data.
To do this:
after your line
p.OutputDataReceived += new DataReceivedEventHandler(SortOutputHandler);
enter this line
p.ErrorDataReceived += new DataReceivedEventHandler(SortOutputHandler);
It might also be a problem with cmd.exe.
I think your issue is the use of OutputDataReceived. In the documentation:
The event is enabled during asynchronous read operations on
StandardOutput. To start asynchronous read operations, you must
redirect the StandardOutput stream of a Process, add your event
handler to the OutputDataReceived event, and call BeginOutputReadLine.
Thereafter, the OutputDataReceived event signals each time the process
writes a line to the redirected StandardOutput stream, until the
process exits or calls CancelOutputRead.
See the example code on that page for more details.
However - I'm not sure you need to go that route. Have you tried just reading directly from the StandardOutput stream?

How can I get whether a process has executed properly or had some errors while executing?

I have a process that I need to start through WPF using C# as back end. The process is starting properly but in the process there is some error. In other words I can say that the process did not start properly. So how can I get that information on my code-behind?
For Example:
p.StartInfo.FileName = BasePath;
p.StartInfo.Arguments = args;
p.Start();
But after executing this file I am getting an error that some of the related DLLs are missing. I know the cause but if I have to detect this error, how could I get it on my code-behind?
Subscribe to the Process.Exited event and then check Process.ExitCode:
public void StartProcess()
{
p.StartInfo.FileName = BasePath;
p.StartInfo.Arguments = args;
p.Start();
p.Exited += new EventHandler(Process_Exited);
}
void Process_Exited(object sender, EventArgs e)
{
var p = sender as Process;
if (p.ExitCode != 0)
MessageBox.Show(string.Format("Process failed: ExitCode = {0}", p.ExitCode));
}

Categories