VB.NET: Error handling - Part three, more on structured error handling.
A tutorial on error handling in VB.NET. This - part three - is about somewhat more advanced structured error handling.
It's kind of a boring subject, I want to code, to make fantastic programs that solve some kind of problem or do funny things. I'm not really interested in errors. But them buggers pop up every now and then and destroy the beautiful experience of using my programs.

So I want to learn how to take care of them in a good way.
This error handling tutorial keeps growing. I didn't know there was so much when I started on it. I hope someone is helped somewhat by it - at least I'm really learning.

I'm writing this as I've finished the Inner exceptions part and I'm sorry I got a little tired towards the end. The facts are there and they are explained but not in so much detail maybe. Anyway here we go...

1) Throwing errors - working at two levels.
2) Your very own exception class, in my case the JensError

3) Inner exceptions - the original error.
4) A error information routine; informing the user in a consistent way.
1) Throwing errors - working at two levels.
By now I assume you feel confident with code like this:
vb
Option Explicit On
Option Strict On
Public Class Form1
Private Sub HandledError1()
Dim test As Short
Try
test = 16000
test *= test
Catch ex1 As IndexOutOfRangeException
MsgBox("We caught an Index out of range exception in the sub: " & vbCrLf & ex1.ToString)
'Catch ex2 As Exception
' MsgBox("Caught an unspecified error in the sub: " & vbCrLf & ex2.ToString)
' Exit Sub
Finally
MsgBox("In finally.")
End Try
MsgBox("After Try - Catch")
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Try
HandledError1()
Catch ex1 As OverflowException
MsgBox("We caught an overflow exception in the calling code: " & vbCrLf & ex1.ToString)
End Try
End Sub
End Class
If not, please review
part two of the tutorial.
When you have caught an error it is "used up", that's what I wrote earlier. That statement wasn't
exactly true... Run the following code, uncomment the line
Throw ex1 and run it again, notice what happens.
vb
Option Explicit On
Option Strict On
Public Class Form1
Private Sub HandledError1()
Dim test As Short
Dim appErr As New ApplicationException
Try
test = 16000
test *= test
Catch ex1 As Exception
MsgBox("Caught an unspecified error in the sub: " & vbCrLf & ex1.ToString)
'Throw ex1
'Throw appErr 'Throwing another error - an ApplicationException error.
Finally
MsgBox("In finally.")
End Try
MsgBox("After Try - Catch")
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Try
HandledError1()
Catch ex1 As Exception
MsgBox("We caught an exception in the calling code: " & vbCrLf & ex1.ToString)
End Try
End Sub
End Class
The
Throw ex1 works like a
Exit Sub but also generates an error that can be used later. If you want to you can
Throw some other error. You decide what error you want to throw. You can re-throw the original error or throw a new error - like we do above - or even throw your own error (see below)!
2) Your very own exception class, in my case the JensError

Let's step things up at bit.
About now I think we leave the beginners error handling... Run the following code.
vb
Option Explicit On
Option Strict On
Public Class Form1
Private Sub HandledError1()
Dim test As Short
'Create a new error object and a variable to hold it.
Dim myError As New JensError(" *** A JensError from the sub HandledError. *** ")
Try
test = 16000
test *= test
Catch ex1 As Exception
'Try uncommenting either or both these MsgBox lines. It will give you
'a feel for how errors are presented to the user.
MsgBox("Caught an unspecified error in the sub: " & vbCrLf & ex1.Message)
'MsgBox("Caught an unspecified error in the sub: " & vbCrLf & ex1.ToString)
'Try uncommenting one of the below rows at a time.
'Think of the result and contemplate the code...
Throw myError
'Throw ex1
Finally
MsgBox("In finally.")
End Try
MsgBox("After Try - Catch")
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Try
HandledError1()
Catch ex1 As JensError
MsgBox("We caught a JensError in the calling code: " & vbCrLf & ex1.ToString)
ex1.Talk()
Catch ex2 As Exception
MsgBox("We caught an exception in the calling code: " & vbCrLf & ex2.ToString)
End Try
End Sub
End Class
Public Class JensError : Inherits Exception
'This is a new class. This class is a home made kind of Exception.
'Since it inherits the class Exception it does have all the methods
'and properties that class Exception has. But it also has some
'special functionality and a special name - maybe that is the most
'useful part. E.g. if you develop your own communications driver
'you can throw your own special errors from it. Then the developer
'using your driver can easily distinguish between errors from your
'driver (a time out maybe) and other errors.
'Note that this class does not handle all methods supported by the
'base class Exception.
#Region "Calls to base class constructors (The 'New' in the Exception Class)"
'This constructor is here to make sure that you can create an error
'object without any arguments. As soon as we create a constructor the
'default no-arg-constructor is inhibited so we have to make our own.
'This only calls the no-arg-constructor of the Exception Class.
Public Sub New()
MyBase.New()
End Sub
'Again, this is here just to ensure that we can handle creation of
'an error object with only a message as parameter. This exists in
'the Exception class but is inhibited as we make an Exception of
'our own.
Public Sub New(ByVal msg As String)
MyBase.New(msg)
End Sub
#End Region
'You can add your own methods to your own Exception Class.
'This function is also just an example but it shows that you
'can put your own stuff in your own exception class.
'This could be used in a lot of ways.
Public Sub Talk()
'Here you can put whatever functionality you want.
MsgBox("A JensError")
End Sub
End Class#End Region
'You can add your own methods to your own Exception Class.
'This function is also just an example but it shows that you
'can put your own stuff in your own exception class.
'This could be used in a lot of ways.
Public Sub Talk()
'Here you can put whatever functionality you want.
MsgBox("A JensError")
End Sub
End Class
Woohaaa...! No, wait! It's not that bad - really.
First of all, the latter part of the code is a new class and most of that class is just comments. The class is a kind of Exception. I've inherited the class Exception to make my own Exception class - the JensError. Read the comments to get an idea why you'd want to do such a thing.
What's new is the new class and that I make an JensError exception object of it on the row
Dim myError As New JensError(" *** A JensError from the sub HandledError. *** ") calling it myError. Then I throw this special exception when I catch another exception - just for the hell of it. No, really, it's to demonstrate that you can do stuff like that.
Then in the calling code (in Form1_Load) I check for JensError exceptions and in case I find one I use a special method that is present only in JensError exceptions. Then I'm done.

Great isn't it! You can do a lot of wonderful things with exceptions, exception handling and your own exception classes. Bet you could make a whole program just by throwing exceptions around.

