Click here to Skip to main content
15,867,453 members
Articles / Programming Languages / XML

VB.NET Global Try Catch in the Application Framework

Rate me:
Please Sign up or sign in to vote.
4.92/5 (18 votes)
27 Jul 2018CPOL2 min read 68K   2.7K   40   18
Implement a Global Try Catch block while still using the .NET Application Framework.

Introduction

This example shows how to setup a global try-catch block using the .NET Application Framework. The example code is in VB.NET but can easily be converted to C# as well.

Background  

As I am not a perfect programmer, and there are times when an exception slips through my fingers, and I get an unhandled exception which usually results in the application crashing very ungracefully. This example shows how to not only setup a global try-catch block so that you can handle the crash more gracefully, but also how to style a form that matches your main application that provides additional information, and an easy 'Copy Text' button that places the text in the clipboard so that the user can e-mail it to tech support. You could take this a step further, and change the button to something 'Send Report' where it e-mails the text automatically or something similar.

Often times, when an unhandled exception occurs, the user takes a screenshot and forwards it on to tech support who then forward it to me such as the one shown below. While this information could be invaluable to track down where the exception is occurring, since it is only a screenshot, I cannot read all the text. So solve this, I have created a simple form which I can style to match my main application and display not only the information from .Net unhandled exception but some additional information as well:

.Net Unhandled Exception Screenshot

There are similar ways of doing a global try-catch without using the .NET Application Framework, but then you would not be able to use the extra features of the .NET Application Framework such as 'Make single instance application'.

Using the code    

Make sure to check the 'Enable application framework' in the Project Properties -> Application Settings.

On that same page, click the 'View Application Events' button:

View Application Events

 

This will open / generate the ApplicationEvents.vb file.

In the ApplicationEvents.vb code, add the following:

 

 

VB.NET
Partial Friend Class MyApplication        
    'One of the global exceptions we are catching is not thread safe, 
    'so we need to make it thread safe first.
    Private Delegate Sub SafeApplicationThreadException(ByVal sender As Object, _
        ByVal e As Threading.ThreadExceptionEventArgs) 

 

VB.NET
Private Sub ShowDebugOutput(ByVal ex As Exception)

    'Display the output form
    Dim frmD As New frmDebug()
    frmD.rtfError.AppendText(ex.ToString())
    frmD.ShowDialog()

    'Perform application cleanup
    'TODO: Add your application cleanup code here.

    'Exit the application - Or try to recover from the exception:
    Environment.Exit(0)

End Sub
VB.NET
Private Sub MyApplication_Startup(ByVal sender As Object, _
    ByVal e As Microsoft.VisualBasic.ApplicationServices.StartupEventArgs) Handles Me.Startup

    'There are three places to catch all global unhandled exceptions:
    'AppDomain.CurrentDomain.UnhandledException event.
    'System.Windows.Forms.Application.ThreadException event.
    'MyApplication.UnhandledException event.
    AddHandler AppDomain.CurrentDomain.UnhandledException, AddressOf AppDomain_UnhandledException
    AddHandler System.Windows.Forms.Application.ThreadException, AddressOf app_ThreadException

End Sub
VB.NET
Private Sub app_ThreadException(ByVal sender As Object, _
    ByVal e As Threading.ThreadExceptionEventArgs)

    'This is not thread safe, so make it thread safe.
    If MainForm.InvokeRequired Then
        ' Invoke back to the main thread
        MainForm.Invoke(New SafeApplicationThreadException(AddressOf app_ThreadException), _
            New Object() {sender, e})
    Else
        ShowDebugOutput(e.Exception)
    End If

End Sub
VB.NET
Private Sub AppDomain_UnhandledException(ByVal sender As Object, _
    ByVal e As UnhandledExceptionEventArgs)

    ShowDebugOutput(DirectCast(e.ExceptionObject, Exception))

End Sub
VB.NET
    Private Sub MyApplication_UnhandledException(sender As Object, _
        e As Microsoft.VisualBasic.ApplicationServices.UnhandledExceptionEventArgs) _
        Handles Me.UnhandledException

        ShowDebugOutput(e.Exception)

    End Sub 
End Class

Optional 

If you want output similar to the screenshot above, the code for it:

VB.NET
Public Sub New()

    On Error Resume Next

    ' This call is required by the designer.
    InitializeComponent()

    ' Add any initialization after the InitializeComponent() call.
    rtfError.AppendText("Product Name:      " & My.Application.Info.ProductName & vbNewLine)
    rtfError.AppendText("Product Version:   " & My.Application.Info.Version.ToString() & vbNewLine)

    Dim asms As New List(Of Assembly)

    For Each asm As Assembly In My.Application.Info.LoadedAssemblies
        asms.Add(asm)
    Next asm

    'Assemblies are listed in the order they are loaded - I prefer them alphabetical.
    'But if the order in which assemblies are being loaded is important, then don't do the sort.
    Dim asmc As New AsmComparer()
    asms.Sort(asmc)

    rtfError.AppendText(vbNewLine)
    For Each asm As Assembly In asms
        'Many of the assemblies are core .Net assemblies. I do not care about them.
        'If you do, comemnt out this next line:
        If IO.Path.GetDirectoryName(asm.Location).ToUpper() <> _
            My.Application.Info.DirectoryPath.ToUpper() Then Continue For

        'Included in this list is the executable path - which is meaningless.
        'Have to cast to Upper (or lower), because one of the paths returns as .EXE, 
        'and the other .exe
        If asm.Location.ToUpper() = Application.ExecutablePath.ToUpper() Then Continue For

        rtfError.AppendText("Loaded Assembly:   " & asm.ToString() & vbNewLine)
    Next asm

    rtfError.AppendText(vbNewLine)
    rtfError.AppendText("OS Name:       " & My.Computer.Info.OSFullName & vbNewLine)
    rtfError.AppendText("OS Version:    " & My.Computer.Info.OSVersion & vbNewLine)

    ''IMPORTANT: This next line is .Net 4.0 only.
    ''       If you need to know if it is a 64 bit OS or not, you will need to use
    ''       a different method for any .Net older than 4.0
    rtfError.AppendText("OS Platform:       " & IIf(Environment.Is64BitOperatingSystem, _
                                                       "x64", "x86") & vbNewLine)

    rtfError.AppendText("Physical Memory:   " & _
                         FormatBytes(My.Computer.Info.AvailablePhysicalMemory) & " / " & _
                         FormatBytes(My.Computer.Info.TotalPhysicalMemory) & _
                         " (Free / Total)" & vbNewLine)
    rtfError.AppendText("Virtual Memory:    " & _
                         FormatBytes(My.Computer.Info.AvailableVirtualMemory) & " / " & _
                         FormatBytes(My.Computer.Info.TotalVirtualMemory) & _
                         " (Free / Total)" & vbNewLine)

    rtfError.AppendText(vbNewLine)
    rtfError.AppendText("Error Output:" & vbNewLine)

