Visual Studio
|
Designing for Debugging
Having a consistent plan for exception and error handling is critical to ensuring that you'll be able to debug problems in production environments. Additionally, many developers are
How should you implement exception handling?Even though Microsoft cleaned up the exception handling story considerably in .NET, compared to other environments, it's still the one area people have the biggest problem getting right. Two books show you the background of exception handling and the best practices that everyone needs to follow. The first is Jeffrey Richter's CLR via C# (Microsoft Press, 2006), and the second is Framework Design Guidelines: Conventions, Idioms, and Patterns for Reusable .NET Libraries, by Krzysztof Cwalina and Brad Abrams (Addison Wesley, 2006). If you're doing .NET development without having read those books' sections on exception handling, you're wasting your time. Both books mention the idea that you should let the applications crash instead of trying to recover from exceptions. Continually in my consulting work, I find that developers will go to great lengths to attempt to recover from unknown exceptions, and this is exactly the wrong approach to take. If you have an unknown exception occurring in your application, and you attempt to recover, you'll be running with unpredictable behavior and opening yourself up to security vulnerabilities.
Additionally, if you try to recover from an unknown exception, you will end up
How can I log unhandled exceptions in my applications?As I described in the last question, you want to let your application crash if there's an unhandled exception, but you will want to at least log that there's been an unhandled exception. For ASP.NET applications, you can look for unhandled exceptions in two places. If you want to get those errors on a per page basis, provide a Page_Error method for the page. For application-wide unhandled exceptions, add a global application class (in Global.asax) to the project, and put your logging in the Application_Error method.
No matter which method is logging your ASP.NET unhandled exceptions, you'll be getting the actual
Exception
-derived unhandled exception by using the
Server.GetLastError
method. After
The one limitation of the <customErrors> section is that the defaultRedirect attribute and <error> elements, where you can specify HTTP error redirects caused by your ASP.NET code, is that you can redirect only to static HTML pages. What I prefer doing in my Application_Error is to put the unhandled exception into the session state and redirect to an ASPX page so I can do better logging and display to the user. I'll still keep my <customErrors> section for the HTTP errors. The following shows an example Application_Error performing the redirect.
void Application_Error(object sender, EventArgs e)
{
// Get the exception and stuff it into a session state variable.
// If you call Server.GetLastError in the redirect page, it will
// return null.
Exception ex = Server.GetLastError ( );
Session.Add ( "UnhandledException" , ex );
// Redirect to the error page.
Response.Redirect ( "ErrorReportingPage.aspx" , false );
// You have to clear the error or it will go through the
// <customErrors> section.
Server.ClearError ( );
}
For console applications, all unhandled exceptions by any thread in your application are reported on a per-AppDomain basis through the AppDomain.UnhandledException event. A breaking change between .NET 1.1 and .NET 2.0 is that in .NET 2.0, unhandled exceptions on all threads terminate the application. In .NET 1.1, only an exception on the main thread terminates the application. Having your application continue on its merry way after a pool thread disappears was completely wrong, and the old behavior was a bug in .NET itself. Even with the improved unhandled exception behavior in console applications, there's still a small problem. If you have an unhandled exception in a pool thread or a finalizer thread, you get the standard crash message, which provides an opportunity to debug the application.
However, the main application thread is still running while the dialog is displayed, so your application can end before you get a chance to attach the debugger. I have no idea why pool threads and finalizer threads are treated this way, but I
Windows Forms applications add another twist to the unhandled exception mix. For background, pool, and finalizer threads, unhandled exceptions are reported through the AppDomain.UnhandledException event. As with the console application case, the main thread continues to run as you're handling the event. If the unhandled exception is in the main thread, you'll need to set the Application.ThreadException event in order to receive the notification. The good news is that setting the Application.ThreadException will disable the standard Windows Forms exception notification dialog box. Since I'm talking about Windows Forms applications, instead of writing your own exception-reporting user interface, you should take a look at the Microsoft Exception Message Box, which is part of Feature Pack for Microsoft SQL Server 2005 - April 2006 and can be downloaded from http://www.microsoft.com/downloads/details.aspx?FamilyID=df0ba5aa-b4bd-4705-aa0a-b477ba72a9cb&DisplayLang=en . If you've ever seen an error in any of the SQL Server 2005 graphical tools, this is the same message box but in a redistributable package. The message box supports copying all the exception data to the clipboard in addition to showing the detailed data about the assertion. You can find more information on the features and usage of the Microsoft Exception Message Box at http://msdn2.microsoft.com/en-us/library/ms166343.aspx . If you control the computers and network your application is running on, you can simply log any unhandled exception information to a file or database in a shared location. However, if you are providing software that runs outside your company, things get a little more interesting. You may want to develop with a Web service you can call with all the exception information from the user's application. If you don't have a server that can run your Web service, you may want to take a look at the Shareware Starter Kit that Microsoft put together to help shareware authors provide program registration, buy-now functionality, and, most importantly, a Web service to record your unhandled exceptions. You can find the source code and more information about the Shareware Software Starter Kit at http://sharewarestarterkit.com . The last item I want to mention about unhandled exceptions is how you can send errors your users report to Microsoft. When the crash dialog box (which is in Figure 4-2, in case you've never seen it before) appears, the user can send the error information to Microsoft. To get all your company's errors, all you have to do is register for Windows Error Reporting at http://msdn.microsoft.com/isv/resources/wer/default.aspx . The only cost to you is for the purchase of a digital certificate. Figure 4-2. Windows application error message
I'd strongly recommend that you sign up for Windows Error Reporting so you can get those crash
When do I put a finalizer on my class?
Almost never. Finalization offers a way for a class to clean up resources before the object is garbage collected in memory. The idea was that if you were holding onto a native resource, such as a native Windows file handle, you'd have a method that allowed you to get the handle closed. The problem is that with C#, a finalizer method is denoted with a "~" in front of a method that's the same
It was a mistake to use "
~
," because many developers coming from a C++ background thought that
To alleviate the problems on developers inadvertently using finalizers, .NET 2.0 introduces the
SafeHandle
class in the
System.Runtime.InteropServices
namespace. If you have a native resource, you'll wrap the native resource and only the native resource in a
SafeHandle
-derived class and override the abstract
ReleaseHandle
and
IsInvalid
get property. In those two
Having seen
|