views:

134

answers:

4

Question: I want to define a global exception handler for unhandled exceptions in my console application. In asp.net, one can define one in global.asax, and in windows applications /services, on can define as below

AppDomain currentDomain = AppDomain.CurrentDomain;
currentDomain.UnhandledException += new UnhandledExceptionEventHandler(MyExceptionHandler);

But how can I define a global exception handler for a console application ?
currentDomain seems not to work (.NET 2.0) ?

+3  A: 

No, that's the correct way to do it. This worked exactly as it should, something you can work from perhaps:

using System;

class Program {
    static void Main(string[] args) {
        AppDomain.CurrentDomain.UnhandledException += UnhandledExceptionTrapper;
        throw new Exception("Kaboom");
    }

    static void UnhandledExceptionTrapper(object sender, UnhandledExceptionEventArgs e) {
        Console.WriteLine(e.ExceptionObject.ToString());
        Console.WriteLine("Press Enter to continue");
        Console.ReadLine();
        Environment.Exit(1);
    }
}
Hans Passant
Argh, stupid mistake, I need to add AddHandler in front of AppDomain.CurrentDomain to see "UnhandledException" in VB.NET...
Quandary
+1  A: 

What you are trying should work according to the MSDN doc's for .Net 2.0. You could also try a try/catch right in main around your entry point for the console app.

static void Main(string[] args)
{
    try
    {
        // Start Working
    }
    catch (Exception ex)
    {
        // Output/Log Exception
    }
    finally
    {
        // Clean Up If Needed
    }
}

And now your catch will handle anything not caught (in the main thread). It can be graceful and even restart where it was if you want, or you can just let the app die and log the exception. You woul add a finally if you wanted to do any clean up. Each thread will require its own high level exception handling similar to the main.

Edited to clarify the point about threads as pointed out by BlueMonkMN and shown in detail in his answer.

