C# pathping process freeze - c#

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

Related

Create and actively use Process without freezing main thread

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

How to Obtain Python Script Return Value to Use in Visual Studio Form?

I am currently using a DAQC2 PiPlate on my Raspberry Pi and testing Analog Inputs on it. To get the inputs, I call a python script to return them. My problem is that I am using Visual Studio to create a Form GUI so that when the program runs independently on the Pi, the form handles the GUI (running on a 7" touchscreen)
I've currently tried using a StreamReader that reads the script's output every time the timer ticks, which then updates the label afterward.
My PythonScript Class
public PythonScript(string scriptName)
{
_scriptName = scriptName;
}
public string Run()
{
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "python3";
psi.Arguments = _scriptName;
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
psi.RedirectStandardOutput = false;
psi.RedirectStandardError = true;
Process process = new Process();
process.StartInfo = psi;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
StreamReader myStreamReader = process.StandardOutput;
string str = myStreamReader.ReadLine();
process.WaitForExit();
process.Close();
return str;
}
My current work in the Form1.cs class that handles the form loading and the location of the timer
private void Form1_Load(object sender, EventArgs e)
{
/**
//Maximize form to fill screen
WindowState = FormWindowState.Normal;
FormBorderStyle = FormBorderStyle.None;
Location = new Point(0, 0);
TopMost = true;
Screen currentScreen = Screen.FromHandle(this.Handle);
this.Size = new System.Drawing.Size(currentScreen.Bounds.Width, currentScreen.Bounds.Height);
**/
//set up timer
timer.Interval = (10);
timer.Tick += new EventHandler(timer_tick);
timer.Start();
}
private void timer_tick(object sender, EventArgs e)
{
string value = "";
PythonScript getValue1 = new PythonScript("/home/pi/Desktop/DAQC2_Script.py");
value = getValue1.Run();
label1.Text = value;
}
My python script
import piplates.DAQC2plate as x
value = str(x.getADC(0, 1))
return value
Currently my form builds and runs properly, however it doesn't do anything. The label remains the default "00.0." I am sure my RaspberryPi and it's equipment are hooked up properly, because I can execute the script (without the return statement) and it reads the data and allows me to print to the console.
I simply want the value in the python script to be updated to the label1.Text every second. I'm new to not only c#, but to python as well so any help is greatly appreciated.
So after awhile of digging, I was drastically overlooking the possibility that my issue was within my python script. I finally got the idea to use process.RedirectStandardError, which led me to find that my 'return' statement was "out of function." Not only was my return statement not in any specific function, but I discovered it wasn't needed at all. The StreamReader handles the reading without any return statement from within the script. This was a learning experience for this noob.

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

Dispatcher timer not running

I'm trying to execute some Python scripts from my WPF app. The scripts are generating the log files and the code in the Tick event is reading them and displaying that as it is in a textbox.
My issue here is that, that LaunchProcess fires successfully, but the UI freezes. I have an indefinite progress bar, which too does not start animating. I'm a beginner with WPF and there is something very small I have to do to get this code working. I'm not getting any error/warnings. The scripts run fine and in the end I get to know the result too. But during the run, the UI of my app freezes.
private void LaunchProcess(string paramStr)
{
Process myProcess = new Process();
StartProgressBar();
try
{
dispatcherTimer = new DispatcherTimer();
dispatcherTimer.Tick += new EventHandler(dispatcherTimer_Tick);
dispatcherTimer.Interval = new TimeSpan(0, 0, 0);
dispatcherTimer.Start();
myProcess.StartInfo.UseShellExecute = false;
// You can start any process
myProcess.StartInfo.FileName = "C:\\Python32\\python.exe";
myProcess.StartInfo.Arguments = "\""+paramStr+"\"";
myProcess.StartInfo.CreateNoWindow = true;
myProcess.StartInfo.RedirectStandardOutput = true;
myProcess.StartInfo.RedirectStandardError = true;
myProcess.Start();
myProcess.WaitForExit();
// This code assumes the process you are starting will terminate itself.
// Given that is is started without a window so you cannot terminate it
// on the desktop, it must terminate itself or you can do it programmatically
// from this application using the Kill method.
dispatcherTimer.Stop();
}
catch
{
MessageBox.Show("Process Launch Failed!!", "Failure", MessageBoxButton.OK, MessageBoxImage.Error);
}
}
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
//txtOutPut.Text = "";
txtOutPut.Text += "\n" + DateTime.Now.ToString();
if (File.Exists(scriptPath+"\\log.txt"))
{
//File.Copy("C:\\FlashAuto\\Execution_Logs\\log.txt", "C:\\FlashAuto\\Temp\\log.txt", true);
TextReader readLogs = new StreamReader(scriptPath + "\\log.txt");
string line = readLogs.ReadLine();
while (line != null)
{
txtOutPut.Text += "\n" + line;
line = readLogs.ReadLine();
txtOutPut.ScrollToEnd();
}
//CountLines = txtExecLog.LineCount - 1;
readLogs.Close();
// Forcing the CommandManager to raise the RequerySuggested event
txtOutPut.ScrollToEnd();
CommandManager.InvalidateRequerySuggested();
readLogs.Dispose();
}
else
{
txtOutPut.Text += "log file not found at: " + DateTime.Now.ToString();
}
}
In case you call LaunchProcess from the UI thread it will obviously be blocked at myProcess.WaitForExit().
You might simply remove the myProcess.WaitForExit() and dispatcherTimer.Stop() calls from the launch method and check if the process is still running in the timer Tick handler.
private void dispatcherTimer_Tick(object sender, EventArgs e)
{
if (myProcess.WaitForExit(0)) // check with timeout zero
{
dispatcherTimer.Stop();
}
... // all your code
}
Calling LaunchProcess method asynchronously would resolve your UI Freeze Issue
public void LaunchProcessAsynchrousCall(string paramStr)
{
ThreadStart displayContentHandler = delegate()
{
LaunchProcess(paramStr)
};
Thread thread = new Thread(displayContentHandler);
thread.IsBackground = true;
thread.Start();
}

Realtime Console Output Redirection using Process

I am using VBOXMANAGE to "export" a guest machine. VBOXManage is a Console application that can control the guest machine's behavior from the host. Since the export command is a long process, it returns process updates like so:
0%...10%...20%...30%...100%
I am writing a C# application that will invoke VBOXManage using Process. Here's my code:
Process VBOXProc = new Process();
VBOXProc.StartInfo.FileName = VBOXMANAGE;
VBOXProc.StartInfo.Arguments = Arguments;
VBOXProc.StartInfo.UseShellExecute = false;
VBOXProc.StartInfo.CreateNoWindow = true;
VBOXProc.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
VBOXProc.StartInfo.RedirectStandardError = true;
VBOXProc.StartInfo.RedirectStandardOutput = true;
VBOXProc.OutputDataReceived += new DataReceivedEventHandler(VBOXProc_OutputDataReceived);
VBOXProc.ErrorDataReceived += new DataReceivedEventHandler(VBOXProc_ErrorDataReceived);
VBOXProc.EnableRaisingEvents = true;
VBOXProc.Start();
VBOXProc.BeginOutputReadLine();
VBOXProc.BeginErrorReadLine();
VBOXProc.WaitForExit();
This is fine, except that the output is being read per LINE. This means that the process updates "
0%...10%...20%...30%...100%" will only show AFTER the actual process is done.
Is there a way to capture the console output in realtime?
Thanks!
This worked for me:
process.StartInfo.CreateNoWindow = true;
process.StartInfo.ErrorDialog = false;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
process.StartInfo.UseShellExecute = false;
process.ErrorDataReceived += (sendingProcess, errorLine) => error.AppendLine(errorLine.Data);
process.OutputDataReceived += (sendingProcess, dataLine) => SetMessage(dataLine.Data);
process.Start();
process.BeginErrorReadLine();
process.BeginOutputReadLine();
process.WaitForExit();
error.AppendLine() and SetMessage() are the methods I used.
You can read directly from the StanadardOutput/Error for the process using all the standard Stream methods, just be sure to set the StartInfo.Redirectxxx to true.
var p = new Process()
p.StartInfo.UseShellExecute = false; //not sure if this is absolutely required
p.StartInfo.RedirectStandardOuput = true;
....
do
{
Thread.Sleep(nnn);
Console.Out.Write(p.StandardOutput.ReadToEnd());
}
while (!p.HasExited);
//catch any leftovers in redirected stdout
Console.Out.Write(p.StandardOutput.ReadToEnd());
The above will echo the output of the child process to your applications Standard Out.
You can read Blocks of a particular size using p.StandardOutput.Read(char[], int, int) or asynchronous reads using p.StadardOutput.BaseStream.BeginRead(...).
All the same methods are available for StandardError.
Sleeping in the loop frees up the processor for other tasks and allows some data to accumulate in the bufffer. If the sleep period is too long and the buffer overflows some output from the executing process will be lost. If the sleep period is too short a lot of CPU cycles are spent reading and empty buffer.
Try to redirect standard input too and apply AutoFlush to StandardInput. Next read stream using StreamReader.
Process proces;
ProcessStartInfo psi = new ProcessStartInfo();
psi.FileName = "test.exe";
psi.UseShellExecute = false;
psi.CreateNoWindow = true;
psi.RedirectStandardOutput = true;
psi.RedirectStandardInput = true;
proces = Process.Start(psi);
proces.StandardInput.AutoFlush = true;
Sorry any mistake, I am Brazilian and to using Google Translate to write this text.
Coincidentally, I also'm doing a program that works with VBoxManage of Virtualbox. In my case I wanted, among other things, convert a virtual disk. Also it delays and the percentage with progress also
I managed to do just this by creating a process of will to run the program, and using a user classes 'Dean North` the other question that is similar to this. It is important to use a thread to run the VBoxManage, otherwise has no way to work the obtained text or view the progress.
O texto é muito grande pra eu adicionar quatro espaços antes de cada linha e repassar.
The classes replace the Process system class. Need not make any changes to your code, just add a arquivo.cs with the text passed by the user Dean North instead of Process p = new Process() use FixedProcess p = new FixedProcess ()
After that it was my code:
private void BotaoParaTestes_Click(object sender, EventArgs e)
{
string linha = #"clonehd " +
"\"Z:\\Máquinas Virtuais\\Teste.vdi\" " +
"\"C:\\Temp\\teste.vdi\" " +
"--format VDI --variant Standard";
Thread tarefa = new Thread(Executar);
tarefa.Start(linha);
}
private void Executar(object Linha)
{
FixedProcess fp = new FixedProcess ();
fp.StartInfo.FileName = ItensEstaticos.VBox;
fp.StartInfo.Arguments = Linha.ToString();
fp.StartInfo.CreateNoWindow = true;
fp.StartInfo.ErrorDialog = false;
fp.StartInfo.RedirectStandardError = true;
fp.StartInfo.RedirectStandardOutput = true;
fp.StartInfo.UseShellExecute = false;
fp.ErrorDataReceived += (sendingProcess, errorLine) => Escrita(errorLine.Data);
fp.OutputDataReceived += (sendingProcess, dataLine) => Escrita(dataLine.Data);
fp.Start();
fp.BeginErrorReadLine();
fp.BeginOutputReadLine();
fp.WaitForExit();
}
private void Escrita(string Texto)
{
if (!string.IsNullOrEmpty(Texto))
{
BeginInvoke(new MethodInvoker(delegate
{
this.Texto.Text += Texto;
}));
}
}
For me the event is only called when the text is changed, not only when the VBoxManage goes to a new line. Sometimes the text was null, then place a check structure as I did before using the text obtained for controls.

Categories

Resources