One of the nice features hidden within My namespace under MyApplication is an event called UnhandledException which allows a developer to handle unhandled exceptions by writing code to display a user friendly message, write to a log file and perhaps send an email to the developer informing them of the problem.
Rather dig straight into a full blown replacement for the default unhandled exception handler I will present them incrementally beginning with showing the exception in a simple web browser embedded in a Windows Form with the stack trace displayed. The final installment will have a highly customized user friendly message box, write each unhandled exception to an XML file and provided email capabilities where logging and email features will be controlled via an unhandled exception handler configuration file which is read from a DLL containing the unhandled exception handler.
To implement a replacement unhandled exception handler go to My Project windows of your project and select the Application tab, select the button “View Application Events” which creates a module with an empty partial class for MyApplication. Select the upper left hand corner ComboBox for Classes and select (MyApplicationEvents) followed by selecting the Method ComboBox in the upper right hand corner of the code window and select unhandledexceptions which creates an event procedure for overriding how unhandled exceptions are handled.
IMPORTANT The following does not work if you are trying this code out from the IDE but instead must run without the IDE debugger. In the attached demonstration project there is code to prevent you from attempting testing from the IDE.
Code to prevent running in the IDE.
Code:
If My.Application.RunningUnderDebugger Then
My.Dialogs.InformationDialog("Can not demo within the IDE")
Else
EndupHere()
End If
Namespace My
Partial Friend Class MyApplication
''' <summary>
''' Indicates if we are running under the IDE or not
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks></remarks>
Public ReadOnly Property RunningUnderDebugger() As Boolean
Get
Return System.Diagnostics.Debugger.IsAttached
End Get
End Property
. . .
Using the following code will pretty much do nothing other than demonstrate we have control over the exception.
Code:
Private Sub MyApplication_UnhandledException( _
ByVal sender As Object, _
ByVal e As UnhandledExceptionEventArgs) Handles Me.UnhandledException
MsgBox(e.Exception.ToString)
End Sub
From here we could write the exception to a log file as shown below which is not bad but we can improve on the formatting and make the file easier to read along with setting it up to be viewable in a exception log file viewer using a Data bound DataGridView where the stack trace is displayed in a control beneath the DataGridView. For now we will use a simple format.
Code:
Private Sub MyApplication_UnhandledException( _
ByVal sender As Object, _
ByVal e As UnhandledExceptionEventArgs) Handles Me.UnhandledException
Dim Doc As New XDocument
Dim FileName As String = "unhandledExceptions.xml"
If IO.File.Exists(FileName) Then
Doc = XDocument.Load(FileName)
Else
Doc = XDocument.Parse( _
"<?xml version=""1.0"" encoding=""utf-8""?><Exceptions></Exceptions>")
End If
Dim config = Doc.<Exceptions>(0)
config.Add(<Exception>
<Date_Time><%= Now %></Date_Time>
<Message><%= e.Exception.Message %></Message>
<StackTrace><%= Environment.NewLine %>
<%= e.Exception.StackTrace.ToString %>
</StackTrace>
</Exception>)
config.Save(FileName)
MsgBox( _
String.Format( _
"An exception happened which will close this program{0}{1}", _
Environment.NewLine, e.Exception.ToString))
End Sub
Last edited by kareninstructor; Jan 12th, 2011 at 07:23 PM.
Reason: Added project file
<?xml version="1.0" encoding="utf-8"?>
<Exceptions>
<Exception>
<Date_Time>2011-01-12T15:02:06.5278012-08:00</Date_Time>
<Message>Year, Month, and Day parameters describe an un-representable DateTime.</Message>
<StackTrace>
at System.DateTime.DateToTicks(Int32 year, Int32 month, Int32 day)
at System.DateTime..ctor(Int32 year, Int32 month, Int32 day)
at WebMessageBox.MainForm.EndupHere() in MainForm.vb:line 16
at WebMessageBox.MainForm.StartHere() in MainForm.vb:line 11
at WebMessageBox.MainForm.cmdUnhandledExceptionDemo_Click(Object sender, EventArgs e) in MainForm.vb:line 5
at System.Windows.Forms.Control.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnClick(EventArgs e)
at System.Windows.Forms.Button.OnMouseUp(MouseEventArgs mevent)
at System.Windows.Forms.Control.WmMouseUp(Message; m, MouseButtons button, Int32 clicks)
at System.Windows.Forms.Control.WndProc(Message; m)
at System.Windows.Forms.ButtonBase.WndProc(Message; m)
at System.Windows.Forms.Button.WndProc(Message; m)
at System.Windows.Forms.Control.ControlNativeWindow.OnMessage(Message; m)
at System.Windows.Forms.Control.ControlNativeWindow.WndProc(Message; m)
at System.Windows.Forms.NativeWindow.Callback(IntPtr hWnd, Int32 msg, IntPtr wparam, IntPtr lparam)</StackTrace>
</Exception>
</Exceptions>
Since each entry is added to the end of the log file for now you can simply move to the end of the file in notepad or your favorite XML editor to view the information. In the final version a viewer will bring you to the last entry automatically.
The next thing we might customize is how to display the stack trace and present a message to the user as shown below.
Code:
Private Sub MyApplication_UnhandledException( _
ByVal sender As Object, _
ByVal e As UnhandledExceptionEventArgs) Handles Me.UnhandledException
Dim Doc As New XDocument
Dim FileName As String = "unhandledExceptions.xml"
If IO.File.Exists(FileName) Then
Doc = XDocument.Load(FileName)
Else
Doc = XDocument.Parse( _
"<?xml version=""1.0"" encoding=""utf-8""?><Exceptions></Exceptions>")
End If
Dim config = Doc.<Exceptions>(0)
config.Add(<Exception>
<Date_Time><%= Now %></Date_Time>
<Message><%= e.Exception.Message %></Message>
<StackTrace><%= Environment.NewLine %>
<%= e.Exception.StackTrace.ToString %>
</StackTrace>
</Exception>)
config.Save(FileName)
Dim f As New frmAlert( _
e.Exception, _
"Unhandled exception", _
String.Format("Exception has been recorded to [{0}]", FileName))
Try
f.ShowDialog()
Finally
f.Dispose()
End Try
End Sub
No matter which method used the application will terminate as it should since the application may very well be in a state that is volatile. If somehow you know (which is difficult to determine) that if an exception is throw that it is okay to continue we can add the following property to MyApplication class.
Code:
Private mContinueAfterException As Boolean
''' <summary>
''' Determine if this app can stay open after an unhandled
''' exception has been thrown.
''' </summary>
''' <value></value>
''' <returns></returns>
''' <remarks>
''' Not practical in most circumstances as we usually
''' do not know how to hangle unhandled exceptions and
''' leaving an app open will not be wise.
''' </remarks>
Public Property ContinueAfterException() As Boolean
Get
Return Not mContinueAfterException
End Get
Set(ByVal value As Boolean)
mContinueAfterException = value
End Set
End Property
Then prior to code you want to allow the program to continue even with an unhandled exception we would set ContinueAfterException to True.
Code:
My.Application.ContinueAfterException = True
Then add this line beneath the message box or form dialog in the unhandled exception event
Code:
e.ExitApplication = Me.ContinueAfterException
Try the attached project out to get a feel for how this can assist you in recording a simple log file and display a user friendly message to your user.
I have been doing something very similar to this for several years now, but instead of writing the information to a log file, I create a mail message object, add all the info such as date, time, user id, stacktrace, etc. to the body, take a screenshot & attach it to the message & send it to myself. That way I know instantly when a user encounters an error & I have all the pertinent info & a screenshot to help me debug it. It has been a tremendous help since I've been doing this.
I have been doing something very similar to this for several years now, but instead of writing the information to a log file, I create a mail message object, add all the info such as date, time, user id, stacktrace, etc. to the body, take a screenshot & attach it to the message & send it to myself. That way I know instantly when a user encounters an error & I have all the pertinent info & a screenshot to help me debug it. It has been a tremendous help since I've been doing this.
In regards to creating an email and image, that is a great idea. We do that also except when designing our overall design decided to forego a screen shot since we were not concerned with seeing the user screen and after implementing realized it was the right decision not to include an image as all the information we decided to send was sufficient to diagnosing the exception. This is not to say a screenshot would be beneficial to others, just not for us. The most common exception we see is during the testing phase of a project, which is where exceptions should be found and taken care of. Any ways my next part of this article will begin showing how to implement logging to XML, sending a message and showing a custom dialog which has two faces, one for testers which shows more detail then the user friendly interface and lastly will provide options to have the unhandled exception handler coded in each project or within a DLL. Also a custom exception log viewer will be demonstrated.
One last thing in regards to email in that in our agency an email may be sent to one or more addresses. For instance during testing the email is sent to the lead developer while in production an email is sent to a special address where a rotating person watches a special MS-Outlook folder for exceptions. An application may send the email to this special folder or to the special folder and a developer. In some cases where we have 24-7 applications text messages is also an option.
As time permits I will show other options in part 3.
I have been doing something very similar to this for several years now, but instead of writing the information to a log file, I create a mail message object, add all the info such as date, time, user id, stacktrace, etc. to the body, take a screenshot & attach it to the message & send it to myself. That way I know instantly when a user encounters an error & I have all the pertinent info & a screenshot to help me debug it. It has been a tremendous help since I've been doing this.
If you are taking screenshots of your user's desktops then I hope at least they know about it, right? I wouldn't be happy to find out an application was taking screenshots of my desktop without me knowing. I could have all sorts of personal information on the desktop (pictures, passwords, who knows).
If I would implement something like this I would at least make sure that the screenshot only shows my application and that it would show this screenshot to the user before sending, asking him to confirm whether or not it is ok to send that screenshot.
The only thing visible in the screenshot is the application that threw the error, showing the state of all controls, contents of textboxes, etc., helping me to reproduce & debug the error...
Last edited by nbrege; Jan 19th, 2011 at 07:58 AM.
As Nick pointed out you really need to use caution not just for personal information but also classified business information. For instance my agency internal applications display extremely sensitive information of taxpayers. If an application were to take a snapshot of a screen from the unhandled exception handler and output it any place (meaning email, file, printer) that there is even the slightest chance of someone seeing it who does not have authorization than we have a serious problem. Our security is even goes to the point of logging what someone views within an application. With that said a thought comes to mind about the user taking screen shots, printing etc which for us is handled in policy.
Another consideration is saving an image to an unavailable drive or a drive that has no remaining disk space etc. If sent via email the email is down. If working remotely there are issues there too.
Recording the proper information should provide a developer with what constituted the exception. If a user entered incorrect data then business rules implemented into an application should gracefully handle this without entering the unhandled exception handler. If the application crashes because of the operating system and enters the unhandled exception handler it would be wise not to take a screenshot either as the application more likely than not is in an unstable condition and with that said we want to do our stuff with kid gloves.
Another thought which I hit on in an article here on customizing My Namespace is having code to insure proper assemblies are installed (in this case IBM-DB2 provider) are installed so that assertion handles a serious issue before it happens.
The attached image views a unhandled exception log file. Upon opening the viewer the user is taken directly to the last exception. If I were to redo the entire schema of writing a log file I would have put in less information rather than more. The basics are exception, inner exception and stack trace for 99% of exceptions I have worked with over the past 5 to 7 years.
Okay with all that said this is not to bash your screen shot but to make people think before they simply do this and that screen shots are rarely needed. We have lots of applications where in the past some took screen shots but stopped because they really did not assist in tracking down the issue causing the exception.
I would agree that taking a screenshot may not be appropriate in all situations, but the applications where I am doing it do not display any sensitive information, just internal operations stuff. And true, a screenshot is not always helpful, but there have been times where it HAS helped me in tracking down bugs. At some point I may take out the screenshots as I get all/most of the bugs squashed. It is up to the programmer to decide if taking a screenshot is appropriate in their environment...
I would agree that taking a screenshot may not be appropriate in all situations, but the applications where I am doing it do not display any sensitive information, just internal operations stuff. And true, a screenshot is not always helpful, but there have been times where it HAS helped me in tracking down bugs. At some point I may take out the screenshots as I get all/most of the bugs squashed. It is up to the programmer to decide if taking a screenshot is appropriate in their environment...