Rodney Foley
Exceptions can actually still be thrown outside of the Main() block, unfortunately. This isn't actually a "catch all" like you might think. See @Hans' answer.
Mike Atlas
@Mike First I said the way he is doing it is correct, and that he could try a try/catch in the main. I am not sure why you(or someone else) gave me a vote down when I was agreeing with Hans just providing another answer that I wasn't expecting to get a check for. That is not really fair, and then to say the alternative is wrong without providing any proof as to how an exception that can be caught by the AppDomain UnhandledException process that a try/catch in Main can't catch. I find it rude to say something is wrong without proving why it is wrong, just saying it is so, doesn't make it so.
Rodney Foley
It's fairly obvious you just downvoted 4 of my old posts from 2009 in retaliation. Consider editing your post and I'll undo my downvote (and you should undo your rage-downvotes you gave me, I'll do the same).
Mike Atlas
How can I change it since you have not provided any proof that I said anything invalid or deserving of a down vote. I am not sure what it would be changed to. So I can edit it just to edit it so you can undo yours and if you tell me what other of mine you down voted I can edit them as well. Once your down votes have been removed and you edit yours I will remove mine.
Rodney Foley
I have posted the example you are asking for. Please be responsible and remove your irrelevant down votes from Mike's old answers if you haven't. (No personal interest, just don't like seeing such abuse of the system.)
BlueMonkMN
@BlueMonkMN, no offense but Mike brought it on himself, I personally never down vote people unless they are being a complete idiot or violating the rules. I usually let the person who asked the question make comments and let the up voting system rule along with the check for the answer used by the one who asked the question. To just down vote an answer from someone truly trying to help is simply being an a-hole for no reason. People like that should not be allowed to get away with it so it seemed only fair to down vote them for simply trying to help others.
Rodney Foley
Yet you still play the same "game" he does, only in a worse way because it's pure retaliation, not based on the quality of an answer. That's not a way to solve the problem, only make it worse. It's especially bad when you retaliate against someone who even had a legitimate concern about your answer (as I have demonstrated).
BlueMonkMN
Oh, I would also add that down-voting is not intended for people who are "being a complete idiot or violating the rules", but rather to judge the quality of an answer. It seems to me that down-voting answers in order to "comment" on the person providing them is a much bigger abuse than down-voting answers based on the content of the answer itself regardless of whether that vote is accurate or not. Don't take/make it so personal.
BlueMonkMN
Thanks BlueMonkMN for providing an example. Gnome, I've upvoted several of your old accepted answers to other questions as a truce. Consider doing the same for me.
Mike Atlas
@Mike I will up vote to counter the downs.
Rodney Foley
@Blue and @Mike I would like to go back to my answer and defend it with the fact that standards practices for a console app is to ALWAYS have try/catch in the main. That there is only a small number of AppDomain exceptions that can not be caught by the main try/catch and must be handled via the UnhandledException option. @Blue your explain is not a very good one because you are not following good design for not trapping exceptions in threads you create since they cannot be caught outside of the thread.
Rodney Foley
Your statement is unclear: do you mean I am not following a good design because one of my examples had an exception handler in each thread, or do you mean that .NET is badly designed because there is no way to trap exceptions from other threads? I showed 3 examples and described the behavior of each. The first, I believe, corresponds to your example, the second adds the "UnhandledException" handler and describes its behavior, and the last shows what I consider "proper" exception handling in cases where you have multiple threads in a console application. Is there something I missed?
BlueMonkMN
@Blue That you are pointing out that every thread needs its own exception handler. This would be a try/catch inside at the beginning of the thread. What I guess I am saying is that we agree on the proper behavior, yet some reason you disagree with me. I was talking about a simple console app as the question didn't state anything about threads. You took it to the additional corner case that includes threads. You agree that try/catch is the right way to do this and not with UnhandledException (if I understand correctly).
Rodney Foley
Updated the answer to clarify the point for those who assume I through this would catch exceptions in other threads even though I never mentioned threading or the question mentioned threading. :)
Rodney Foley
THE HORSE: IT IS DEAD!
Mike Atlas
+2  A: 

You also need to handle exceptions from threads:

static void Main(string[] args) {
Application.ThreadException += MYThreadHandler;
}

private void MYThreadHandler(object sender, Threading.ThreadExceptionEventArgs e)
{
    Console.WriteLine(e.Exception.StackTrace);
}

Whoop, sorry that was for winforms, for any threads you're using in a console application you will have to enclose in a try/catch block. Background threads that encounter unhandled exceptions do not cause the application to end.

David
+4  A: 

If you have a single-threaded application, you can use a simple try/catch in the Main function, however, this does not cover exceptions that may be thrown outside of the Main function, on other threads, for example (as noted in other comments). This code demonstrates how an exception can cause the application to terminate even though you tried to handle it in Main (notice how the program exits gracefully if you press enter and allow the application to exit gracefully before the exception occurs, but if you let it run, it terminates quite unhappily):

static bool exiting = false;

static void Main(string[] args)
{
   try
   {
      System.Threading.Thread demo = new System.Threading.Thread(DemoThread);
      demo.Start();
      Console.ReadLine();
      exiting = true;
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception");
   }
}

static void DemoThread()
{
   for(int i = 5; i >= 0; i--)
   {
      Console.Write("24/{0} =", i);
      Console.Out.Flush();
      Console.WriteLine("{0}", 24 / i);
      System.Threading.Thread.Sleep(1000);
      if (exiting) return;
   }
}

You can receive notification of when another thread throws an exception to perform some clean up before the application exits, but as far as I can tell, you cannot, from a console application, force the application to continue running if you do not handle the exception on the thread from which it is thrown without using some obscure compatibility options to make the application behave like it would have with .NET 1.x. This code demonstrates how the main thread can be notified of exceptions coming from other threads, but will still terminate unhappily:

static bool exiting = false;

static void Main(string[] args)
{
   try
   {
      System.Threading.Thread demo = new System.Threading.Thread(DemoThread);
      AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
      demo.Start();
      Console.ReadLine();
      exiting = true;
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception");
   }
}

static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
{
   Console.WriteLine("Notified of a thread exception... application is terminating.");
}

static void DemoThread()
{
   for(int i = 5; i >= 0; i--)
   {
      Console.Write("24/{0} =", i);
      Console.Out.Flush();
      Console.WriteLine("{0}", 24 / i);
      System.Threading.Thread.Sleep(1000);
      if (exiting) return;
   }
}

So in my opinion, the cleanest way to handle it in a console application is to ensure that every thread has an exception handler at the root level:

static bool exiting = false;

static void Main(string[] args)
{
   try
   {
      System.Threading.Thread demo = new System.Threading.Thread(DemoThread);
      demo.Start();
      Console.ReadLine();
      exiting = true;
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception");
   }
}

static void DemoThread()
{
   try
   {
      for (int i = 5; i >= 0; i--)
      {
         Console.Write("24/{0} =", i);
         Console.Out.Flush();
         Console.WriteLine("{0}", 24 / i);
         System.Threading.Thread.Sleep(1000);
         if (exiting) return;
      }
   }
   catch (Exception ex)
   {
      Console.WriteLine("Caught an exception on the other thread");
   }
}
BlueMonkMN