But really. Try the code, comment out and uncomment parts. Pay heed to what happens and read the comments in the code. Then make yourself a nice cup of tea (or whatever), have a break, think of somthing else and be back soon.
Tea time...
3) Inner exceptions - the original error.
Ok, you saw what I did above. I caught an error and threw another error, the JensError exception. Great, fine, but what if you want to know what the original error was too? Of course there's a way...
Run the following, commenting out and uncommenting according to the comments.
vb
Option Explicit On
Option Strict On
Public Class Form1
Private Sub HandledError1()
Dim test As Short
'Create a new error object and a variable to hold it.
Dim myError As New JensError(" *** A JensError from the sub HandledError. *** ")
'This only creates a variable, no object.
'I might not want to run the constructor of the err object yet.
'(More on this below)
Dim myError2 As JensError
Try
test = 16000
test *= test
Catch ex1 As Exception
MsgBox("Caught an unspecified error in the sub: " & vbCrLf & ex1.Message)
'
'Note that when the below line of code is executed you'll get the
'message "Now we are in one of the constructors of the JensError class."
'from the constructor of the JensError class below.
myError2 = New JensError("Original error as a parameter. ", ex1)
'Try uncommenting one of the below rows at a time.
'Think of the result while contemplating the code...
Throw myError2
'Throw myError
'Throw ex1
Finally
MsgBox("In finally.")
End Try
MsgBox("After Try - Catch")
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Try
HandledError1()
Catch ex1 As JensError
MsgBox("We caught a JensError in the calling code: " & vbCrLf & ex1.ToString)
ex1.Talk()
Catch ex2 As Exception
MsgBox("We caught an exception in the calling code: " & vbCrLf & ex2.ToString)
End Try
End Sub
End Class
Public Class JensError : Inherits Exception
#Region "Calls to base class constructors (The 'New' in the Exception Class)"
Public Sub New()
MyBase.New()
End Sub
Public Sub New(ByVal msg As String)
MyBase.New(msg)
End Sub
'Here, first we use the Exception Class constructor to properly
'initialize everything that is common between the Exception Class
'and our JensError Class. Then we have some special functionality
'in our class, namely showing a message box.
'This is only an example but it shows some of what you can do with
'your own exception class and som of the things you have to do
'with your own exception class. This will show the message
'"Now we are in one of the constructors of the JensError class."
'when the JensError object is created, NOT when you use it!
Public Sub New(ByVal msg As String, ByVal err As Exception)
MyBase.New(msg, err)
MsgBox("Now we are in one of the constructors of the JensError class.")
End Sub
#End Region
Public Sub Talk()
'Here you can put whatever functionality you want.
MsgBox("A JensError")
End Sub
End Class
First, note that the appErr is initialized with a message and the original exception. This will give an error message that consists - amongst other things - of your thrown error (the ApplicationException), your message and the original error.
Then you try the different
Throw-statements and nothing new happens until you get to the
myError2 = New JensError("Original error as a parameter. ", ex1) and the
Throw myError2 which has to be uncommented at the same time.
In the JensError class I've added one more constructor that takes care of when you want to use a message and the original error. In that constructor I included a messagebox to show when the constructor is used. Try commenting out only the row
Throw myError2 and you'll still get the messagebox. Also note that it's not when you
Dim myError2 As JensError that the constructor is executed.
If you've followed me this far, experimented with the code, read it, read the comments and my text I hope you've got a good grip on
Throwing errors, making your own exception classes, handling inner exceptions and at the same time got
a glimpse on constructors - especially in exception classes. You should also have been somewhat
acquinted with inheritance, pertaining to the Exception class anyway.
If not, read again, still not? Please
comment and I'll try to update the tutorial or explain more.
4) A error information routine; informing the user in a consistent way.
All of this error handling stuff has two purposes:
To spare the user from ugly errors while trying to recover and dying gracefully if you can't recover.
One small step in the quest of keeping the user happy is to present a nicly formatted error dialog.
CODE
Option Explicit On
Option Strict On
Public Class Form1
Private Function UserInteractionOnError(ByVal codePos As String, ByVal err As Exception) As Boolean
Dim msgText As String
msgText = "An error occurred in the program at: " & vbCrLf & codePos & vbCrLf
msgText = msgText & "The error was: " & vbCrLf & err.Message & vbCrLf & vbCrLf
msgText = msgText & "Take note of the above message." & vbCrLf
msgText = msgText & "Do you want to try to save your work before restarting the program?"
Return (MsgBox(msgText, CType(20, MsgBoxStyle), "Program in trouble...") = 6)
End Function
Private Sub HandledError1()
Dim test As Short
Try
test = 16000
test *= test
Catch ex1 As Exception
If Not UserInteractionOnError("HandledError1: test *= test", ex1) Then Application.Exit()
Finally
MsgBox("Cleaning up in finally.")
End Try
MsgBox("Resuming the execution of the program")
End Sub
Private Sub Form1_Load(ByVal sender As System.Object, ByVal e As System.EventArgs) Handles MyBase.Load
Try
HandledError1()
Catch ex1 As Exception
MsgBox("We caught an exception in the calling code: " & vbCrLf & ex1.ToString)
End Try
End Sub
End Class
The idea here is that you use the
If Not UserInteractionOnError ...Then... in all your Catch blocks in order to make sure the user isn't surprised. It's just a nice touch.
This concludes my error handling tutorial.
Regards
/Jens
PS: I would very much like some comments on this tutorial. If you didn't like it I'd be happy to hear any suggestions to improve it. Thank you.
PPS: If you want to learn real hard core custom error handling
take a look at this from PsychoCoder.
This post has been edited by jens: 6 Mar, 2009 - 04:19 PM