I have inherited a hideous Excel VBA application, written by a guy who apparently didn't like functions and comments. Opening the source code is like staring into an abyss of madness, with 9 levels of hell indentation awaiting to trap the unwary.
First, the setup.
In the process of debugging an intermittent issue, I have identified a particular line in the primary function as its location. Since the exception is not easy for me to fix, I gave this line, and only this line, an error handler that displays a custom message.
' Lots of stuff cut out
On Error GoTo TechReleaseBuildError
lb_Err1 = BuildDirectories(gj_TechBld.mt_TechRlsOutDir)
On Error GoTo 0
' Lots of stuff cut out
Exit Sub
TechReleaseBuildError:
MsgBox "Error building tech release. Pause the synchronization software and try again."
Stop
Resume
End Sub
The Stop and Resume are only there to help debugging, since the normal error messages that pop up don't have an option to debug the error.
Within the BuildDirectories function, I have identified one line that was causing the error that was being kicked up to primary function. I have placed error handlers in this function as well.
' Lots of stuff cut out
Exit Function
TEST:
Stop
Resume
SynchroRetry:
Dim CurrentError As ErrObject
CurrentError = Information.err
SynchroRetryCounter = SynchroRetryCounter + 1
Debug.Print "Synchro Error, retrying #" + SynchroRetryCounter
Stop
If SynchroRetryCounter > 5 Then
Stop
Information.err.Raise CurrentError.Number, CurrentError.Source, CurrentError.Description, CurrentError.HelpFile, CurrentError.HelpContext
Else
Application.Wait Now + #12:00:03 AM#
Resume
End If
End Function
At the very beginning of BuildDirectories I have placed an On Error GoTo TEST statement as a catch-all error handler so I can identify what lines they are coming from, and also used the SynchroRetry handler for the one line that I already know causes an issue, which I immediately reset back to the TEST handler. The SynchroRetry handler just waits for 3 seconds and retries, failing after the 5th retry.
' For some reason, this line (and only this line so far) causes problems when synchronization software is running.
' The error handler waits for a few seconds then tries again a few times.
Dim SynchroRetryCounter As Integer
SynchroRetryCounter = 0
On Error GoTo SynchroRetry
lj_FSO.CopyFile Source:=lt_RPOFilePath & lt_RPOFileName, Destination:=lt_VinRposDir & "\" & lt_RPOFileName, OverWriteFiles:=True ' Trouble line
On Error GoTo TEST
I have verified with Find that these are the only 3 On Error statements in the function (the only ones in the module, even). The top-level function also has no On Errors other than the two shown.
In brief summary, the top-level function has an error handler on only one line, and the function it encloses also has two error handlers inside it, one for one of its known troublesome lines and a general one that should be encompassing the entire function.
The problem is that some error is occurring that is causing my "Error building tech release." message to display, but does not seem to be triggering the TEST or SynchroRetry error handlers. I cannot imagine how this is possible. I'm relatively new to VBA's monstrosity of an error handling model, but I don't believe I did anything wrong. Did I miss some subtlety here? How is an exception able to bypass my handlers and bubble up the stack like this? I do not believe the assignment or passing of the function value could cause the problem, since their just a Boolean and String value. The problem is also intermittent, and only appears when we have file synchronization software active on the directory this program is copying files to.
So a couple hours after I posted this, I managed to figure it out.
The reason that the Debug button wasn't on the error boxes is because in Options the Error Trapping was set to "Break on Unhandled Error". Changing it to "Break in Class Module" brought the button back.
The reason the exceptions were bypassing my error handlers is because they were happening in my SynchroRetry error handler. In particular, I had to change the first few lines from this:
Dim CurrentError As ErrObject
CurrentError = Information.err
SynchroRetryCounter = SynchroRetryCounter + 1
Debug.Print "Synchro Error, retrying #" + SynchroRetryCounter
to this:
Dim CurrentError As New ErrObject
Set CurrentError = Information.err
SynchroRetryCounter = SynchroRetryCounter + 1
Debug.Print "Synchro Error, retrying #" + CStr(SynchroRetryCounter)
Note the additions of New, Set, and CStr.
Problem solved.
Related
Hello guys i have loop which opens for me Inline Shape (xlsx file attached in doc). It works fine but sometimes i get an error:
Run-time error 6069
It informs that i try to open excel application and probably it's not installed (WOW). When i debug it highlights a lane
wddoc.InlineShapes(lShapeCnt).OLEFormat.Activate
I press "Run" and it works normally like there was no error.
But it's frustrating cause i need to interfere and user who will use this tool can't do that.
It may be some timeout issue. You may try to repeat operation for a while:
...
timeout = Now + #00:00:01# 'Try for 1 second max
On Error Resume Next
Do
Err.Clear
wddoc.InlineShapes(lShapeCnt).OLEFormat.Activate
Doevents 'Sometimes you need to allow events to succeed in some methods
Loop While Err.Number=0 Or Now>timeout
If Err.Number <> 0 Then
MsgBox "Coudn't make call..."
Err.Raise Err.Number
End If
On Error GoTo 0 'or whatever error handler you were using
...
I have implemented the error handling methods from "Professional Excel Development" by Rob Bovey and Stephen Bullen in my Excel VBA project.
I would like to know how to run in DEBUG mode so that I can find the exact location of the error since the output error log only shows me the function where the error occurred, not the line.
P.S. I have answered my own question below, since I figured it out during the course of running my code. Please see their excellent book for the full method.
-- HOW TO RUN IN DEBUG MODE --
-- This code is based on the method from book "Professional Excel Development" by Rob Bovey and Stephen Bullen, but the book isn't detailed about how to debug.
-- This answer doesn't show all error handling code, only how to use what you have implemented from the book in debug mode.
-- Your error stack will appear in a log file called Error.log in the same directory as your program. Scroll to the bottom to find the name of the function where the error occurred (the first function listed at run time.) This might get you close enough to find the error.
--To find the exact point of the error using this error handling system, you must run in debug mode.
Go to module M_ErrorHandler which contains the error handling functionality from this book. You might have named your module differently.
Change
Public Const gbDEBUG_MODE As Boolean = False
to
Public Const gbDEBUG_MODE As Boolean = True
Run the program.
When an error occurs, the execution will stop in that function at the Stop line under ErrorHandler.
ErrorExit:
On Error Resume Next
-- Include cleanup code here
bBoldLateArrivals = bReturn
Exit Function
ErrorHandler:
bReturn = False
If bCentralErrorHandler(msMODULE, sSOURCE) Then
Stop -- *** STOPS HERE ***
Resume
Else
Resume ErrorExit
End If
End Function
In debugger, step through the code using F8. The "Resume" line will execute next. The next line you jump to will be the exact line that caused the error. Note the location of the error and determine how to fix it.
Press F5 to run the bad line and raise the error again, which will take you to STOP again.
Unfortunately, you can't just turn off Debug and continue from this point. You now have two choices: a) Just quit the program in any state (stop execution) or b) Finish carefully to protect your data and worksheets.
To continue with full error cleanup and prevent problems in the worksheets, you must resume execution from the ErrorExit label of each function in the stack. To do this, scroll up a few lines and click on the line "On Error Resume Next" to place the cursor. In your debug menu, choose "Debug...Set Next Statement" or use Ctrl-F9.
ErrorExit:
On Error Resume Next -- *** PUT CURSOR ON THIS LINE **
-- All the good cleanup lines you have here will run
Press F5 to continue running. You will stop on the Stop line in the next function up the stack. Click again at "On Error Resume Next" in that function and use Ctrl-F9 then F5 again. Repeat until you have reached the top of the program.
Fix the error.
Set DEBUG mode to false and save.
Public Const gbDEBUG_MODE As Boolean = False
I have a big macro which basically processes some columns and spits out results based on some cross-checking with an access 2003 database. It works absolutely fine - no hitches at all.
However, I recently had to make a modification to it. It was literally changing an '8' to a '9' in one line of the code. But next time I ran it, it threw the 1004: Method 'Range' of object '_Global' failed error. Excel 2003 is a funny thing - I once scratched my head for hours over this, trying to find offending lines of code that could be causing the error, but alas to no avail. I did something that I didn't expect to trigger anything:
Starting with the original macro (100% confirmed working), if I just open the code up, and then save it so the 'last updated' metadata will update to reflect the save, though absolutely nothing has changed, it will throw that error again on opening.
It's as if it's so fragile that saving the macro as is will break it. Any ideas?
Update: here's what I changed that initially brought about the issue
iOutputCols = 9 'this was changed to 9 from 8
ReDim Preserve sOutputCols(iOutputCols)
sOutputCols(0) = "Policy No"
sOutputCols(1) = "Client"
sOutputCols(2) = "Trans"
sOutputCols(3) = "Effective Date"
sOutputCols(4) = "ClosingRef"
sOutputCols(5) = "Gross"
sOutputCols(6) = "Comm"
sOutputCols(7) = "Net Due"
sOutputCols(8) = "Risk" 'this line was added
Making the change here, while originally causing the error, doesn't seem special - I did small changes like the above elsewhere in the code and in other modules, one time I even did something as testval = "test" and even that redundant line will produce the error. The most minimalistic way to cause it? Simply open it up, save it without changing anything, and on next use the error occurs.
The error occurs at this line, in a completely different code section which is part of a form:
If strErr <> "" Then MsgBox strErr, vbInformation + vbOKOnly, "Action Error"
Application.ScreenUpdating = True 'error occurs here, message box which shows me the error right above
End Sub
Update 2
Removing the error handling throws the error on this line
Case "> Send Next Period Reminder" 'error on line below
Call ReplaceText(wordApp, "[office_address]", Range("Address_" & Worksheets("UserParms").Range("F6").Value).Value) 'error this line
Call ReplaceText(wordApp, "[office_address2]", Range("Address2_" & Worksheets("UserParms").Range("F6").Value).Value)
'more of the same replacetexts below
For context, this is when an option is selected for "Send Next Period Reminder", which pulls a word .dot template from a static folder and populates it based on the data selected within the sheet (Hence the replace texts). This is in a different module and has never been touched before.
Try properly qualifying your Range method calls. You have lines like this:
Call ReplaceText(wordApp, "[office_address]", Range("Address_" & Worksheets("UserParms").Range("F6").Value).Value) 'error this line
Call ReplaceText(wordApp, "[office_address2]", Range("Address2_" & Worksheets("UserParms").Range("F6").Value).Value)
While it may not be obvious, there are cases, both environmental and code-based, where these unqualified uses of Range could fail. Change the references like Range("Address... to something like yourTargetWS.Range("Address...
I am doing project in powerpoint 2007 automation. In that i am using macro programming (VBA). I am getting the following error while run the macro.
Err.Number= -2147024809 (80070057)
but my concern is not with error Because i want to catch this error and base on this error i want to perform some action.
Thats why i try to code like this for error handing code:
OnError Goto Err:
'some code
Err:
If Err.number = -2147024809 (80070057) then
'do something
end if
Resume next
So bascially if i write error number in this way then its not allow it. it gives error.
and major thing is when error occurs sometime it did not go to "Err : ". It just pop-up error with "End" and "Debug" options.
While it seems to work, I'd be cautious about using a reserved object name (Err) as a label, for one thing.
On Error GoTo ErrorHandler
' Your code here
' Make sure you don't hit the errorhandler when there's
' no error:
NormalExit:
Exit Sub ' or Function, whichever this is
ErrorHandler:
If err.Number = 123456788 Then
' Take corrective action
' then continue
Resume Next
End If
' Trap other specific or general errors here
' Then make sure you know where you're going to exit:
Resume NormalExit
If you need to trap very specific errors that might occur only at certain places in your code, you can also do a local errorhandler:
On Error Resume Next
' Some code that might cause problems
' Did it throw the error you're trying to trap?
If Err.Number = 12398745 then
' Corrective action
End If
' And now we return to our regularly scheduled error trapping
On Error GoTo ErrorHandler
Error numbers are numeric: -2147024809, its just displayed to you as the string "-2147024809 (80070057)" for clarity (80070057) being the error number in hexadecimal.
You want;
if err.number = -2147024809 then ....
or if you so choose
if err.number = &h80070057 then ....
The 80070057 part of the error message is the unsigned hexadecimal version of the negative number -2147024809. Remove that part of your code and you will be fine, if you want to keep track of the hex version of the number (can be useful for researching errors via Google etc) then just add it as a comment.
What options are there in ASP Classic for error handling?
For example:
I'm using the Mail.SendMail function but when switching on the testing server it doesn't work, which is normal. I want to test if mailing is possible, if not then continue and/or show a message.
Any ideas?
There are two approaches, you can code in JScript or VBScript which do have the construct or you can fudge it in your code.
Using JScript you'd use the following type of construct:
<script language="jscript" runat="server">
try {
tryStatements
}
catch(exception) {
catchStatements
}
finally {
finallyStatements
}
</script>
In your ASP code you fudge it by using on error resume next at the point you'd have a try and checking err.Number at the point of a catch like:
<%
' Turn off error Handling
On Error Resume Next
'Code here that you want to catch errors from
' Error Handler
If Err.Number <> 0 Then
' Error Occurred - Trap it
On Error Goto 0 ' Turn error handling back on for errors in your handling block
' Code to cope with the error here
End If
On Error Goto 0 ' Reset error handling.
%>
Regarding Wolfwyrd's anwer: "On Error Resume Next" in fact turns error handling off! Not on. On Error Goto 0 turns error-handling back ON because at the least, we want the machine to catch it if we didn't write it in ourselves. Off = leaving it to you to handle it.
If you use On Error Resume Next, you need to be careful about how much code you include after it: remember, the phrase "If Err.Number <> 0 Then" only refers to the most previous error triggered.
If your block of code after "On Error Resume Next" has several places where you might reasonably expect it to fail, then you must place "If Err.number <> 0" after each and every one of those possible failure lines, to check execution.
Otherwise, after "on error resume next" means just what it says - your code can fail on as many lines as it likes and execution will continue merrily along. That's why it's a pain in the ass.
1) Add On Error Resume Next at top of the page
2) Add following code at bottom of the page
If Err.Number <> 0 Then
Response.Write (Err.Description)
Response.End
End If
On Error GoTo 0
A rather nice way to handle this for missing COM classes:
Dim o:Set o = Nothing
On Error Resume Next
Set o = CreateObject("foo.bar")
On Error Goto 0
If o Is Nothing Then
Response.Write "Oups, foo.bar isn't installed on this server!"
Else
Response.Write "Foo bar found, yay."
End If
the statement On Error Resume Next should be placed on top of what we want to validate.
On Error Resume Next
'Your code logic is here
Then end with statement like:
If Err.Number <> 0 then
'Your error message goes here'
End if
For anytone who has worked in ASP as well as more modern languages, the question will provoke a chuckle. In my experience using a custom error handler (set up in IIS to handle the 500;100 errors) is the best option for ASP error handling. This article describes the approach and even gives you some sample code / database table definition.
http://www.15seconds.com/issue/020821.htm
Here is a link to Archive.org's version
Been a while since I was in ASP land, but iirc there's a couple of ways:
try catch finally can be reasonably simulated in VBS (good article here here) and there's an event called class_terminate you can watch and catch exceptions globally in. Then there's the possibility of changing your scripting language...
Some scenarios don't always allow developers to switch scripting language.
My preference is definitely for JavaScript (and I have used it in new projects). However, maintaining older projects is still required and necessary. Unfortunately, these are written in VBScript.
So even though this solution doesn't offer true "try/catch" functionaility, the result is the same, and that's good enough for me to get the job done.