Customize Automated Error Reporting In SmartAssembly

Despite being, for some time, a keen user of SmartAssembly for the .NET components that he produces, Matteo hadn't discovered the Automated Error Reporting feature until recently. As he reports, it has enabled him to be much quicker and more effective in dealing with any problems that one of his users might experience, and it keeps his customers happy too

Contents

Introduction

Like most other .NET-components developers, there are just a few things that I need: the latest version of Visual Studio, a good installer, occasionally a license manager, and Red Gate’s SmartAssembly.

Why SmartAssembly? I discovered SmartAssembly some years ago, when I was searching for software that was able to obfuscate the code that I write. Today I reckon that SmartAssembly is the perfect tool to use for my code obfuscation, and it’s also a very useful instrument for improving, through CIL optimization, the overall quality of my assemblies after they are built; I use obfuscation, pruning and merging above all. I usually write code inside Visual Studio, I compile it (hoping no bugs are present!) and then I use SmartAssembly to do the rest.

During the last few months I finally found the time to evaluate, from the ground up, an interesting feature that was entirely new to me. It is called Automated Error Reporting, and it allows us to catch and automatically manage all the unhandled exceptions that could occur while an assembly is running.

This can be very handy, both for commercial software and for a company’s internal applications. While we are developing commercial software, we have the means to monitor the way that our assemblies behave in actual use. Usually, when we publish software, we lose all further knowledge about its behavior on the clients’ machines unless they tell us. Users download our software, they start using it, and the chances are that they’ll find some bugs that we weren’t able to detect during our testing, possibly because they are using machine configurations that are different from the configurations we used in our development and test processes. In the time that it takes us to detect and fix bugs, there is a risk that users might abandon our products in favor of those of our competitors. With Automated Error Reporting we are able to reduce the time that is required to track down bugs and consequently to release patches. This can prevent any potential problems with users who are evaluating our products.

On the other hand, for a business application that is used within a corporation, Automated Error Reporting allows us to avoid the most tedious tasks that we have to perform when users discover bugs. Usually, when users discover a new bug, they paste the screen image into an email that they deliver to the development team. When an emailed bug-report arrives, it has to be forwarded to the developer responsible for the bug. Maybe he reads it, but, having a lot of others thing to do, perhaps he plans to check it at a later moment. It is quite possible that days then pass and he forgets it entirely. If we are using a bug tracking system, we can make the bug reporting process reliable and relatively painless by automating the submission of the new issue to it. In this way things become easier and the time needed to solve bugs decreases.

