Tuesday, July 27, 2010

Code Review: Catching Unhandled Exceptions in .NET

This is a post about a very obscure topic that I normally would avoid, but since I spent the last several days dealing with it, I thought I would share the love.  If I ran into this problem with a very simple .NET eConnect integration, anyone could run into it.

I recently deployed three eConnect 9 imports at a client site, just like I've done with dozens and dozens of .NET eConnect integrations.

The client uses Citrix, and has two load balanced application servers for Dynamics GP.  To install my three imports, we had to install them on both of the Citrix servers.  The installs went fine, the apps all appeared to work well, and I thought my work was done.

Until the key business user launches one of the imports and gets an error.  The app doesn't launch, no windows appear, she just sees a "crash" error message.


After further testing, we find that the app launches fine on Citrix server #1, but gives her the error on Citrix server #2.  Exact same app.

I then check Event Viewer and see some arcane error messages related to the application crash.  They are basically generic .NET errors that don't provide any guidance as to the cause of the crash.


EventType clr20r3, P1 invoiceimport.exe, P2 1.0.2.0, P3 4c374d29, P4 mscorlib, P5 2.0.0.0....

I find out that these are very generic .NET errors that are occurring because I do not have an error handler at the very beginning of my application code.  After further research, I learn about "Unhandled Exceptions".  Although it is possible to handle most exceptions with a Try Catch block in the main program code, there is also a dedicated Unhandled Exception event handler. 

My current interpretation is that this is one way to handle exceptions for situations where you don't have a Try Catch block, or where an error occurs during initialization, such as referencing a DLL or external assembly.

        static void Main()
        {
            AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(CurrentDomain_UnhandledException);
           
            Application.SetCompatibleTextRenderingDefault(false);
            Application.Run(new MainForm());
        }

        static void CurrentDomain_UnhandledException(object sender, UnhandledExceptionEventArgs e)
        {
            Exception ex = (Exception)e.ExceptionObject;
            MessageBox.Show("An unexpected error (unhandled exception) occured trying to launch " + Properties.Settings.Default.appName + ": \r\n\r\n" + ex.InnerException.ToString());
        }

So in my case, the goal was to at least return some type of detail about the cause of the crash.  After installing the new version with the UnhandledException handler, we received this message.


Certainly better than the event viewer message, but still pretty cryptic.  Since I have become more familiar with the .NET exception messages, I knew to start at the top and work my way down, or just look at the InnerException.


So I focused on the System.Drawing.Icon.Initialize error.  My apps are basic business apps and aren't doing anything special with icons, so this didn't mean much to me.  After quite a few Google searches, I stumbled across an MSDN Forum post that described the same error.  The resolution in that case was to replace the icon on a window.

Seriously?  Since all three of the applications used the same icon, and since the exact same app worked on the other Citrix server, I was pretty doubtful that replacing the icon would solve the problem.  But it's a  simple fix to try.  I removed the icon from all of the forms in my application, saved and closed Visual Studio, then recreated the icon file using an icon editor, and then added the icon back to the forms.  I then recompiled the app.

Today we installed the new version of the import, and voila, it worked.

It still doesn't make any sense to me, but it is nice to be able to resolve such a random and obscure error for once.

No comments: