What error handling should be used in VB.NET - vb.net

What error handling should be used in VB.NET?
Should one use the "On Error Goto ErrorHandler ... Exit Sub ErrHandler ... End Sub" pattern or should you use the "try { ... } catch { ... } finally { ... }" pattern?

"try { ... } catch { ... } finally { ...}" pattern by a long shot.
C#
try
{
// Do Something that may go wrong
}
catch (Exception ex)
{
//Do something with the error
}
finally
{
//Cleanup
}
or
VB
Try
// Do Something that may go wrong
Catch ex as Exception
//Do something with the error
Finally
//Cleanup
End Try

The most obvious reasons I can think of off the top of my head to steer clear of On Error GoTo... would have to be:
On Error GoTo does not discriminate between types of exceptions.
On Error GoTo does not provide as much structure as Try/Catch/Finally (e.g., nesting one Try/Catch block within another).
On Error GoTo has no counterpart to Finally (that I know of).
I'm sure in many cases, clever use of On Error GoTo could mimic the behavior that is built in to VB.NET's Try/Catch/Finally feature. But what would be the point?

On Error Goto ErrorHandler ... Exit Sub ErrHandler ... End Sub is from the VB6 days. Definitely go with Try... Catch... Finally...

A little background
'On Error Goto' is the way things were done in VB 6 before the .Net days. The VB compiler still allows this so you can easily port old VB code to VB.Net. VB.Net is probably the only .Net language that supports this.
'Try Catch Finally' is the .Net way to do things and a lot more flexible allowing you to catch, wrap and rethrow exceptions. It allows for easier interoperation between components written in different languages and is a lot more readable when you do more complex error handling because you don't have goto's.

Old post, but here's my two penneth. Debugging with On Error Goto is a lot easier than Try Catch:
On Error Goto ErrHand
do something that goes wrong
do something else
ErrHand:
Resume Next
Place a breakpoint on Resume Next and if it hits, step through your code once and you jump to the statement directly after the one that caused the exception, making it a lot easier to track down the problem.
On Error Goto is also good if you expect errors, like resources that are created by other processes that you are not in control of and aren't yet available, or locked resources when you know other processes will lock a resource and you must wait your turn. With On Error Goto you can wait for a bit and then Resume Retry, where Retry is a label in your code to try the operation again. When you look at code with that structure it's very obvious what's going on:
On Error Goto ErrHand
dim ErrCnt as Integer = 0
do something
RetryFile:
On Error Goto FileErr
download a file
On error Goto ErrHand
do something with the file
On Error Goto 0
Goto Done
FileErr:
ErrCnt += 1
if ErrCnt < 10 then
sleep for a while
Resume RetryFile
else
Throw New Exception("Can't download the file")
end if
ErrHand:
Throw New Exception(Err.Number & ": " & Err.Description)
Done:
tidy up
One last point. I use both structures as I do like the Try Catch nesting, but as can be seen from the above, similar functionality can by achieved with On Error Goto, and in some ways it's neater as it moves the error handling out of the flow of the main code and all into one place.

Related

How do I replace On Error Resume Next with try-catch?

