mjFeik
mjFeik

Reputation: 21

Process.Kill() related crashes

Or not!

I have a fairly simple application timer program. The program will launch a user selected (from file dialog) executable and then terminate the process after the user specified number of minutes. During testing I found that a crash occurs when I call the Process.Kill() method and the application is minimized to the system tray.

The executable in question is Frap.exe which I use frequently and is the reason I wrote the app timer in the first place. I always minimize fraps to the tray, and this is when the crash occurs.

My use of Kill() is straight forward enough...

while (true)
        {
            //keep checking if timer expired or app closed externally (ie. by user)
            if (dtEndTime <= DateTime.Now || p.HasExited)
            {
                if (!p.HasExited)
                    p.Kill();
                break;
            }
            System.Threading.Thread.Sleep(500);
        }

In searching for alternatives methods to close an external application programmatically, I found only Close() and Kill() (CloseMainWindow is not helpful to me at all). I tried using Close(), which works providing the application is minimized the tray. If the app is minimized, Close() doesn't cause a crash but the app remains open and active. One thing I noticed in a few posts posts regarding closing external applications was the comment: "Personally I'd try to find a more graceful way of shutting it down though." made in THIS thread found here at stack flow (no offense to John). Thing is, I ran across comments like that on a few sites, with no attempt at what a graceful or elegant (or crash-free!!) method might be.

Any suggestions?

The crash experienced is not consistant and I've little to offer as to details. I am unable to debug using VS2008 as I get message - cant debug crashing application (or something similar), and depending on what other programs I have running at the time, when the Kill() is called some of them also crash (also programs only running in the tray) so I'm thinking this is some sort of problem specifically related to the system tray.

Upvotes: 2

Views: 5125

Answers (3)

JMarsch
JMarsch

Reputation: 21743

Is it possible that your code is being executed in a way such that the Kill() statement could sometimes be called twice? In the docs for Process.Kill(), it says that the Kill executes asynchronously. So, when you call Kill(), execution continues on your main thread. Further, the docs state that Kill will throw a Win32Exception if you call it on an app that is already in the process of closing. The docs state that you can use WaitForExit() to wait for the process to exit. What happens if you put a call to WaitForExit() immediately following the call to Kill(). The loop looks ok (with the break statement). Is it possible that you have code entering that loop twice?

If that's not the problem, maybe there is another way to catch that exception: Try hooking the AppDomain.CurrentDomain.UnhandledException event (currentDomain is a static member)

The problem is that Kill runs asynchronously, so if it's throwing an exception, it's occurring on a different thread. That's why your exception handler doesn't catch it. Further (I think) that an unhandled async exception (which is what I believe you have) will cause an immediate unload of your application (which is what is happening).

Edit: Example code for hooking the UnhandledExceptionEvent Here is a simple console application that demonstrates the use of AppDomain.UnhandledException:


using System;
public class MyClass
{
    public static void Main()
    {
        System.AppDomain.CurrentDomain.UnhandledException += MyExceptionHandler;
        System.Threading.ThreadPool.QueueUserWorkItem(DoWork);
        Console.ReadLine();
    }

    private static void DoWork(object state)
    {
        throw new ApplicationException("Test");
    }

    private static void MyExceptionHandler(object sender, System.UnhandledExceptionEventArgs e)
    {
        // get the message
        System.Exception exception = e.ExceptionObject as System.Exception;
        Console.WriteLine("Unhandled Exception Detected");
        if(exception != null)
            Console.WriteLine("Message: {0}", exception.Message);
        // for this console app, hold the window open until I press enter
        Console.ReadLine();
    }

}

Upvotes: 2

mjFeik
mjFeik

Reputation: 21

I dont think I should claim this to be "THE ANSWER" but its a decent 'work around'. Adding the following to lines of code...

p.WaitForInputIdle(10000);
am.hWnd = p.MainWindowHandle;

...stopped the crashing issue. These lines were placed immediately after the Process.Start() statement. Both lines are required and in using them I opened the door to a few other questions that I will be investigating over the next few days. The first line is just an up-to 10 second wait for the started process to go 'idle' (ie. finish starting). am.hWnd is a property in my AppManagement class of type IntPtr and this is the only usage of both sides of the assignment. For lack of better explaination, these two lines are analguous to a debouncing method.

I modified the while loop only slightly to allow for a call to CloseMainWindow() which seems to be the better route to take - though if it fails I then Kill() the app:

while (true)
{
   //keep checking if timer expired or app closed externally (ie. by user)
   if (dtEndTime <= DateTime.Now || p.HasExited)  {
      try {
          if (!p.HasExited)  // if the app hasn't already exitted...
          {
             if (!p.CloseMainWindow())  // did message get sent? 
             {
                if (!p.HasExited)  //has app closed yet?
                {
                   p.Kill();  // force app to exit
                   p.WaitForExit(2000);  // a few moments for app to shut down
                }
             }
             p.Close();  // free resources
          }                        
       }
      catch { // blah blah }
      break;
   }
   System.Threading.Thread.Sleep(500);
}

My initial intention for getting the MainWindowHandle was to maximize/restore an app if minimized and I might implement that in the near future. I decided to see if other programs that run like Fraps (ie, a UI but mostly run in the system tray (like messanger services such as Yahoo et al.)). I tested with XFire and nothing I could do would return a value for the MainWindowHandle. Anyways, this is a serperate issue but one I found interesting.

PS. A bit of credit to JMarsch as it was his suggestion RE: Win32Exception that actually lead me to finding this work around - as unlikely as it seems it true.

Upvotes: 0

Jeff Youel
Jeff Youel

Reputation: 653

My first thought is to put a try/catch block around the Kill() call and log the exception you get, if there is one. It might give you a clue what's wrong. Something like:

try 
{
    if(!p.HasExited)
    {
        p.Kill();
    }
    break;
}
catch(Exception ex)
{
    System.Diagnostics.Trace.WriteLine(String.Format("Could not kill process {0}, exception {1}", p.ToString(), ex.ToString()));
}

Upvotes: 1

Related Questions