.NET Global exception handler in console application
Asked Answered
L

5

222

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, one 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) ?

Edit:

Argh, stupid mistake.
In VB.NET, one needs to add the "AddHandler" keyword in front of currentDomain, or else one doesn't see the UnhandledException event in IntelliSense...
That's because the VB.NET and C# compilers treat event handling differently.

Laocoon answered 28/6, 2010 at 14:25 Comment(0)
B
317

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) {
        System.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);
    }
}

Do keep in mind that you cannot catch type and file load exceptions generated by the jitter this way. They happen before your Main() method starts running. Catching those requires delaying the jitter, move the risky code into another method and apply the [MethodImpl(MethodImplOptions.NoInlining)] attribute to it.

Barfield answered 28/6, 2010 at 14:32 Comment(9)
Argh, stupid mistake, I need to add AddHandler in front of AppDomain.CurrentDomain to see "UnhandledException" in VB.NET...Laocoon
I implemented what you proposed here, but I don't want to exit the application. I just want to log it, and continue the process (without Console.ReadLine() or any other disturbance of program flow. But what I get is the exception re-raising again and again, and again.Mislead
@Shahrooz Jefri: You can't continue once you get an unhandled exception. The stack is messed up, and this is terminal. If you have a server, what you can do in UnhandledExceptionTrapper is restart the program with the same command line arguments.Laocoon
It most certainly does! We're not talking about the Application.ThreadException event here.Barfield
I think the key to understanding this answer and the comments in reply is to realize that this code demonstrates now to detect the exception, but not fully "handle" it as you might in a normal try/catch block where you have the option to continue. See my other answer for details. If you "handle" the exception this way, you have no option to continue running when an exception occurs on another thread. In that sense, it can be said that this answer does not fully "handle" the exception if by "handle" you mean "process and continue running".Oster
@Tomas: That's why there is Application.ThreadException as well.Laocoon
Not to mention there are lots of technologies to run in different threads. For example, the Task Parallel Library (TPL) has its own way to catch unhandled exceptions. So, saying this doesn't work for all situations is kind of ludicrous, there is NO one place catch-all for everything in C#, but depending on the technologies you use there are various places you can hook into.Morganica
@StefanSteiger Application.ThreadException is for form application and it is not working in Console app. Easy to test, just assign Application.ThreadException event and call Task.Factory.StartNew(() => { throw new Exception("Test");}); and event Application.ThreadException is not fired.Eanore
@HansPassant: you have deleted the answer that you link to. Is there a particular reason, e.g. the answer over there is incorrect? As a 10k user I can still see it and I wonder whether the approach of delaying the JITter is basically ok.Morpheus
O
25

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");
   }
}
Oster answered 29/6, 2010 at 0:17 Comment(3)
TRY CATCH does not work in release mode for unexpected errors :/Swinish
Argh this is really bad to encourage as a matter of course.. Sooner or later your app will have a bug introduced in a thread where it'll stop halfway through then silently exit and you'll assume it worked fully, and you'll spend ages trying to figure it out before remembering you foolishly suppressed all exceptions.Sierrasiesser
@ChrisKuliukas The exceptions are not being suppressed. They are being output to the console. You're more likely to see it this way than if your application exited without handling the exception.Oster
M
12

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.

Monoxide answered 28/6, 2010 at 15:24 Comment(0)
A
1

I just inherited an old VB.NET console application and needed to set up a Global Exception Handler. Since this question mentions VB.NET a few times and is tagged with VB.NET, but all the other answers here are in C#, I thought I would add the exact syntax for a VB.NET application as well.

Public Sub Main()
    REM Set up Global Unhandled Exception Handler.
    AddHandler System.AppDomain.CurrentDomain.UnhandledException, AddressOf MyUnhandledExceptionEvent

    REM Do other stuff
End Sub

Public Sub MyUnhandledExceptionEvent(ByVal sender As Object, ByVal e As UnhandledExceptionEventArgs)
    REM Log Exception here and do whatever else is needed
End Sub

I used the REM comment marker instead of the single quote here because Stack Overflow seemed to handle the syntax highlighting a bit better with REM.

Agamemnon answered 19/6, 2020 at 19:58 Comment(0)
S
-13

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.

Strive answered 28/6, 2010 at 14:32 Comment(13)
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.Ripping
@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.Strive
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).Ripping
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.Strive
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.)Oster
@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.Strive
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).Oster
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.Oster
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.Ripping
@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.Strive
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?Oster
@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).Strive
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. :)Strive

© 2022 - 2024 — McMap. All rights reserved.