Application exits on process exited event - c#

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.

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

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.

C# process.start, how do I know if the process ended?

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.

Starting processes then raising events to monitor them

I am starting a process that runs an external 3rd party package. I would then like to start a timer that fires an event every few seconds to monitor the output of this process. Here's the code I use to start the process:
Process process = new Process();
process.StartInfo.FileName = exe;
process.StartInfo.Arguments = args;
process.StartInfo.CreateNoWindow = true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
process.BeginErrorReadLine();
RuntimeMonitor rtm = new RuntimeMonitor(OutputFile(), InputFile(), 5000);
process.WaitForExit();
rtm.Close();
The constructor for the runtime monitor is:
public RuntimeMonitor(string _outfile,
string _title,
double intervalMs) // in milliseconds
{
outFile = outfile;
title = _title;
timer = new System.Timers.Timer();
timer.Interval = intervalMs;
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_func);
timer.AutoReset = true;
timer.SynchronizingObject = null;
timer.Start();
}
The symptom I am getting is that timer_func is not called until the process ends (i.e. after process.WaitForExit(); completes). I thought the timer elapsed event fired asynchronously, but it seems to be locked
EDIT: well it gets stranger. I did try setting timer.SynchronizingObject to null, but to no avail. I also tried using the System.Threading.Timer class and got the same result.
One more piece of information. I do issue a call to start gathering data from the process, but not sure that is relevant (i.e. the BeginErrorReadLine call in the code above)
I am not sure why your timer does not appear to be firing. I took your code and structured it a bit to try and test this out. The following code works just fine. Perhaps you can use it to get your's working.
void Main()
{
Process process = new Process();
process.StartInfo.FileName = "notepad.exe";
process.StartInfo.CreateNoWindow = true;
process.StartInfo.WindowStyle = ProcessWindowStyle.Hidden;
process.StartInfo.UseShellExecute = false;
process.StartInfo.RedirectStandardError = true;
process.StartInfo.RedirectStandardOutput = true;
process.Start();
RuntimeMonitor rtm = new RuntimeMonitor(5000);
process.WaitForExit();
rtm.Close();
}
public class RuntimeMonitor
{
System.Timers.Timer timer;
// Define other methods and classes here
public RuntimeMonitor(double intervalMs) // in milliseconds
{
timer = new System.Timers.Timer();
timer.Interval = intervalMs;
timer.Elapsed += new System.Timers.ElapsedEventHandler(timer_func);
timer.AutoReset = true;
timer.Start();
}
void timer_func(object source, object e)
{
Console.WriteLine("Yes");
}
public void Close()
{
timer.Stop();
}
}
I know your code shows that you create the timer inside the RuntimeMonitor constructor, but just in case - check the Timer.SynchronizingObject property of your Timer and make sure it is null. If it is not, then that might explain your situation. Note using the Timer in the designer will set the property as well - From MSDN:
If the Timer is used inside Visual Studio in a Windows Forms designer, SynchronizingObject is automatically set to the control that contains the Timer. For example, if you place a Timer on a designer for Form1 (which inherits from Form), the SynchronizingObject property of Timer is set to the instance of Form1.

Categories