End Sub

Private Function FormatBytes(ByVal bytes As Long) As String

    If bytes < 1000 Then
        Return CStr(bytes) & "B"
    ElseIf bytes < 1000000 Then
        Return FormatNumber(bytes / 1024, 2) & "KB"
    ElseIf bytes < 1000000000 Then
        Return FormatNumber(bytes / 1048576, 2) & "MB"
    Else
        Return FormatNumber(bytes / 1073741824, 2) & "GB"
    End If

End FunctionPrivate Sub btnCopy_Click(sender As System.Object, e As System.EventArgs) Handles btnCopy.Click

    My.Computer.Clipboard.Clear()
    My.Computer.Clipboard.SetText(rtfError.Text, TextDataFormat.Text)
    My.Computer.Clipboard.SetText(rtfError.Rtf, TextDataFormat.Rtf)

End Sub

Public Class AsmComparer
    Implements IComparer(Of Assembly)

    Public Function Compare(x As System.Reflection.Assembly, y As System.Reflection.Assembly) _
        As Integer _
        Implements System.Collections.Generic.IComparer(Of System.Reflection.Assembly).Compare

        Return String.Compare(x.ToString(), y.ToString())

    End Function
End Class 

History

11/19/2012 - Updated to use the proper ApplicationEvents.vb file instead of the Application.Designer.vb file. (Thanks to Zac Greve to pointing this out!)

7/27/2018 - Update source download to include a C# project that has similar code.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)


Written By
Software Developer (Senior)
United States United States
This member has not yet provided a Biography. Assume it's interesting and varied, and probably something to do with programming.

Comments and Discussions

 
QuestionI have error on first "line" in the codes, how do I fix that ? Pin
Member 1341046022-Jan-21 1:23
Member 1341046022-Jan-21 1:23 
QuestionCan you do a F# or J# version of this if possible Pin
Daszbin1-Aug-18 6:45
professionalDaszbin1-Aug-18 6:45 
QuestionCan you do a C# version of this Pin
Daszbin11-Apr-18 9:35
professionalDaszbin11-Apr-18 9:35 
AnswerRe: Can you do a C# version of this Pin
B.O.B.27-Jul-18 15:53
B.O.B.27-Jul-18 15:53 
AnswerRe: Can you do a C# version of this Pin
B.O.B.27-Jul-18 17:36
B.O.B.27-Jul-18 17:36 
GeneralB.O.B - You are a real programmer ! Pin
Renganathan Iyengar27-Nov-14 17:35
professionalRenganathan Iyengar27-Nov-14 17:35 
GeneralMy vote of 5 Pin
Alhoot20043-Feb-13 13:29
professionalAlhoot20043-Feb-13 13:29 
GeneralMy vote of 5 Pin
Ștefan-Mihai MOGA14-Dec-12 5:15
professionalȘtefan-Mihai MOGA14-Dec-12 5:15 
GeneralMy vote of 5 Pin
Savalia Manoj M10-Dec-12 20:54
Savalia Manoj M10-Dec-12 20:54 
GeneralMy vote of 3 Pin
Klaus Luedenscheidt18-Nov-12 19:10
Klaus Luedenscheidt18-Nov-12 19:10 
AnswerRe: My vote of 3 Pin
B.O.B.18-Nov-12 20:29
B.O.B.18-Nov-12 20:29 
GeneralRe: My vote of 3 Pin
Klaus Luedenscheidt18-Nov-12 21:24
Klaus Luedenscheidt18-Nov-12 21:24 
SuggestionRe: My vote of 3 Pin
Brisingr Aerowing18-Nov-12 22:52
professionalBrisingr Aerowing18-Nov-12 22:52 
GeneralRe: My vote of 3 Pin
B.O.B.18-Nov-12 23:03
B.O.B.18-Nov-12 23:03 
GeneralRe: My vote of 3 Pin
Brisingr Aerowing18-Nov-12 23:34
professionalBrisingr Aerowing18-Nov-12 23:34 
AnswerRe: My vote of 3 Pin
B.O.B.19-Nov-12 5:52
B.O.B.19-Nov-12 5:52 
GeneralRe: My vote of 3 Pin
karenpayne21-Mar-13 6:27
karenpayne21-Mar-13 6:27 
Hello,

You mention UnhandledException does not trap all unhandled exceptions, which exceptions are you speaking of?
Kevin S. Gallagher
Programming is an art form that fights back

AnswerRe: My vote of 3 Pin
B.O.B.22-Mar-13 21:15
B.O.B.22-Mar-13 21:15 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.