I have a Function that has some bug in it somewhere causing it to return #VALUE when I try to execute it in excel.
I have no idea where the error is, and stepping through the code is just tedious. So I'd like the debugger to break as soon as an error occurs.
I tried going to Tools->options->General->"Break on All Errors" but noticed no change.
How do I get the VBA IDE to break on an error?
Just add an error handler in your function like the one below.
If an error occurs, the IDE will print the error description in the immediate window and stop on Debug.Assert 0.
Then press F8 two times to go to the line where the error occured.
Function Test() As Variant
On Error GoTo ErrHandler
Dim v(), n&, r&, c&
For r = 1 To 3
For c = 1 To 4
n = n + 1
ReDim Preserve v(1 To r, 1 To c)
v(r, c) = n
Next c
Next r
Test = v
Exit Function
ErrHandler:
Debug.Print Err.Number & ": " & Err.Description
Debug.Assert 0
Resume
End Function
Something like:
Public Function dividddeee(a As Variant, b As Variant) As Double
On Error GoTo wtf
dividddeee = a / b
Exit Function
wtf:
On Error GoTo 0
MsgBox "Houston, we've had a problem here"
MsgBox a & vbCrLf & b
End Function
If you add error handlers, you can take advantage of Debug.Assert to force a break if you don't want the standard handler to execute. You can define a compiler constant to just let your error handlers deal with it when you release it to the wild. If you want to see what specific line caused the error, you can put Resume Next after the Debug.Assert. When you step through it, it will take you to the line immediately after the one that caused the error.
Drop this small demo into Module and run the Sub with both Release = True and Release = False for an example:
Option Explicit
#Const Release = False
Private Sub Demo()
Debug.Print DivByZero(5)
End Sub
Public Function DivByZero(inValue As Integer) As Double
On Error GoTo Handler
DivByZero = inValue / 0
Exit Function
Handler:
#If Release Then
MsgBox Err.Description & " in DivByZero"
#Else
Debug.Assert False
Resume Next
#End If
End Function
If you call a VBA function as UDF the settings of the VBA IDE are not involved. So no chance for error debugging this way.
Try calling the function from a test Sub. Then the error debugging will work.
But there are some things a function cannot do as a UDF but can do called from a Sub. If one of those things is the reason for the #VALUE error, then no way around setting a breakpoint in the function and stepping forward until the next step is not possible. The last line in stepping is the error line.
You should really mention if the function is
Called from an Excel cell.
Has an event handler.
Show how your variables are declared.
If called from a cell, the inputs to the function can cause you problems with different inputs. Sometimes preventing the call of the function if the types significantly change to something unexpected. For example a parameter declared as variant in its signature will pass in an error but fail in the function. You may be trapping this error and returning #VALUE in the code. No way for us to know that.
If you have an event handler, for a quick test, you could put a 'Stop' in the event handler to stop like you are asking. If not you can put one in as already stated. Assertions are nice, I like them and use a lot of them - but here since you know the function and are working on this explicit problem a Stop should be good enough for your testing. Just don't save it for production code. Make a Test copy of the book.
Related
I'm trying to find the line number where my code crashes but many explanation on this site seems to complicated for my level.
My code is basically as below and I have no idea where it's breaking.
Sub1
Call function1
Call function2
End Sub
Other answers on this website seems to be just a short function. But I don't know where to call the function in my code or how to get a popup message. If I'm meant to put my sub1 code into their function, I don't know where either. Beginner here.
If your code doesn't have line numbers, then VBA has no way of giving you line numbers.
You can write VBA and make it look 1980-like to do this:
Sub1
On Error GoTo 100
10 Call Function1
20 Call Function2
90 Exit Sub
100 Debug.Print Err.Message & " on line " & Erl
End Sub
But you don't want to do that. Really, you don't need a line number.
You need smaller functions that handle runtime errors.
On Error GoTo ErrHandler
When a runtime error occurs, execution jumps to the line label called ErrHandler.
...
Exit Sub
ErrHandler: '<< the line label is denoted with a colon
What goes in that handler? If you're debugging, you might want to just Stop execution there and inspect your locals:
Stop
Then add Resume on the next line, and press F8 to step into it. Resume will return to the call that caused the error. If that's a function call, then you need to handle runtime errors in that function.
Make sure you never leave Stop and Resume instructions in production code:
Sub WhenWillThisEnd()
On Error GoTo ErrHandler
Debug.Print 42/0
Exit Sub
ErrHandler:
Resume 'jumps back to the line that caused the error
Resume Next 'resumes execution on the line right after the one that went boom
End Sub
I have a situation where I'd like to use GoSub within a Subroutine and in some situations Return, but in others I would not have it Return. This will be in a large be For Loop and GoSub without Return could happen potentially hundreds of times. If I do not Return will this build up in memory and cause any issues?
I suppose my question boils down to: does GoSub stack? And will a large enough stack of un-returned GoSub's cause problems?
If it does stack, I can change the code to use GoTo in the instances where I do not want to Return, but for simplicity's sake I'd rather not.
Also thank you in advance for not lecturing me on GoTo/GoSub not being best practice :)
As plenty of others said: Don't do it. I am now programming (for a living) since 30 years and never had the need to use GoSub/Return except as a substitute when a programming language didn't provide any subroutines.
That said - I was curious about how VBA handles this. First thing: I assume that there must be a kind of stack. You can have multiple GoSub/Return in one routine and it is handled correctly:
Sub testSub1()
Call StrangeRoutine1
End Sub
Sub StrangeRoutine1()
GoSub L1
Exit Sub
L1:
Debug.Print "Strange1 - L1a"
GoSub L2
Debug.Print "Strange1 - L1b"
Return
L2:
Debug.Print "Strange1 - L2a"
Return
End Sub
This Prints:
Strange1 - L1a
Strange1 - L2a
Strange1 - L1b
So the return statements jumps after the correct GoSub - this is for sure handled with a kind of stack.
However, it seems that this stack is cleared once a Subroutine is left. The following routine has a GoSub, but no Return. So it leaves an open GoSub on the "stack". But when called a 2nd time and issues a Return without an GoSub, it throws an runtime error 3 "Return without GoSub`.
Dim count As Long
Sub testSub2()
For count = 1 To 2
Call StrangeRoutine2
Next
End Sub
Sub StrangeRoutine2()
If count > 1 Then Return
GoSub L1
Exit Sub
L1:
Debug.Print "Strange2 - L1"
End Sub
P.S.: Did i mention it: Don't do it!
The shortcut to see the stack in VBE is Ctrl+E. As you see it does not stack:
Sub GosubDemo()
GoSub MyRoutine
Debug.Print "Line before Exiting"
Exit Sub
GoSomeWhereElso:
Debug.Print "SomewhereElso I am "
Return
MyRoutine:
Debug.Print "My routine"
GoSub GoSomeWhereElso
Return
End Sub
However, do not use GoSub or GoTo in VBA. It is considered a very bad practice. GoTo could be used for Error Handling like On Error GoTo ErrorHandler.
GoSub ... Return MSDN
Dijkstra - GoTo considered harmful
I wish to pause the execution of my VBA code once an error appears, and continue the execution when corrected?! Because I have a very long execution, so it always goes from the beginning...
you need to use an error handler. Something like
On Error GoTo errorTrap
at the beginning of your code directly after your dims and other setup.
Then for the actual errortrap you would write this before the End Sub.
The whole thing would look like this.
Sub test()
Dim v As Variant, x As Integer 'etc etc
On Error GoTo errorTrap
'run your code here. An example is below
x = "hello" 'this will create an error since hello is not an integer
MsgBox "finished"
End 'ignore the error trap when done
errorTrap:
Debug.Print Err.Description
Stop 'stop here and figure out what is going on.
'any other code needed to fix the error
Resume Next
End Sub
hey, first thanks to all for answering my other questions. I am extremely new to Excel VBA and some things I just get hung up on. I have a userform (not embedded in a worksheet) and I have a few fields that are for currency (amounts, etc) and if someone inputs a letter it errors after they hit the command button and they lose all info. I need error code to where I can tell them in a msgbox that they should not put characters in a currency field. I don't need it specific to those fields but I don't want them to lose there data when they hit the command button to dump the data into a spreadsheet.
How can I have them see the error msg, hit the ok button and have it take me right back to the screen without losing the data they have alread entered? Basically give them the opporunity to correct their error but not have to reinput 50 fields?
Thanks
Can't be specific without the actual code, but add error handlers to your code:
Sub SomeRoutine()
Dim stuff
On Error GoTo EH
' Code
Exit Sub
EH:
' Any errors with come here
If Err.Number = <specific errors to trap> Then
MsgBox "Oops..."
'As a debug tools, put a Resume here,
' but be sure to put a break on it,
' and don't leav it in the finished code
Resume
End If
End Sub
As I understand it you want the user to enter numeric numbers only into the text box - right? This is what I normally do.
In a global module add the following function:
Function IFF(c, t, f)
Dim v
If c Then v = t Else v = f
IFF = v
End Function
Then in your textbox_change event add the below:
Private Sub txtAmount_Change()
txtAmount.Text = IFF(IsNumeric(txtAmounto.Text), Val(txtAmount.Text), 0)
End Sub
This will basically put 0 in the box as soon as the user enters an invalid number.
Hope this helps
A slightly different take on error handlers than that given by chris neilsen
Sub SomeRoutine
On Error GoTo ErrHandler 'doesn't matter where you put it
'as long as it's before the code you want to protect
'Dim Stuff
'Do Stuff
ExitRoutine: 'Note the colon(:), which makes this a label
'Any cleanup that you _always_ want
Exit Sub
ErrHandler:
Select Case Err.Number
Case <some error you want to handle specially>
'special handling
Case Else
'default handling, which may include:
Resume ExitRoutine
End Select
Resume
End Sub
Note that that last Resume will never get hit in normal processing (if you've written your error-handling Cases correctly), but will let you set it as "Next Statement" when you're debugging in Break mode. This is an easy way to see exactly which statement threw the error.
Can somebody explain me what this code means ?
I guess its if an error occurs go to section ErrHandler, but why is there a resume next at the end and Exit sub inbetween ?
On Error Goto ErrHandler:
N = 1 / 0 ' cause an error
'
' more code
'
Exit Sub
ErrHandler:
' error handling code
Resume Next
End Sub
I also wrote a short code to get a better understanding.
If you run it in VBA excel you will get 4 numbers that pops up in order : 1,2,2,4
I understand the first 2 numbers , but why is the third number 2 and the last one 4 ?
btw for this example no error occured .
On Error Goto Err:
n = 1
MsgBox n
Err:
n = 2
MsgBox n
Resume Next
MsgBox 4
Resume Next
End Sub
It has already been said that Resume Next will pick up execution at the point after the error occurred. There is also Resume, which picks up execution starting with the line that caused the error and Resume , which picks up execution at a specified label.
So, this is what happens in the code you provided:
N = 1 / 0 raises a divide by zero error
execution is moved to the ErrHandler label.
Error handling code will run till it reaches Resume Next.
Resume next will move execution to the line after N = 1/0. Since you are going back it makes sense that your error handling code might do something like this N = 1, otherwise the rest of the code will try to use N which is uninitialized and you will get another error.
The following link provides a simple explanation oerror handling and the three resume statements:
http://www.cpearson.com/excel/ErrorHandling.htm
It picks up execution of the method body at the point after where the error occured.
From the On Error documentation:
Specifies that when a run-time error occurs, control goes to the statement immediately following the statement where the error occurred, and execution continues from that point.