Gotta Catch 'Em All! - Global Exception Handling in C#
June 8, 2024
• 1,154 views
We've seen how try/catch/finally works in C#... but what about those exceptions that are still sneaking through and crashing your program? Isn't there a way that we can know when ANY exception is thrown in our C# applications?
Well this isn't a solution for recovering your program from any problem, but it might help you diagnose it more easily! In this video, I'll walk you through global exception handling in C# and a second tip for how to do this with pesky runaway tasks!
View Transcript
so you've been breaking all of the rules about exceptions you've been sprinkling try catch with empty exception handlers all over your code base and yet there are still some exceptions that are crashing your program what can you possibly do to catch these exceptions hi my name is Nick centino and I'm a principal software engineering manager at Microsoft no I'm not recommending that you put TR catches around everything in your code base but of course as we are building more bigger and complex applications there are situations where you want to be able to have Global exception handles so in this video I want to introduce to you some Concepts that you can put into place in your applications to have Global exception handlers this could be really helpful for Telemetry and logging and that way when you have distributed applications you can get more insights when
your application is doing something that you don't expect a quick reminder that if you like this kind of content subscribe to the channel and check out that pined comment for my courses on dome train now let's jump over to visual studio and throw some exceptions all right on my screen I have a pretty sad program this is just going to throw an invalid operation exception and says oh no of course if we go run this application as you might expect right away it crashes right this is not new or surprising to us and of course what we would probably want to do if this was somewhere in an application where this exception could be thrown right obviously we wouldn't just want to throw it and catch it but if you needed to catch something like this you would just wrap it in a TR catch
of course right you would have this around the whole thing so try catch you'd put whatever type of exception filter you'd want and this would live inside side here right so this would allow you to catch the exception again not very surprising but applications get a lot more complex than just a single line that throws an exception we do have the ability to put a global exception Handler in place and I'm not suggesting that you do this somehow try and recover your application necessarily but this can be really good as a Last Stand to try and get some information before your application is torn down the way that this looks is that we can use the app domain current domain and then put an unhandled exception Handler on top of this and that way when this gets triggered which is again when an exception is
thrown that's as you might expect not handled we will get this event Handler fired and if you're not familiar with event handlers this is basically just a delegate and it uses the special syntax it's plus equals and that way you can actually chain event handlers together so you could essentially if you needed to for some reason you could do another one of these after and you could plus equals onto it and do another thing like that right so these get chained together but we're not doing that in this example I just wanted to be able to show you that you can hook up this event handler and do what you would need to do inside of here in this example we're just going to write some information out to the console but if you had a web application or something else that was running and
distributed you might say hey we got to write to the the log file we have to try and do some last attempt at getting some Telemetry up so that we have information about this crash there are all sorts of different things that you might want to do but I highly recommend that you know trying to necessarily recover your application when it's already in this state where who knows what's happening probably not going to be super trivial to do probably not a great option but having some attempt at getting extra information so when you want to go diagnose this I think that could be a good approach if we go run this now what we should be able to see is we press play and we get the exception thrown but you might have noticed very quickly right we do see system invalid operation exception so
if I put these together here I'll zoom in a little bit too you can see we get the exception object so that's the first one right here that's that line and then we get the trace right below it so when we print out the whole exception object this is essentially the two string coming from uh rxo exception object on line three and then is terminating is the very next line so this is terminating it is going to crash the application and that way if I go ahead and press F5 to continue right the application's done executing so it's kind of cool you end up getting to intercept when this is happening right the debugger was still attached the application actually had not exited yet but we did get this event handler triggered so you get this last opportunity to try and log something send Telemetry
but like I said recovery probably not a great option because who knows what is happening but before we continue on this is just a quick reminder that I do have courses available on dome train if you're just getting started in your programming journey and you want to learn C you can head over to D train I have a getting started in C course it's approximately 5 hours of content taking you from absolutely no experience to being able to program in C and after that I have my deep dive course which will take you to the next level with another 6 hours of content so that you can start building basic applications head over to D pran and check it out let's head back to the video I want to talk about something else next because it's very common now that we are doing a snle
weight in our applications we're using tasks and things like that but there are situations that we can have tasks do something totally nasty and we have a task that is running not being awaited for some reason and has an exception being thrown when this type of thing happens you can have some really weird unexpected behavior in your application if it's not crashing your app outright in this example I wanted to show you that I have a task being run here it's not being awaited you can see that Visual Studio is actually telling us like hey you probably aren't doing what you think you want to do here and that's because it's not being now you don't necessarily have to await a task it's just that more often than not you probably do you probably do need to make sure that this task is running being
tracked and being able to be cleaned up later so what this example will do is it's going to run this task and after 3 seconds it's going to throw an exception right so this is going to go run on a different thread and then I have this wild true Loop here and basically every 5 seconds it's just going to wake up and wait again so it's just going to keep the application alive so if we go ahead and run this now we should see that if we count to 1 2 3 it'll throw an exception and what we can see here is if I press continue the application's still running right so if you had something running on a task somewhere and it threw an exception this is basically going completely unnoticed not a great thing right so if you had some behavior that you
were truly waiting for on this task what's actually happening right are we still waiting for this thing to finish did it finish processing and writing some data somewhere or is it just gone and we have no idea what the state is right it's the latter we have no idea what's up I mean we do cuz we saw it actually throw the exception but I think you get the idea if this kind of thing happens in your application which should you be expecting to do if you want to catch and deal with this type of thing now of course the obvious thing you might say is well just go ahead and await this do the right thing that's going to change the behavior in this case and like I said you may have situations where you do have Tas running for whatever reason I can't predict
everything that you want to do but if this happens and you want to have this type of behavior cuz if I go put this back in and I go do this again what happens exceptions thrown but notice our current domain unhandled exception Handler did not fire that little trick that we put in to say hey we should catch exceptions when the application's about to crash nope that's not triggering so it's not enough to catch these types of things on tasks but there is yet another Handler that we can go use to help us here and here we go it's very similar but on the task scheduler we can use unobserved task exception very same or similar at least idea here where we have a vent Handler syntax with a sender and some event arguments the event arguments in this case are a little bit different
we have the exception and then whether or not it's been observed we can go check this out and run it and see what happens so let's go see exceptions thrown but wait a second nothing got printed into the console why did that happen right we got to see that we have this Handler up here the top we know that's not triggering it because we already ran that and then we added this other one so we have both event handlers hooked up right we have the first one but we know that wasn't going to work because we already saw us run and that wasn't being triggered and then we added this other one for the unobserved task exception that's supposed to help us here but why didn't it Why didn't it go run this code inside of this event handler from line 10 to 14 when
that's really what it's supposed to be doing for us and that's because the garbage collector didn't run and that's only going to trigger this when the garbage collector runs and realizes that it has a task that it needs to go clean up and there were some uncaught exceptions so there's something we can do just to simulate this if we put a garbage collector collect in here if we go run this now we should see the behavior that we're expecting there's the exception being thrown and the garbage collector ends up running after 5 seconds and we can see that we have have the same type of information being printed out we can see that we're writing out the arguments exception and that's going to be this information all here with the trace and then it is observed is set to false in this case this will
allow us to again get that kind of last effort stand on this task it's not crashing the application right but we can get this last attempt before the task is cleaned up to say hey what's going on here something was unobserved maybe we should go log some information send some Telemetry do something about that so these are two different strategies that you can use separately or together in your applications if you want to be able to get some extra information in scenarios where your application's crashing or doing something unexpected the first instance that we looked at was when your app is truly crashing and you have this last stand to try and log some information when the app is being torn down and the other one is specific to tasks and if those tasks have unobserved exceptions when the garbage collector is coming around to
clean them up that's when that other Handler will go run if you found this helpful and you want to learn more about different exception handling patterns you can check out this video next thanks and I'll see you next time
Frequently Asked Questions
What is global exception handling and why is it important?
Global exception handling allows me to catch unhandled exceptions across my application in one centralized place. This is important because it helps me log errors and gather telemetry data when unexpected issues occur, especially in complex applications.
How can I implement a global exception handler in my C# application?
I can implement a global exception handler by using the AppDomain.CurrentDomain.UnhandledException event. This allows me to set up an event handler that will be triggered whenever an unhandled exception occurs, giving me a chance to log the error before the application crashes.
What should I do about exceptions thrown in tasks that are not awaited?
For exceptions thrown in tasks that are not awaited, I can use the TaskScheduler.UnobservedTaskException event. This event allows me to handle exceptions that occur in tasks that were not properly observed, giving me a chance to log the error when the garbage collector cleans up the task.
These FAQs were generated by AI from the video transcript.