Create and actively use Process without freezing main thread - c#

I'm currently trying to create new NodeJS process and while it's running, put it's console output into my winform textbox.
Whenever this process is executed, it's freezing main thread as if form is waiting for this process to exit. After the process is closed thats when the console output is added to the textbox.
What I'm trying to achieve is simultaneously have this node process running in the background and have whatever it's outputing in the textbox.
Edit 1:
I Managed to run the console without freezing main thread but the output only shows when the process is closed
My current code:
private void Btn_connect_Click(object sender, EventArgs e)
{
if(backgroundWorker1.IsBusy != true)
{
backgroundWorker1.RunWorkerAsync();
}
}
private void BackgroundWorker1_DoWork(object sender, DoWorkEventArgs e)
{
var worker = sender as BackgroundWorker;
nodeProcess = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "node.exe";
startInfo.Arguments = #"path" + " arg1 arg2 arg3";
startInfo.UseShellExecute = false;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.RedirectStandardOutput = true;
nodeProcess.StartInfo = startInfo;
nodeProcess.Start();
while (worker.CancellationPending != true)
{
Thread.Sleep(200);
AddText(nodeProcess.StandardOutput.ReadToEnd());
worker.ReportProgress(1);
}
e.Cancel = true;
}
public void AddText(string text)
{
if(txt_log.InvokeRequired)
{
txt_log.Invoke(new Action<string>(AddText), new object[] { text });
return;
}
txt_log.Text += "\n " + text;
}

Instead of a BackgroundWorker you could try using Process.BeginOutputReadLine, Process.OutputDataReceived, and Process.Exited.
void StartProcess()
{
Process nodeProcess = new Process();
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "node.exe";
startInfo.Arguments = #"path" + " arg1 arg2 arg3";
startInfo.UseShellExecute = false;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.RedirectStandardOutput = true;
nodeProcess.StartInfo = startInfo;
nodeProcess.EnableRaisingEvents = true;
nodeProcess.Exited += nodeProcess_Exited;
nodeProcess.OutputDataReceived += nodeProcess_OutputDataReceived;
nodeProcess.Start();
nodeProcess.BeginOutputReadLine();
}
void nodeProcess_Exited(object sender, EventArgs e)
{
// Do something when the process exits, if you need to.
// You'll want to check InvokeRequired before you modify any of your form's controls.
}
void nodeProcess_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (txt_log.InvokeRequired)
{
txt_log.Invoke(new Action<string>(AddText), new object[] { e.Data });
return;
}
txt_log.Text += "\n " + e.Data;
}

Related

Duplicate process start doesn't react as expected

i'm trying to open a process with c# and react to it, when it's been closed. This work's for me:
private void StartProc()
{
var process = new System.Diagnostics.Process { StartInfo = { FileName = "PathTo.exe" } };
process.Start();
process.EnableRaisingEvents = true;
process.Exited += this.Editor_Exited;
}
private void Editor_Exited(object sender, EventArgs e)
{
MessageBox.Show("Process canceled");
}
Lets say I'm opening a text editor with this code. If there is already an instance of this text editor the code won't open a second instance and also jumps instant in the Editor_Exited Code.
I want the code to open a new instance and don't jump in the Editor_Exited code.
string processName = "PathTo.exe";
var process = new System.Diagnostics.Process { StartInfo = { FileName = processName } };
if (process.Start())
{
process.EnableRaisingEvents = true;
process.Exited += this.Editor_Exited;
}
else
{
var p = Process.GetProcessesByName(processName);
p.WaitForExit();
}
I get this is not 100% what you are asking for, but its a work around

How to keep c# process alive (Node server)

Hello i'am creating a process in c# that opens a node.js, so far so good but the process seems to end and tipically when i execute the same command from my cmd it stays alive, how can i achieve such thing from c#? Here is the code of the Process
private void buttonStartLLodiAgent_Click(object sender, EventArgs e)
{
//proc.WaitForExit();
ProcessStartInfo psi = new ProcessStartInfo();
psi.CreateNoWindow = true;
psi.RedirectStandardInput = true;
psi.RedirectStandardOutput = true;
psi.UseShellExecute = false;
psi.RedirectStandardError = true;
psi.FileName = "node.exe";
psi.Arguments = AppDomain.CurrentDomain.BaseDirectory + "/LLodiAgent/main.js";
using (var process = Process.Start(psi))
{
//labelStatusLLodiAgent.Text = "Active";
process.BeginOutputReadLine();
process.OutputDataReceived += proc_OutputDataReceived;
//process.WaitForExit();
}
}
public void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
outputConsole.Text = outputConsole.Text + "\r\n" + e.Data;
}
I get my Output right, but it seems like the Node service is not up, so i guess it executes but then kills it.
How to keep the server alive and the c# recieving data?
Thanks in advance, (this is my first post)

