I'm guessing a few of you may have seen this dialog before...
This is what you get when there is an unhandled exception in your .NET code that's running in ArcMap. And it's not exactly handy for an end user - what does a stack trace mean to them? Not much. So the question becomes - can we replace this dialog with our own, have it inform the user gracefully that an error has occured, all the while logging the stack trace and other useful information for the developers/administrators.
I should stop here and note that what I'm trying to catch are truly unhandled exceptions. Generally issues which occur deep within ArcGIS, and result in some kind of un-anticipated failure in our custom .NET code. Even with a reasonable amount of try/catch blocks, these exceptions do occur from time to time, and they are exceptionally difficult to fix.
In this post, I'm going to tackle intercepting the unhandled exception. Once you've caught the exception and collected the information you need, there are any number of ways to log the information - custom web services, Enterprise Library Logging Block, Log4Net etc. In a future post, I'll cover some of these.
Catching "Unhandled" ExceptionsFrom the .NET side of things there are two ways you can "catch" unhandled exceptions. I say "catch" because you can't actually catch them in the typical try/catch/finally sort of scenario. At .NET 2.0 an unhandled exception will allways cause the application to exit. While the CLR is shutting things down, it raises events which you can provide handlers for.
1) AppDomain.UnhandledException EventThis is the king-kahuna, and the story should end right here. Basically, if an unhandled exception occurs in the AppDomain, the AppDomain.UnhandledException Event gets raised. At which time you can log whatever you want. Great - problem solved and this post is over. Only from my testing, this just does not work within ArcMap. After some head scratching, I read this curious little note in the docs on MSDN...
For certain application models, the UnhandledException event can be preempted by other events if the unhandled exception occurs in the main application thread.
No indication as to what other events these may be, and I'm not sure what's going on in ArcMap (maybe someone from ESRI can shed some light?) but the end result is that this event never fires. Thus, we move on to...
2) Application.ThreadExceptionWhile somewhat limiting since it only responds to unhandled exceptions that are on Windows.Forms threads Application.ThreadException Event provides an opportunity to log the exception before the application exits. And it does work in ArcMap, so it's part of a solution.
Example ProjectSince I figured this all out by writing some "research" (read: hacker) code, I thought I'd share it - maybe someone else can figure out how to get those other unhanded exceptions.
About the code - this is VB.NET, written for ArcGIS Desktop 9.2. It has a total of 5 commands, and an Extension. My goal was to find a way to centrally handle these exceptions - specifically in my extension.
The first command I created was the NotHandledCommand - which simply throws an exception in the on_click event. This was my baseline to see if I could catch this from my extension. The answer is no - you can't catch an exception that occurs in the command class itself. More interesting is that throwing an exception in an ICommand does not cause a JIT window to appear. It looks like ArcMap is eating the exception. Since I created my first command by inheriting from BaseCommand, I created another command that directly implements ICommand - and same thing. I can see why ESRI may want to do this (kepp ArcMap stable), but if you are going to eat exceptions, please raise an event so developers can know that an exception has occured.
Anyhow - to address this, we're creating some new Command/Tool templates which will have try/catch blocks with the correct logging code baked in (I'll do a post about creating custom visual studio templates too because this is super handy).
The next set of commands, ExceptionFormCommand and ExceptionFormTwoCommand simply open up forms, which have a single button which throws an exception. As I noted above, since this code is running on a System.Windows.Forms thread, the exception is caught and the Application.ThreadException Event is raised. In the extension. I created two commands simply to test it a little more. I then took this a step further and created a dockable window (ExceptionDocWinCommand) which also just raises an exception, and yes it also works. Then, to finish it off, I created a user control (button that throws an exception), which I then put on the dockable window - and yes - that worked too.
So - you can download the code below. Compile in Visual Studio 2005, run ArcMap and you'll need to put the commands onto toolbars manually - they're all in the "AAA Research" category.
And if anyone knows how to correctly wire up the AppDomain.UnhandledException event, or another way to catch unhandled exceptions please let me know.
Download Sample Code
I'm Dave and this is my blog. I'm usually writing about .NET Software Development, ArcGIS, or Agile Practices, but other stuff does creep in from time to time. I hope you find something of use, and feel free to contact me if you have any questions. You can also check out my profile on LinkedIn
dojo.DTSAgile.com is our technology preview / demo site. As I and my team cook up cool things we post them here.
ArcDeveloper.net is a site that hosts a set of open source projects related to ArcGIS. This includes Tile Cache for .NET (TC4N) and Feature Server for .NET (FS4N). Come over and check it out!
Assembla is a free service that provides Subversion source control, wikis and work Tracking. The ArcDeveloper project is run from here. It rocks. Check them out today.
Agilistas is a LinkedIn group focused on discussing and promoting Agile practices. Everyone is welcome to join in the conversation as we evolve the process of creating software to make it more enjoyable for all involved.