My good friend Sam Gentile posted two excellent entries on his (and his team's) experience with their product deployment and the pitfalls and issues they ran into. Pretty good information overall. It's also quite timely for me as I was actually dealing with some of this stuff yesterday as well for the project I'm working on.
Sam's recommendation on global exception handlers is dead on: On smart clients built using Windows Forms, always remember to hook into both the Application.ThreadException and AppDomain.UnhandledException events. The first one will trap any unhandled exception ocurring in the main UI thread, while the second will trap any exception ocurring on other background and pooled threads you're using (and you will be using them, believe me). We also have our own custom pretty dialog to display the error information to the user as well as to allow him/her to send an email-report of the error for diagnostics.
However, my issues with this turned out to be a little different: We had to get this working on a VSTO Outlook AddIn which spawned multiple AppDomains. Normally, the VSTO 2005 loader will create an AppDomain to host your AddIn (very nice, makes things much easier and safer); however, we're also using parts of the Elixir framework. Specifically, we're using the facilities it has to allow you to present Windows Forms UserControls as "Views" in Outlook Folders the AddIn creates. This is a really nice functionality if you're basically using Outlook as a shell that hosts your application or to provide custom views of the information your application handles using Outlook facilities.
However, the problem is how this functionality works: It basically generates an HTML file on disk that presents your UserControl as an ActiveX Control inside the embedded Internet Explorer window that Outlook creates. This means that now part of your AddIn code is loading into the default AppDomain! Besides the security issues this creates (it is a fairly ugly hack, after all), it now means you need ensure your global exception handler gets installed into both AppDomains.
The way we accomplished this was though a simple GlobalErrorHandler class, like this:
/// The GlobalErrorHandler class ensures
/// that we have hooked all necessary
/// events to capture unhandled exceptions in our appdomains.
static class GlobalErrorHandler
public static void Ensure()
// hook WinForms
Application.ThreadException += OnApplicationUnhandledException;
AppDomain.CurrentDomain.UnhandledException += OnDomainUnhandledException;
private static void OnApplicationUnhandledException(object sender, ThreadExceptionEventArgs e)
private static void OnDomainUnhandledException(object sender, UnhandledExceptionEventArgs e)
private static void HandleException(Exception exp)
IErrorService errorSvc = AppContext.GetService<IErrorService>();
} // class GlobalErrorHandler
Now all we had to do was ensure that GlobalErrorHandler.Ensure() was called as soon as the Outlook AddIn started and whenever one of our UserControls used as Outlook folder views got created. The global exception handler just uses our IErrorService implementation to deal with the unhandled exception (which shows the user the pretty dialog, among other things).
Sam also mentions the Exception Handling Block of the Enterprise Library. I think this one is the coolest of the application blocks in the Enterprise Library (and possibly the only one I like). What I like about it is not so much the configuration UI (though that can be nice), but the programming model it enforces throughout your application. We're currently not using it, as we feel we don't currently need that level of control, though we might consider doing so in the future. One of the reasons from shying away from getting it in right away is that I've had pretty lousy experiences when deploying solutions using the Enterprise Library, though I realize that's probably my fault :)
One of the issues that has made the deployment experience lousy for me has been the Enterprise Library Design. The EL team did a great job of instrumenting the Enterprise Library throughout all the code with performance counters, logging errors into the machine's application log and all that. Unfortunately, this causes some issues during deployment because both of these operations require that you install with (as far as I can tell) administrative privileges and that the library is installed correctly so that when the components are running under a different user account they don't fail because of access denied errors.
Unfortunately, this hasn't always worked right for me, and sometimes even though the library seems to install OK and running InstallUtil.exe and friends on them seems to work, I still get the access denied errors at runtime, which sucks, and working around these issues can be an exercise in frustration (particularly because you end up with the components failing because of stuff that is not their main purpose and for which many times I don't care/need).