Redirecting batch file output to winform textbox issues

I created a GUI that calls a batch file and shows the command line output in a textbox after clicking a button. The batch file runs and the output is redirected properly for some time until ~80 lines in and the textbox output suddenly starts displaying the actual code in the batch file. Does this indicate a bug in the batch file or a problem with my script? I don't really know how to start debugging this problem.
I also notice that the batch file I'm calling from the GUI makes calls to other batch files. Could this be causing problems as well?
I should also mention that the batch file successfully runs from the command line.
private void buildProg_Click(object sender, EventArgs e)
{
using (Process process = new Process())
{
process.StartInfo.WorkingDirectory = some_directory;
process.StartInfo.FileName = "start.bat";
process.StartInfo.Arguments = "arg1 arg2";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.RedirectStandardError = true;
process.OutputDataReceived += proc_OutputDataReceived;
process.EnableRaisingEvents = true;
process.Start();
process.BeginOutputReadLine();
}
}
private void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
this.Invoke((Action)(() =>
{
textBox1.AppendText(Environment.NewLine + e.Data);
}));
}
When I run it from the GUI the batch file seems to be tripping up at this part.
if exist %version_file_path% (
set /p _version= <%version_file_path%
) else (
echo %version_file_path% not found
pause
)
There's two things happening here;
1) The /p statement is asking for user input, pausing the command and waiting.
2) You're disposing the Process before the events are firing, meaning we wouldn't be able to simulate the input it needs to continue executing.
This pretty much solves it:
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
this.FormClosing += Form1_FormClosing;
}
Process p;
private void Form1_FormClosing(object sender, FormClosingEventArgs e)
{
p?.Dispose();
}
private void button1_Click(object sender, EventArgs e)
{
if (p != null)
p.Dispose();
p = new Process();
p.StartInfo.WorkingDirectory = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location);
p.StartInfo.FileName = "test.bat";
p.StartInfo.Arguments = "";
p.StartInfo.CreateNoWindow = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardInput = true;
p.OutputDataReceived += proc_OutputDataReceived;
p.Start();
p.BeginOutputReadLine();
}
private void proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
this.Invoke((Action)(() =>
{
textBox1.AppendText(Environment.NewLine + e.Data);
}));
//can use either of these lines.
(sender as Process)?.StandardInput.WriteLine();
//p.StandardInput.WriteLine();
}
}

C# pathping process freeze

I'm creating a network diagnostic application and trying to add a pathping command to it where it takes an adress from a textfield as path to ping when I press a button, but the application freezes when I press the button and nothing shows in the output window.
private void btn_PingPath_Click(object sender, EventArgs e)
{
ProcessStartInfo PathPingStartInfo = new ProcessStartInfo();
PathPingStartInfo.FileName = "CMD.EXE";
PathPingStartInfo.UseShellExecute = false;
PathPingStartInfo.CreateNoWindow = true;
PathPingStartInfo.RedirectStandardOutput = true;
PathPingStartInfo.RedirectStandardInput = true;
PathPingStartInfo.RedirectStandardError = true;
PathPingStartInfo.StandardOutputEncoding = Encoding.GetEncoding(850);
Process PathPing = new Process();
PathPing.StartInfo = PathPingStartInfo;
PathPing.Start();
PathPing.StandardInput.WriteLine("PATHPING " + txt_PingPath.Text);
while (PathPing.StandardOutput.Peek() > -1)
{
txt_Output.Text = PathPing.StandardOutput.ReadLine();
}
while (PathPing.StandardError.Peek() > -1)
{
txt_Output.Text = PathPing.StandardError.ReadLine();
}
//txt_Output.Text = PathPing.StandardOutput.ReadToEnd();
PathPing.WaitForExit();
}
EDIT
I found the while loop from another question but it did not help. I still get no output in the output text window and the application still freezes.
The PATHPING command can end up running for several minutes before exiting, so your last line, PathPing.WaitForExit(); will also not return for several minutes (or until pathping exits). You can't wait like this on the UI thread, because the UI also needs to use this thread to re-draw and listen for windows messages.
You can free up the UI thread so that your application doesnt freeze by either creating a new thread, or using async/await features in .Net 4.5+, or using the event pattern. The following example uses the event pattern.
private void btn_PingPath_Click(object sender, EventArgs e)
{
ProcessStartInfo PathPingStartInfo = new ProcessStartInfo();
PathPingStartInfo.FileName = "CMD.EXE";
PathPingStartInfo.UseShellExecute = false;
PathPingStartInfo.CreateNoWindow = true;
PathPingStartInfo.RedirectStandardOutput = true;
PathPingStartInfo.RedirectStandardInput = true;
PathPingStartInfo.RedirectStandardError = true;
PathPingStartInfo.StandardOutputEncoding = Encoding.GetEncoding(850);
Process PathPing = new Process();
PathPing.StartInfo = PathPingStartInfo;
PathPing.Start();
PathPing.StandardInput.WriteLine("PATHPING " + txt_PingPath.Text);
PathPing.StandardInput.Flush();
PathPing.OutputDataReceived += (o, args) => txt_Output.Text += args.Data;
PathPing.ErrorDataReceived += (o, args) => txt_Output.Text += args.Data;
PathPing.BeginErrorReadLine();
PathPing.BeginOutputReadLine();
}

