I am writing an application in C# to download images using the Process class and wget.exe.
I want to handle the Process.Exited event without calling WaitForExit because calling WaitForExit hangs my UI. To work around this, I have tried many techniques like calling both Process.Start() and Process.WaitForExit() on another thread, using a BackgroundWorker etc. Still, my UI hangs at some level. So now I want simply handle the Process.Exited event without WaitForExit.
My code is:
bool processComplete = false;
Process process = new Process();
private void Start()
{
process.StartInfo.FileName = "path of wget";
process.StartInfo.Arguments = "arguments for downloading images";
process.EnableRaisingEvents = true;
process.Exited += new EventHandler(Process_Complete);
process.StartInfo.WindowStyle = ProcessWindowStyle.Hide;
process.Start();
}
private void Process_Complete(object sender, EventArgs e)
{
processComplete = true;
}
After starting the process, the Process.Exited event fires two times. First, while process is going on in between if some times downloading become slow(if images are more than 1000 etc ow at the starting only it fires process.exited event) and then after all images are downloaded,
I would expect the Process.Exited event to fire once. Why is it firing twice?
Faced the same issue.
I guess it's somehow related to accessing process members from different threads (in my case at least), 'cause from the log it's clear that handler is called from different threads.
Anyway, my solution was to unsubscribe from event in event handler:
bool processComplete = false;
Process process = new Process();
private void Start()
{
process.StartInfo.FileName = "path of wget";
process.StartInfo.Arguments = "arguments for downloading images";
process.EnableRaisingEvents = true;
process.Exited += Process_Complete;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hide;
process.Start();
}
private void Process_Complete(object sender, EventArgs e)
{
process.Exited -= Process_Complete;
processComplete = true;
}
I think alternative solution might be to synchronize access to process instance (using lock for example).
Related
I have a problem with my PictureBox after executing a process. At the time I call the process, that just executes a batch file, the PictureBox (displaying a GIF animation) freezes.
I want to click the button that starts the process and show the PictureBox displaying the GIF animation, and after the process is done I want to hide the PictureBox.
Does anyone know how to achieve this?
Here's my code:
// My executeCmdCommand
public void executeCmdCommand(string argument)
{
var arg0 = argument; // Path
System.Diagnostics.Process process = new System.Diagnostics.Process();
System.Diagnostics.ProcessStartInfo startInfo = new System.Diagnostics.ProcessStartInfo();
startInfo.WindowStyle = System.Diagnostics.ProcessWindowStyle.Hidden;
startInfo.FileName = "cmd.exe";
startInfo.Arguments = "/C " + arg0;
process.StartInfo = startInfo;
process.Start();
process.WaitForExit();
}
private void button1_Click(object sender, EventArgs e)
{
DisplayImage();
string cmdCom = #"C:\Users\Mim\Desktop\test.bat";
executeCmdCommand(cmdCom);
HideImage();
}
private void DisplayImage()
{
imageControl.Show();
//PictureBox imageControl = new PictureBox();
imageControl.Width = 400;
imageControl.Height = 400;
Bitmap image = new Bitmap(#"C:\Users\foobar\Desktop\loading.gif");
imageControl.Dock = DockStyle.Fill;
imageControl.Image = (Image)image;
Controls.Add(imageControl);
}
private void HideImage()
{
imageControl.Hide();
}
The problem lies on this line of code inside your executeCmdCommand() function:
process.WaitForExit();
Take a look at what the documentation says about this method:
Process.WaitForExit Method | Microsoft Docs
Sets the period of time to wait for the associated process to exit, and blocks the current thread of execution until the time has elapsed or the process has exited. To avoid blocking the current thread, use the Exited event.
So, you should not call this method if you want the UI thread unblocked till the end of the external process execution. But if you still want to be warned when the process ends, then you can use the Exited event as suggested on the documentation.
You can found an example of the use of the Exited event, and another ideas, here:
Windows Form run external process without blocking UI - Stack Overflow
I would add only one thing to the answer that I've just linked: If you are going to hide your PictureBox inside the event handler called by the Process.Exited event, you should set a reference of your Form to the Process.SynchronizingObject property (documentation), otherwise your event handler can (mostly probably) be executed on a thread different from your UI thread, causing an error when accessing anything from your Form, including the controls.
The code could be like this:
using System.Diagnostics;
public void executeCmdCommand(string argument)
{
Process process = new System.Diagnostics.Process();
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.FileName = "cmd.exe";
startInfo.Arguments = $"/C {argument}";
process.StartInfo = startInfo;
process.EnableRaisingEvents = true;
process.SynchronizingObject = this;
process.Exited += (sender, e) => {
HideImage();
};
process.Start();
//process.WaitForExit();
}
I've an application which does
Process.Start()
to start another application 'ABC'. I want to wait till that application ends (process dies) and continue my execution. How can I do it?
There may be multiple instances of the application 'ABC' running at the same time.
I think you just want this:
var process = Process.Start(...);
process.WaitForExit();
See the MSDN page for the method. It also has an overload where you can specify the timeout, so you're not potentially waiting forever.
Use Process.WaitForExit? Or subscribe to the Process.Exited event if you don't want to block? If that doesn't do what you want, please give us more information about your requirements.
I do the following in my application:
Process process = new Process();
process.StartInfo.FileName = executable;
process.StartInfo.Arguments = arguments;
process.StartInfo.ErrorDialog = true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Minimized;
process.Start();
process.WaitForExit(1000 * 60 * 5); // Wait up to five minutes.
There are a few extra features in there which you might find useful...
You could use wait for exit or you can catch the HasExited property and update your UI to keep the user "informed" (expectation management):
System.Diagnostics.Process process = System.Diagnostics.Process.Start("cmd.exe");
while (!process.HasExited)
{
//update UI
}
//done
I had a case where Process.HasExited didn't change after closing the window belonging to the process. So Process.WaitForExit() also didn't work. I had to monitor Process.Responding that went to false after closing the window like that:
while (!_process.HasExited && _process.Responding) {
Thread.Sleep(100);
}
...
Perhaps this helps someone.
Process.WaitForExit should be just what you're looking for I think.
Referring to the Microsoft example:
[https://learn.microsoft.com/en-us/dotnet/api/system.diagnostics.process.enableraisingevents?view=netframework-4.8]
Best would be to set:
myProcess.EnableRaisingEvents = true;
otherwiese the Code will be blocked.
Also no additional properties needed.
// Start a process and raise an event when done.
myProcess.StartInfo.FileName = fileName;
// Allows to raise event when the process is finished
myProcess.EnableRaisingEvents = true;
// Eventhandler wich fires when exited
myProcess.Exited += new EventHandler(myProcess_Exited);
// Starts the process
myProcess.Start();
// Handle Exited event and display process information.
private void myProcess_Exited(object sender, System.EventArgs e)
{
Console.WriteLine(
$"Exit time : {myProcess.ExitTime}\n" +
$"Exit code : {myProcess.ExitCode}\n" +
$"Elapsed time : {elapsedTime}");
}
Like Jon Skeet says, use the Process.Exited:
proc.StartInfo.FileName = exportPath + #"\" + fileExe;
proc.Exited += new EventHandler(myProcess_Exited);
proc.Start();
inProcess = true;
while (inProcess)
{
proc.Refresh();
System.Threading.Thread.Sleep(10);
if (proc.HasExited)
{
inProcess = false;
}
}
private void myProcess_Exited(object sender, System.EventArgs e)
{
inProcess = false;
Console.WriteLine("Exit time: {0}\r\n" +
"Exit code: {1}\r\n", proc.ExitTime, proc.ExitCode);
}
Try this:
string command = "...";
var process = Process.Start(command);
process.WaitForExit();
I want to run Iperf via c#
when running via cmd everything works fine and fast
but I run it via c# using this code :
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());
}
I get wrong strange behavior :
when running 1 stream (those of you who use Iperf will know...) every works fine in console and my application (winform)
but I run 3 streams and above , my application wont go over and than just hangs when it should exit
what can be the problem ?
what can be a good work around this problem ?
First you should avoid using Thread.Sleep(x) at any cost.
And i assume you are not using a backgroundWorker or a new Thread to execute the IPerf processes?
I think the backgroundworker would work well for you. Take a look here
In addition:
A cmd program is in most cases very linear.
A WinForm program not. You have to sepereate GUI and Work-Thread.
the GUI is only for Input and Display. Everthing else you should do in another Thread, because otherwise you will block the GUI.
In C#, I can start a process with
process.start(program.exe);
How do I tell if the program is still running, or if it closed?
MSDN System.Diagnostics.Process
If you want to know right now, you can check the HasExited property.
var isRunning = !process.HasExited;
If it's a quick process, just wait for it.
process.WaitForExit();
If you're starting one up in the background, subscribe to the Exited event after setting EnableRaisingEvents to true.
process.EnableRaisingEvents = true;
process.Exited += (sender, e) => { /* do whatever */ };
Process p = new Process();
p.Exited += new EventHandler(p_Exited);
p.StartInfo.FileName = #"path to file";
p.EnableRaisingEvents = true;
p.Start();
void p_Exited(object sender, EventArgs e)
{
MessageBox.Show("Process exited");
}
Be sure you save the Process object if you use the static Process.Start() call (or create an instance with new), and then either check the HasExited property, or subscribe to the Exited event, depending on your needs.
Assign an event handler to the Exited event.
There is sample code in that MSDN link - I won't repeat it here.
Take a look at the MSDN documentation for the Process class.
In particular there is an event (Exited) you can listen to.
I am making form application, that also runs a console process on different thread. Basically I need to unblock a button after application has exited. Before I made event handler the process after completion just stopped, but now, after event the applications itself is killed.
Here is the code for making process run:
public void CallConsole()//This Calls the console application
{
Thread.CurrentThread.IsBackground = true;
Process p = new Process();
p.StartInfo.UseShellExecute = false;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.RedirectStandardError = true;
p.StartInfo.FileName = filename;
if (checkBox1.Checked)
p.StartInfo.CreateNoWindow = true;
p.EnableRaisingEvents = true;
p.OutputDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
p.ErrorDataReceived += new DataReceivedEventHandler(p_OutputDataReceived);
p.Exited += new EventHandler(p_Exited);
p.Disposed += new EventHandler(p_Exited);
p.Start();
p.BeginErrorReadLine();
p.BeginOutputReadLine();
}
I tried to work with Thread.IsBackground property, but that didn't change anything
Here's the event handler itself:
void p_Exited(object sender, EventArgs e)//Process on exit or disposed will make button1 avalable
{
button1.Enabled = true;
}
Any ideas why the application after adding
p.EnableRaisingEvents = true;
is now killed, not just the process?
The problem here was that
void p_Exited(object sender, EventArgs e)//Process on exit or disposed will make button1 available
{
button1.Enabled = true;
}
required invocation and did not have any sort of error handling. Once I added another function that checks button1.InvokeRequired and in case it does calls itself again trough invocation it worked out great
The problem here is that the Exited event is firing on a thread pool thread. Controls can only be modified on the UI thread.
You could call BeginInvoke, but it's simpler to just configure the Process object to invoke itself by setting:
p.SynchronizingObject = button1;
Button implements ISynchronizeInvoke, which the Process object uses to invoke its events.