I have migrated a few controls from VB6 to VB.Net and one of the read-only properties has "On Error Resume Next." Because of this, the property is not throwing any error and it will return a value always.
Now I have replaced this with try-catch and I want your guys' opinion whether the try-catch implementation holds good or need any changes.
Below, I have shown the original code as well as the try-catch implementation code.
Original wizard migrated code
Public ReadOnly Property TotalAmount() As String
Get
'On Error Resume Next
Dim intIndex As Short
Dim numFS As Short
Dim totalAmount As Double
With m_udtProperties_READ
numFS = CShort(UBound(m_udtProperties_READ.FundSource))
If numFS >= 0 Then
For intIndex = 0 To numFS
totalAmount = totalAmount +
CDbl(m_udtProperties_READ.FundSource(intIndex).FromSide.Amount)
Next
End If
TotalAmount= CStr(totalAmount)
End With
End Get
Try catch implementation code.
Public ReadOnly Property TotalAmount() As String
Get
Dim intIndex As Short
Dim numFS As Short
Dim totalAmount As Double
With m_udtProperties_READ
Try
numFS = CShort(UBound(m_udtProperties_READ.FundSource))
Catch ex As Exception
End Try
If numFS >= 0 Then
For intIndex = 0 To numFS
Try
totalAmount = totalAmount + CDbl(m_udtProperties_READ.FundSource(intIndex).FromSide.Amount)
Catch ex As Exception
End Try
Next
End If
TotalAmount = CStr(totalAmount)
End With
End Get
End Property
is there any better way than above?
on error resume next is nasty when you need to convert code from vb6 to .net.
Basically, it means to simply ignore any error that comes after it and continue with the code execution right from the code row after the one that threw the error.
In .Net, doing the equivalent thing would mean that each row after where the on error resume next was, should be wrapped in a try...catch block with an empty catch.
Obviously, this is not practical nor is it good practice (In fact, it's a very bad practice to swallow exceptions).
Lucky for everyone that translates code, not every row is likely to throw an exception.
You need to isolate the danger zones in your translated code and only wrap them in try...catch.
I would recommend not to swallow the exceptions but propagate them to where they can be handled - so my advise is to do refactoring instead of just translating.

Does the VB ErrorObject work with try/catch blocks or just onError/GoTo?

When converting onError/GoTo statements from VB6 into VB.Net, I've been told to use try/catch statements instead. Most of the VB6 error blocks utilize Microsoft.VisualBasic.ErrObject to provide the error code and description. For example:
CombinePDF_ERROR:
lErrorCode = Err
strErrorSource = Err.Source
strErrorDescription = Err.Description
bInProcess = False
strCombinePDFLastFile1 = strFile1
strCombinePDFLastFile2 = strFile2
ChDrive left$(strCurrentDir, 1)
ChDir strCurrentDir
Call CombinePDFUIUnload
Err.Raise lErrorCode, strErrorSource, strErrorDescription
End Sub
Does the Err (Microsoft.VisualBasic.ErrObject) get its information from the onError/GoTo statements? lErrorCode, strErrorSource, strErrorDescription aren't given values prior to this. How do I replicate this functionality in a try/catch? Catch an exception and messageBox the message? First time using VB6 or VB.Net. Thank you for your time.
That specific code in your question is basically like this Catch block below. The Err.Raise is equivalent to a Throw, and the Err object is roughly equivalent to an Exception object.
Catch ex
bInProcess = False
strCombinePDFLastFile1 = strFile1
strCombinePDFLastFile2 = strFile2
ChDrive left$(strCurrentDir, 1)
ChDir strCurrentDir
Call CombinePDFUIUnload
Throw ex
But that's just that one block. You need to check each VB6 error handler, work out what its doing, and work out the closest equivalent with Try Catch. You need to understand the VB6 On Error and the Err object, and also .Net Try...Catch and Exception object.
You are going to have a very hard time on this project if you don't know VB6 or VB.Net.

Handling errors in try/catch blocks

What is the convention in VB when a sub requires that a try/catch block succeed in order to function, but the catch block doesn't bubble the exception up?
I could put all of the code into the try block, but this seems messy since most of it doesn't need to be tried, it just needs the try to have succeeded.
For example, should the catch block exit the sub? This would work in my current situation, and if it is the proper procedure, let me know, but what about the more general scenario where both both success and failure require additional processing?
I would so something along the lines of
Dim success As Boolean = False
Try
'Code to execute
success = True
Catch ex As Exception
End Try
If success Then
'success processing
Else
'failure processing
End If
This is an unanswered old question, so I try to answer it perhaps can help someone else.
Try this:
Dim successState As Boolean = True
Try
' Do something in here that
' might raise an error.
Catch
' Handle exceptions that occur within
' the Try block, here.
successState = False
Finally
' Perform cleanup code in here.
End Try
If successState Then
MessageBox.Show("Success!")
End If
When it catch error, no success box will appear.

vba: passing a variable into error handles

i have a statement:
on error go to label
however i would like to pass into the label the variable which caused the error
is this possible?
You can use Err to get the error No and Description
Sub|Function SomeName()
On Error GoTo Err_SomeName ' Initialize error handling.
' Code to do something here.
Exit_SomeName: ' Label to resume after error.
Exit Sub|Function ' Exit before error handler.
Err_SomeName: ' Label to jump to on error.
MsgBox Err.Number & Err.Description ' Place error handling here.
Resume Exit_SomeName ' Pick up again and quit.
End Sub|Function
First, I think you mean:
on error goto label
And no, you can't pass variables using a goto command. However, you can check the Err.Description for details, and if you are raising your own errors, you can do this:
' Raise a custom error.
Err.Raise Number:=vbObjectError + 1000, _
Source:="TestRaiseCustomError", _
Description:="My custom error description."
So if you are raising your own error, you could set Source to the field that caused the problem.
Refer to the Use the Err Object's Raise Method to Raise Custom Errors section at this link for more info.
I can't think of a clever way to do it. I normally have an error handeling class/function that way I can use "on error goto" to pass the error to the bottom block then call the error handeling function. The advantage of this is it's nice to have a centralised error handler but also you can customise it so in my case I pass the name of the procedure thats crashed. It's not pretty but you could if you really wanted to pass either a collection of variables (dependant on how many you have) or set up something to identify the variable based on the line number (which you'd have to add manauly...)
on error goto err
'Code
err:
ErrorHandeler err, "String with Procedure name"
Declare global variables and use them in the code and in your error code.
Public global_variable1 As Integer
Public global_variable2 As String
Private Sub Btn1234_Click()
....
end sub
Err_abcd:
....
End Sub