How to display output of iperf cmd prompt in textbox

I am using iperf-2.0.5-2-win32 tool to find network bandwidth. I have written codes in c# which opens the cmd prompt, pass iperf parameters to start server side & client side. iperf-2.0.5-2-win32 exe will not open directly, need to open through cmd prompt only.
At present the output(Transfer rate & Bandwidth) is displaying on cmd prompt itself. I want these output to be displayed in textbox
I have tried StreamReader also. But it takes null, I have also tried OutputDataReceived Event, its also taking null.
Found few codes for ipconfig & ping.but those were not working with iperf codes.
button_click event(),
{
Process Client_proc = new Process();
ProcessStartInfo Client_command = new ProcessStartInfo("cmd.exe");
string ip = txtIP.Text;
Client_command.CreateNoWindow = true;
Client_command.WindowStyle = ProcessWindowStyle.Hidden;
Client_command.WorkingDirectory = #"E:\Iperf\RunEXE_Through_Application\iperf-2.0.5-2-win32";
Client_command.Arguments = "/c START iperf -c " + ip;
Client_proc.StartInfo = Client_command;
Client_command.RedirectStandardOutput = true;
Client_command.UseShellExecute = false;
Client_proc.OutputDataReceived += new DataReceivedEventHandler(Client_proc_OutputDataReceived);
Client_proc.Start();
Client_proc.BeginOutputReadLine();
Client_proc.WaitForExit();
}
void Client_proc_OutputDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
string newLine = e.Data.Trim() + Environment.NewLine;
MethodInvoker append = () => txtOutput.Text += newLine;
txtOutput.BeginInvoke(append);
}
}
Plz help me.Earlier responses are appreciated
Thanks
you use this complete code for your disposal
It is not perfect (some problems when using multiple streams )
public void RunProcess(string FileName, string Arguments, bool EventWhenExit )
{
process = new Process();
process.EnableRaisingEvents = true;
process.OutputDataReceived += new DataReceivedEventHandler(OnDataReceivedEvent);
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.LoadUserProfile = false;
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
Thread.Sleep(1000);
if (EventWhenExit)
{
process.EnableRaisingEvents = true;
process.Exited += new EventHandler(myprocess_Exited);/*New line */
}
process.Start();
process.BeginOutputReadLine();
PID = process.Id;
}
private void myprocess_Exited(object sender, EventArgs e)
{
process.Refresh();
Thread.Sleep(2000);
onProcessEnd(this, "ENDOF " + Proc.ToString());
Console.WriteLine("Process exsiting ");
}
private void OnDataReceivedEvent(object sender, DataReceivedEventArgs e)
{
string OutputFromProcess = e.Data;
//fire event to event handler class for further use
onDataOutputFromProcess(this, OutputFromProcess, Proc.ToString());
}
than in your GUI layer you should bind to onDataOutputFromProcess event
there you should have something like
if (screenToPrint.InvokeRequired) //&& this.Visible)
{
try
{
this.Invoke(new Action<AppendToScreenParam>(AppendTextFullConfig), new object[] { append });
}
catch (Exception ex)
{
Console.WriteLine(ex);
}
return;
}
else
{
screenToPrint.SelectionFont = font;
screenToPrint.SelectionColor = append.Color;
//screenToPrint.AppendText(append.Message);
string TextToPrint = string.Format("{0}\n", append.Message);
screenToPrint.AppendText(TextToPrint);
}
}
Maybe it is because iperf process is returning error. Subscribe the ErrorDataReceived event with Client_proc.ErrorDataReceived += Client_proc_ErrorDataReceived; and see the results. If command returns error, you can see the error message as output.
void Client_proc_ErrorDataReceived(object sender, DataReceivedEventArgs e)
{
if (e.Data != null)
{
this.txtOutput.BeginInvoke(new MethodInvoker(() => { this.txtOutput.Text = e.Data; }));
}
}

Categories

Resources