SmartAssembly’s Automated Error Reporting feature permits us to easily implement this type of technique. SmartAssembly is in fact able to integrate itself into JIRA Bug Tracker (http://www.atlassian.com/software/jira/) through a plug-in that you can download at http://sasync.codeplex.com/.

In this article I’ll explain how to use the SmartAssembly SDK to develop Automated Error Reporting services that are customized to meet our specific requirements.

During the article I will not provide examples for you as I usually do. This is because the SmartAssembly SDK already contains a lot of useful examples that you can study, or even modify for your own needs.

Automated Error Reporting With SmartAssembly

In SmartAssembly, Automated Error Reporting can be enabled by configuring it within the ‘Automated Error Reporting’ section contained in the Project Settings window.

1311-image001small.png

Figure1: Automated Error Reporting feature in SmartAssembly.

When an unhandled exception occurs, it is caught by SmartAssembly and a report of all the information available about the exception is created and then sent to the Red Gate’s reporting web service. You can, of course, use your own report server instead. Using the SmartAssembly UI, we can browse all the collected exception reports and analyze them.

SmartAssembly allow us to display a notification window to the end user, alerting him about the exception, or, alternatively, send in a report automatically without bothering the user.

In its simplest form, the notification window looks like Figure 2:

1311-image003.png

Figure 2: Automated Error Reporting’s default notification window.

Figure 2 is related to a demo application called SmartAssemblyDemo, which has thrown a DivideByZeroException exception. This exception has not been handled by the developers, using the try … catch statement, so SmartAssembly catches it for them. The user can send the exception’s details back to the development team by pressing the “Send Error Report” button. Figure 3 shows how the reported exception will be notified:

1311-image004small.png

Each report can be browsed by double-clicking on it. Figure 4 shows how a report appears:

1311-image006small.png

Figure 4: Exception report in SmartAssembly.

Each report can be saved to the file system by pressing the “Save As…” button. Once saved, it is represented as an XML document. Figure 5 shows its contents:

1311-image008small.png

Figure 5: Content of an Exception Report in SmartAssembly.

The most important node is the StackTrace node. It gives all the information related to the exception thrown by the assembly, such as the type of exception, the method that threw it, and the line at which it occurs.

Why should SmartAssembly contain an Automated Error Reporting feature when it is primarily concerned with code obfuscation and optimization? There is an important advantage to integrating the error reporting with the obfuscation functionality. If you obfuscate your code, and an exception is thrown, its stack trace will contain obfuscated code too. Your DoSomething() method, responsible for the exception, will probably have been renamed during obfuscation to, for example, wjf() so the stack trace will no longer be of any use to you. The uniqueness of the Automated Error Reporting feature of SmartAssembly is its ability to ‘de-obfuscate’ the stack trace in the report. So, if you need to obfuscate your code and to implement Automated Error Reporting too, you’ll need to use Automated Error Reporting software that is able to de-obfuscate your code too.

Customize the Exception Handler

With SmartAssembly, we can fully customize our Automated Error Reporting service by using the SmartAssembly SDK that comes with the Professional Edition. We can write our custom exception handler, in the form of a DLL library, and link it to our SmartAssembly project as show in the Figure 6.

1311-image010small.png

Figure 6: Custom exception handler in a SmartAssembly project.

Figure 6 shows a SmartAssembly project that uses the SmartAssemblyHandler.dll as a custom template for Automated Error Reporting. When we build the project, the custom template will be merged into the original assembly.

To create a custom handler we only need to open Visual Studio, create a new project of type Class Library and add a reference to the SmartExceptionCore.dll assembly to it. It is available under the SDK\Bin subfolder of the main SmartAssembly program folder.

Figure 7 shows the main objects contained in the SmartExceptionsCore.dll assembly.

1311-image012small.png

Figure 7: Main objects inside the SmartExceptionsCore.dll assembly.

The UnhandledExceptionHandler Object

To code our custom handler, we have to create a class that derives from the UnhandledExceptionHandler class. Figure 7 reports all its properties and methods in the right-hand box. We need to override the following abstract methods:

The first method, OnReportException(…), is the one that is invoked when an unhandled exception is detected. Its method body is the right place in which to develop all the logic related to the exception report generation and delivery.

SmartAssembly performs a lot of tasks during its execution. It will, for example, display the UI, send the report, and execute the custom handler. During these stages, exceptions can also happen. If they do, they are managed as FatalExceptions, and the OnFatalException(…) method will be fired. This method expects a FatalExceptionEventsArg object as an argument. It contains nothing more than the exception that caused the issue. No report can be sent due to the fact that the report’s generation and delivery engine is not working.

The last method, OnSecurityException(…), is invoked when some SecurityExceptions occur during the execution of the custom handler. The UnhandledExceptionHandler base class, and in general all the classes contained inside the SmartExceptionCore.dll assembly, makes use of unmanaged code and reflection. If our assembly is running in a partial trust environment, the Automated Error Reporting would be prevented from taking place due to security restrictions. In this case, we can perform some custom action by overriding the OnSecurityException(…) abstract method. The argument provided to this method is a SecurityExceptionEventsArg object. It implements a ReportException property, of type System.Boolean, that permits us to set whether the OnReportException() method will be called or not.

After we have developed the three previous abstract methods we need to perform a final step. We need to implement a static AttachApp() method. Think of it as the Main() method in .NET desktop applications. SmartAssembly uses it to initialize the assembly with the appropriate exception handler. Its body must contain a call to the AttachExceptionHandler(…) static method defined inside the UnhandledExceptionHandler class. It allows us to set the exception handler that we want to use.

Figure 8 shows the “skeleton” of a typical UnhandledExceptionHandler derived classes.

Figure 8: Skeleton of an UnhandledExceptionHandler derived class.

In addition to the main methods that we have seen so far, the UnhandledExceptionHandler base class implements other useful properties and methods that can be used to customize notifications to users. Figure 9 list the most important ones.

 

Name

Description

ApplicationName

A constant that contains the application name as inserted during the SmartAssembly project settings compilation. For details see Figure 6.

CompanyName

Same as the previous, but related to the name of the company that developed the product.

GetUserID()

Returns a GUID associated with the end user using the assembly. It must be overridden in order to give to it a value. Otherwise an empty value is returned.

SetProxy(…)

This allows us to set the proxy to use, if any, to reach the reporting web service.

DebuggerLauncher

An event that is fired when the debugger is launched. The debugger allows us to browse the exception report locally, without having to send it to the report server. SmartAssembly must be installed on the user’s machine.

SendingReportFeedback

An event that is fired after the report has been sent to the reporting web service.

Figure 9: Additional properties and methods of the UnhandledExceptionHandler class.

Create and Send the Report

As we have seen, the OnReportException(…) method allow us to interact with the report generation and delivery. It expects a ReportExceptionEventArgs object as an argument. This object gives us all the information and methods that we need in order to write our custom notification solution. Figures 10 and 11 list them.

 

Properties

 

Name

Description

Exception

This represents the unhandled exception thrown.

CanDebug

If true, the user can choose to open the exception report locally using the debugger.

ShowContinueCheckBox

This states whether the application can continue after the exception is thrown. Its value is set by SmartAssembly’s code in relation to the type of exception detected.

TryToContinue

If true, the user can select whether he want to continue with the application or to close it after the OnReportException(…) method ends.

Figure 10: Properties of ReportExceptionEventArgs.

 

methods

 

Name

Description

SendReport()

Sends the report to the report server.

AddCustomProperty(…)

This adds a custom property (as a name/value pair) to the exception report. Properties added will appear under the <CustomProperties> node of the report (Figure 5).

AttachFile(…)

This attaches a file to the exception report, such as an image of the user’s monitor that shows the exception thrown. Capturing Screenshots for Automated Error-Reporting,by Jonathan Evans, contains more information on how to generate this.

GetReportRawData()

This gets the report’s data (Figure 5) as a byte array. You can convert it into the XML document by using the Encoding.UTF8.GetString(….)  method.

LaunchDebugger()

Launches the debugger.

SaveEncryptedReport(…)

Saves the compressed and encrypted exception report to a file.

Figure 11. Methods of ReportExceptionEventArgs.

Report Handled Exception

Why can’t we use SmartAssembly to collect information about exceptions that can occur in an assembly if they have already been handled by the use of a try … catch statement?

There are several reasons for this. Sometimes our catch statement deals only with generic exceptions and we might want to be informed as to which kind of exception is thrown (FileLoadException, SqlException and so on…). Sometimes a particular exception condition could occur many times, and this could suggest to us that a specific method should be re-evaluated in order to fix something that is not working as we expected.

SmartAssembly allows us to send exception reports manually to the web reporting service. We simply need to add to our solution a reference to the SmartAssembly.ReportException.dll assembly contained in the SmartAssembly’s SDK, and use the ExceptionReporting class defined inside it. It implements a single static method:

We invoke this, passing as its argument the exception that we want to report, and the job is done.

Conclusion

In this article I’ve tried to emphasize how important it is to take control of all the exception conditions that can occur during the lifetime of our software, and how SmartAssembly can help us to automate the process of collecting and recording all the exception events, as well as helping us to organize all the information that we can consistently abstract from them.

I believe that it is likely that we can improve the overall quality and reputation of our software by appropriate exception management. This is because:

  • We can keep in touch with the software we have developed even after we have released it.
  • Bug fixing and the release of patches can take less time. Every extra day in which our buggy software is running on the users’, or even customers’, machines increases the possibility that they become aware of the bug and the chance that the reputation of the software will suffer.
  • Software that displays an informative window rather than the .NET runtime’s developer-oriented unhandled exception windows can leave a much better impression on the user.