views:

509

answers:

5

What I need to know:

I would like to detect when a the main thread (process?) terminates so that I can ensure certain actions are performed before it is terminated.

What I have found myself:

I found the events AppDomain.DomainUnload and AppDomain.ProcessExit. AppDomain.DomainUnload seems to work with non-applications like MbUnit. AppDomain.ProcessExit seems to work with applications but there is a 3 second time limit which I really don't like. Is there more ways to detect when an AppDomain / process terminates?

Background:

I am looking for such an event to ensure my log is persistet to file when the application terminates. The actual logging runs on another thread using a producer-consumer pattern where it is very likely that log entries might queue up in memory and I need to ensure this queue is saved to file when the application terminates.

Is there anything else I should be aware of?

Update:

Changed the above to reflect what I have found out myself. I am not happy with the 3 second time limit during ProcessExit. The MSDN documentation does say though that it can be extended:

The total execution time of all ProcessExit event handlers is limited, just as the total execution time of all finalizers is limited at process shutdown. The default is three seconds, which can be overridden by an unmanaged host.

Does anyone know how to override the default?

More ideas are also highly appreciated!

Follow up:

I have posted a follow up question to this.

+1  A: 

Based on the documentation, it looks like the default application domain (the one your Main method is probably running in) will not receive the DomainUnload event.

I don't know a built-in event that would do what you expect.

You could define your own custom event, have interested parties register with it, and fire off the event just before you return from Main().

Eric J.
Sounds kinda hacky :-). I hope there is a better solution. Thanks for the idea though!
Hermann
Btw, you were right about Domain.Unload not working in the application. Damn.
Hermann
It sounds like DomainUnload is intended to provide an event when additional app domains that your application loads are unloaded. Not sure why that restriction is in place.
Eric J.
+3  A: 

You should have an entry point for your application. Normally you can do there some logging when all tasks are terminated:

static void Main()
{
  try
  {
    Application.Run( .... );
  }
  finally
  {
    // logging ...
  }
}
tanascius
+5  A: 

What exactly do you want to find out?

  • When the process terminates? (Just because the AppDomain is unloaded doesn't necessarily mean that the entire process is terminating)
  • When the main thread terminates (If there are other non-background threads, the main thread can terminate without the process terminating (or AppDomain unloading)

So they're not quite the same thing.

Anyway, it is generally dangerous to have log messages buffered in memory at all. What happens if someone turns off the power? Or if I terminate your process through Task Manager? All your log messages are gone. So often, you'll want unbuffered writes in your log, to get messages pushed to disk immediately.

Anyway, another (more robust) approach might be to run the logger itself in a non-background thread. That way, even if the rest of the application terminates, the logger won't, so the process is kept alive. Then you just have to set some flag when the rest of the app terminates, to let the logger know that it too should close once it has written out all pending log messages.

It still won't save you from the case where the system loses power or someone forcibly termianates the process on the OS-level, but it will handle all cases where the application closes normally, and gives you unlimited time to perform clean-up actions (since the process isn't actually terminating yet, it's still got one live thread)

jalf
That's a very good point about the logging thread not terminating if it's not a BG thread.
Eric J.
Thanks for the clarification. Performance is critical which was the reason I chose to buffer the log messages in memory, but losing it all due to a power outage sucks a lot. The logger is running in a non-background thread right now. I didn't know/notice that it would hold up the process. Do you think implementing IDisposable would be a good idea? I guess the main problem I have is that I don't really understand the difference between an AppDomain and process and what causes them to terminate. I'll have to study that. Thanks for your detailed response!
Hermann
+1 for the foreground (i.e., non-background) thread idea. Every thread created via Thread.Start() is a foreground thread by default, while threads in the ThreadPool are strictly background threads.
Matt Davis
+2  A: 

ie. guaranteed to be called and have unlimited time to finish?

Unfortunately, NO option is going to have unlimited time, and be guaranteed. There is no way to enforce this, as many things can happen. Somebody tripping over the power cord or a forced termination of your program will prevent any option from giving you adequate time to handle things.

In general, putting your logic at the end of the Main routine is probably the most reasonable option, since that gives you complete freedom in handling your termination events. You have no time constraints there, and can have the processing take as much time as needed.

There are no guarantees that this will run, though, since a forceful termination of your program may bypass this entirely.

Reed Copsey
Damn, I hate that you cannot control reality :-).
Hermann
A: 

Hi,

I don't know how old this thread is, but I've had a similar problem whcih was a little tough for me to solve.

I had a WinForms application that was not firing any of the above forementioned events when a user logged out. Wraaping the Application.Run() in a try finally didn't work either.

Now to get around this you would have to using PInvoke into Win32 API's to achieve this. Well you did prior to .NET 2.0 anyways. Luckly MS introduced a new class called SystemEvents. With this class you can catch a SessionEnd event. This event allows you to cleanup when the OS want to terminate your app. There is no .NET time limit o this event it appears, although the OS will eventually kill your app if you take too long. This is a little more than 3 seconds, although 3 seconds should be plenty of time to cleanup.

Secondly my other problem was I wanted my worker thread to terminate the main thread once it was finished its work. With an Application.Run() this was hard to achieve. What I ended up doing was calling Application.Run() with a shared Application context. The thread is then able to call ApplicationContext.ThreadExit() to force the Application.Run to return. This seems to work quite nicely.

Hope this helps someone.

Regards

NozFX

NozFX