PictureBox freezes while executing process - c#

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

Related

How to open .exe files from C# one by one? [duplicate]

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

Why does the Process.Exited event fire two times?

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).

strange behavior when running process using process class vs cmd c#

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.

Is there a way to switch from a Process thread back to the thread which originally created the Process upon exit?

public class SomeClass {
static void Method() {
Process p = new Process();
p.StartInfo.RedirectStandardError = true;
p.StartInfo.RedirectStandardOutput = true;
p.StartInfo.UseShellExecute = false;
p.StartInfo.CreateNoWindow = true;
p.StartInfo.FileName = "a command line util";
p.StartInfo.Arguments = "some arguments";
p.EnableRaisingEvents = true;
p.OutputDataReceived += (sender, e) => {
Debug.Log(e.Data);
};
p.ErrorDataReceived += (sender, e) => {
Debug.Log(e.Data);
};
Thread mainThread = Thread.CurrentThread;
p.Exited += (sender, e) => {
if (p.ExitCode == 0) {
// Need to do something in the thread which originally created p.
// I even got mainThread from above, but don't know if I can 'switch' to it.
}
};
p.Start();
p.BeginOutputReadLine();
p.BeginErrorReadLine();
}
My problem is basically this:
I'm in the "main" thread and create a process and start it.
I got about getting async events from the process.
By the time the process exits, the callback is still in a process's thread, but I need to do some other stuff in the "main" thread.
In case anyone is wondering why the static and why the need for all this, it's cause I'm on Unity (the game engine) and need a static method for executing code in a menu item. The menu item needs to generate an assets from a command line util and then cause Unity to refresh its assets dir to find the new asset. The problem is any calls to Unity code need to execute in the main thread and the minute I start getting the Process callbacks I'm no longer in the main thread.
UPDATE Just in case, I'm on Mono for Unity, which is aprox. .NET 3.5 without Windows specific stuff. So for instance, the Dispatcher class from System.Windows is not available.

Is it possible to figure out when all Control.BeginInvoke(s) have been completed?

In my current project I'm taking a command prompt and pretty much displaying it on a richTextBox based on an input typed in a textBox and a button is pressed.
See Having trouble with Process class while redirecting command prompt output to winform
One small update I want to make (might not be a particularly small update code-wize) is to have the button in a "disabled" state while the command prompt is doing it's execution. Since the project uses "Control.BeginInvoke" to update the text on the richTextBox, it does a "fire and forget." This means there isn't really a way I can re-enable a disabled button once all the "BeginInvokes" have been processed to the UI.
I guess the question is, is it possible to get a callback once all the "BeginInvokes" have been executed and say "Hey I'm done, here is your button back." This will prevent a user from hitting the button sending duplicate processes.
Here is a snippet of the code I'm using:
public void GetConsoleOuput(string command = null)
{
ProcessStartInfo startInfo = new ProcessStartInfo();
startInfo.FileName = "cmd.exe";
startInfo.RedirectStandardOutput = true;
startInfo.WindowStyle = ProcessWindowStyle.Hidden;
startInfo.UseShellExecute = false;
startInfo.CreateNoWindow = true;
if (!string.IsNullOrEmpty(command))
{
startInfo.Arguments = command;
}
Process process = new Process();
process.StartInfo = startInfo;
process.OutputDataReceived += new DataReceivedEventHandler(AppendRichBoxText);
process.Start();
process.BeginOutputReadLine();
process.Close();
}
public void AppendRichBoxText(object sendingProcess, DataReceivedEventArgs outLine)
{
string outputString = args.Data;
MethodInvoker append = () => richTextBox.AppendText(outputString);
richTextBox.BeginInvoke(append);
}
// Would like EventHandler method to enable button once all "BeginInvokes" are
// done running asynchronously due to a callback.
public void EnableButton
{
/// re-enable a disabled button
}
Just call BeginInvoke again after all of your updates to call EnabledButton afterwards.

Categories