C# append command line output to a Text Box - c#

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?

Related

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

Accessing forms control from function handler on C#

I have created an interactive shell on C# but I don't know how to access my forms control and assign received values to my textbox, I know that the threads can't access the UI thread but in this case, I don't seem to be able to fix the problem, there is going to be many input and output in that shell, and I want to make sure that everything is shown to user.
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}
private void button1_Click(object sender, EventArgs e)
{
Process process = new Process();
process.StartInfo.FileName = "echoo.exe";
process.StartInfo.Arguments = "";
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.ErrorDataReceived += new DataReceivedEventHandler(OutputHandler);
process.StartInfo.RedirectStandardInput = true;
process.Start();
StreamWriter sw = process.StandardInput;
process.BeginOutputReadLine();
process.BeginErrorReadLine();
sw.WriteLine("sent");
process.WaitForExit();
}
static void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
Form1.textBox1.Text = outLine.Data;
}
}
If you write this in your setter:
static void OutputHandler(object sendingProcess, DataReceivedEventArgs outLine)
{
if(textBox1.InvokeRequired)
{
textBox1.BeginInvoke((MethodInvoker) delegate() {textBox1.Text = outLine.Data;});
}
else
{
textBox1.Text = outLine.Data;
}
}
It'll force the set onto the UI thread. I found this from this question: stack question

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

Console application immediately exits after setting RedirectStandardOutput to true

I have a Process that needs to update the console in realtime based on the output. But its not working. The console just opens and closes, and the Process runs in the background. I can not figure out what I am doing wrong. Here is my code:
private static StringBuilder sortOutput = null;
static void Main(string[] args)
{
Process process;
process = new Process();
process.StartInfo.FileName = "C:\\ffmbc\\ffmbc.exe";
//process.StartInfo.Arguments = "-i new5830df.mxf -an ";
process.StartInfo.UseShellExecute = false;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.RedirectStandardOutput = true;
sortOutput = new StringBuilder("");
process.OutputDataReceived += new DataReceivedEventHandler(OutputHandler);
process.Exited += new EventHandler(myProcess_Exited);
process.StartInfo.RedirectStandardInput = true;
process.Start();
process.BeginOutputReadLine();
}
private static void OutputHandler(object sender, DataReceivedEventArgs outLine)
{
string line;
line = (outLine.Data.ToString());
Console.WriteLine(line);
}
private static void myProcess_Exited(object sender, System.EventArgs e)
{
Console.WriteLine("Proccess Finished");
}
Make sure to call process.WaitForExit() to block until the process exits.
Your Main() has exited, so the console closes. You need to wait until the called program completed, before you exit your Main function.

Print standard output of Process in event handler

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

Categories