Better way to retry a statement that threw an exception in vb.net

I usually do something like this:
Dim Attempts = 0
Try
Retry:
<Block>
Catch
If Attempts < 3 Then
Attempts += 1
Thread.Sleep(2000)
GoTo Retry
Else
Throw
End If
End Try
This is really bad looking for me, but i don't know of a better way of doing it.
You could also try the following:
Dim retryCount as Integer = 0
Dim wasSuccessful as Boolean = False
Do
Try
<statements>
'set wasSuccessful if everything was okay.'
wasSuccessful = True
Catch
retryCount +=1
End Try
Loop Until wasSuccessful = True OrElse retryCount >=5
'check if the statements were unsuccessful'
If Not wasSuccessful Then
<do something>
End If
It will retry up to five times if the statements were not successful but will immediately exit the loop if the statements' execution was successful.
I think that's a bad usage, I use this one, and it's much cleaner.
Dim maxAttempt As Integer = 2
For i As Integer = maxAttempt To 0 Step -1
Try
...
'Successful Quit
Exit For
Catch
Thread.Sleep(2000)
End Try
Next
Just use a For loop or a While loop rather than GoTo, breaking on success. But other than that, it's the right approach.
Conceptually it's the right approach, although I would not catch each and every exception, see answer by #0xA3.
You could make it a bit 'prettier' by separating the retry logic from the actual code, e.g.:
Sub TryExecute(Of T As Exception)(ByVal nofTries As Integer,
ByVal anAction As Action)
For i As Integer = 1 To nofTries - 1
Try
anAction()
Return
Catch ex As T
Thread.Sleep(2000)
End Try
Next
' try one more time, throw if it fails
anAction()
End Sub
Which could then be used like so:
TryExecute(Of SomeExceptionType)(3, Sub()
<Block>
End Sub())
This will only work in VB 10, if you're using .Net 3.5 / VB 9, you need to separate this in a separate function
In general, retrying something that failed should be considered very carefully. Usually it is much better to report the error and let the user decide.
Raymond Chen gives a nice example how automatic retries might lead to unwanted problems and gives the advice to avoid retrying:
Take it easy on the automatic retries