Speed impact of On Error Resume Next - vbscript

Given that vbscript does not have the On Error Goto MyLabel kind of syntax as with vba, I'm considering using the following to handle errors:
On Error Resume Next
'Do some stuff
If Err.Number <> 0 Then
Err.Clear
'Handle the error
End If
'Do some more stuff
If Err.Number <> 0 Then
Err.Clear
'Handle the error
End If
'...
My question is: What would the speed impact be of this type of technique?
Additionally, I would like to know if there is a better way to build some error handling into vbscript

It makes no sense to compare the speed of a correct vs. a faulty implementation. If your script does not fullfill its specs without error handling and is to slow when using OERN, you have to choose another language.
As OERN hides errors, its scope should be as small as possible. Your sample code smells like using OERN globally - a very bad idea. In addition: You need a Err.Clear or "On Error GoTo 0" after each risky operation and its corresponding check of Err.Number.
In other languages (e.g. Python) "it's easier to ask forgiveness than permission"; because of the poor nature of VBScript's error handling you should "look before you leap" in most cases and use OERN only when absolutely unavoidable (e.g. check for existence of registry keys, deal with possibly failing external resources).
The idiom for local error handling:
Dim gaErr ' global error array
...
Sub SomeSub()
...
OERN
risky op
gaErr = Array(Err.Number, Err.Description, ...)
OEG0
If gaErr(0) Then
handle error
...
End Sub
If your specs make a global error handling necessary:
Dim gaErr
OERN
ret = main()
gaErr = Array(...)
OEG0
If gaErr(0) Then
some fatal/not locally handled error occured somewhere in your script
handle it gracefully
Else
If ret is bad Then
deal with this
Else
ask for more money
End If
End If
WScript.Quit good/bad
Function main()
main = ...
End Function
Update wrt comment:
Why is a global OERN evil? Consider:
OERN
(1) Process/change important data
(2) Delete original data - if (1) failed you just destroyed the base of your business
OERN
(1) Process/change important data
(2) If Err.Nunber Then Backup(database, destinatiom)
(3) Continue although the Backup didn't take place, because at least three error were hidden
BTW: Your addition of the Err.Clear are in the wrong place - you loose the error info before you can handle the error.
Update wrt comment II:
Consider this demo script:
' dgeh.vbs - demo global error handling
Option Explicit
' globals
Dim gWAN : Set gWAN = WScript.Arguments.Named
Dim gaErr
Dim iRet
' top-level/global OERN starts here (errors before will abort mercylessly)
On Error Resume Next
iRet = main() ' not more than two statements!
gaErr = Array(Err.Number, Err.Description, Err.Source)
On Error GoTo 0
If gaErr(0) Then
WScript.Echo "Fatal Error:", Join(gaErr, " * ")
iRet = 1 ' or choose a better number
Else
If iRet Then
WScript.Echo "need one of /good, /bad, or /fatal, Mr. User!"
Else
WScript.Echo "all's well"
End If
End If
WScript.Quit iRet
Function main()
main = 2
If gWAN.Count = 1 And (gWAN.Exists("good") Or gWAN.Exists("bad") Or gWAN.Exists("fatal")) Then
readTheDocs
main = 0
End If
End Function
Sub readTheDocs()
WScript.Echo "read Microsoft's Docs"
If gWAN.Exists("fatal") Then
caTastrophy
Else
trySomethingRisky
End If
End Sub
Sub trySomethingRisky()
Dim n
If gWAN.Exists("bad") Then n = 1 / 0
WScript.Echo "but be skeptical of the text and your interpretation"
End Sub
Sub caTastrophy()
On Error Resume Next ' simulating the evil global OERN and not checking *each* statement
WScript.Echo "saving the world"
saveTheWorld
WScript.Echo "deleting the now obsolete original"
On Error GoTo 0
End Sub
output for
cscript dgeh.vbs /fatal
read Microsoft's Docs
saving the world
deleting the now obsolete original
all's well
echo %ERRORLEVEL%
0
As there is no Sub saveTheWorld, you just made error handling, VBScript, and all the rest obsolete. You can't even promise never to use the evil global OERN again, because you - and those scripting guys who use it habitually (and thereby have proven that they are paid for their jokes, not their code) - have vanished. Let's hope the calling process won't take the ERRORLEVEL value as licence for some further deletions.
output for
cscript dgeh.vbs
need one of /good, /bad, or /fatal, Mr. User!
echo %ERRORLEVEL%
2
cscript dgeh.vbs /nix
need one of /good, /bad, or /fatal, Mr. User!
cscript dgeh.vbs /fatal /bad
need one of /good, /bad, or /fatal, Mr. User!
demonstrate the "look before you jump" strategy.
output for the good case:
cscript dgeh.vbs /good
read Microsoft's Docs
but be skeptical of the text and your interpretation
all's well
echo %ERRORLEVEL%
0
both messages are shown (and rightfully so).
and now something entirely different:
cscript dgeh.vbs /bad
read Microsoft's Docs
Fatal Error: 11 * Division by zero * Microsoft VBScript runtime error
echo %ERRORLEVEL%
1
Please mark the absence of the second message. The divide by zero will cause the execution of the next line in the active OERN scope (saving in gaErr) and not continue blindly/mindlessly. The risky operation (doing what main() and all its children do) is then checked (via the gaErr proxy). That honors the rule "no more than two line (risky op and saving the eror info) in an OERN scope".
The price to pay for this kind of global error handling: You loose the line number info - dealing gracefully with an error becomes more difficult.
But as there is just one OERN in your script (you won't have Sub caTastrophy() in your programs from now on), you can comment it out during development and debugging.
output with OERN commented out:
cscript dgeh.vbs /bad
read Microsoft's Docs
E:\trials\SoTrials\answers\21901890\vbs\dgeh.vbs(46, 30) Microsoft VBScript runtime error: Division by zero

When i use error handling in Vbscript i do not find it has a performance issue. However in my programs it will normally do something quite simple like output a "Failed" statement to a log file then wscript.quit, so i have never found it to reduce system performance. On the example you have given it is usual to add a On Error Goto 0 after the last End If where you are checking for an error. This could cause an error not to be cleared so when you next perform an error check it has incorrectly still storing the old error value.

Related

If Run can't find file then continue anyway

In VBScript I am running a program I expect is likely to be in the PATH or current directory of the script. However if it is not in those places and cannot be found the script stops with error "The system cannot find the file specified". The command is not crucial and I would like to continue even in that case. Is that possible?
As #Noodles pointed out in the comments you can use On Error Resume Next to have the interpreter hand over error handling to you/your code, and then "handle" the error by glossing over it:
Set sh = CreateObject("WScript.Shell")
...
On Error Resume Next
sh.Run "unimportant.exe", 0, True
On Error Goto 0
...
If you want to be a little more diligent and have the script fail if other errors occur add a routine to check the error number and quit after displaying an error message if it's neither 0 nor 0x80070002:
Set sh = CreateObject("WScript.Shell")
...
On Error Resume Next
sh.Run "unimportant.exe", 0, True
If Err.Number <> 0 And Err.Number <> &h80070002 Then
WScript.Echo Trim("[0x" & Hex(Err.Number) & "] " & Err.Description)
WScript.Quit 1
End If
On Error Goto 0
...
Be very careful when using On Error Resume Next, though. Use it sparingly, and disable it again (On Error Goto 0) as soon as possible. Contrary to popular belief the statement doesn't magically make errors go away. It just hands full responsibility for checking and handling errors over to you. Basically it's the VBScript term for "shut up, I know what I'm doing".

What is the code to exit/ stop VBscript from running in the event of a condition not being met?

I have looked on Google and the answer is not there!
First things first. WScript.Quit DOES NOT WORK! I have no idea what "WScript" is but it clearly has nothing to do with client side scripting for a web page. I have seen this "WScript" thing somewhere before and it just produces errors (maybe obsolete or something) so please do not suggest it...
Anyway... all I wish to do is completely stop the script in the event of a condition not being met. Obviously I don't want "Exit Sub" because the code would then carry on running if that sub is embedded!
I am aware of the "stop" command but I am under the impression that it is only used for debugging.
Hopefully a very simple question.
UPDATE and Conclusion: Before I close this subject I will just expand a little on what I was trying to do...
I had a number of main subs that were being started by a button click. In order to make it so that I did not have to edit each individual sub I embedded a universal sub within each one that did a preliminary check.
Part of that preliminary check was to stop the program in the case of an incorrect user input. If an error was detected I wanted to halt all progress from that point on. An "exit sub" would obviously just skip the rest of that preliminary sub and the main sub would carry on executing.
In the end it was just a case of writing in an error flag (that is checked in the main subs) or incorporating the error condition operation in each main procedure. In that way you exit the main sub and the problem is solved.
It was not laziness - I just wanted to reduce the amount of code. Thank you for the responses anyway.
I've found that the WScript is always available if running a .vbs/.vbe/.wsf script using either the wscript.exe or cscript.exe engine. When WScript is not available is if running using a different engine. E.g. running VBScript within a HTA, a webpage, in VBA or from a hosted COM script control.
To exit a script which is not running from wscript.exe or cscript.exe, you can do something like the following:
main
Sub main
' execute code here
' oops a confition is not met:
If Not condition then Exit Sub
' more code to execute if condition was met
End Sub
You can use something like this:
Sub MyFunc
----------
My Code
----------
End Sub
Function Main
On Error Resume Next
MyFunc
If Err.Number <> 0
Exit Function
End Function
It'll stop executing the code, the point it finds an exception or throws an error.
The simplest way, What i found is: WScript.Quit
The Wscript object is only available if you are running in the Windows Script Host (wscript.exe,cscript.exe). NOT Internet Explorer (iexplorer.exe, mshta.exe)
Well an easy way to do this is to declare a global variable that gets set to false until an error occured, and if the error occured then that variable will be set to true, after that anytime that variable gets checked you can exit sub\function\for\do\whatever.
Example:
Dim ErrorOccured
On Error Resume Next
ErrorOccured=False
Sub Main()
If ErrorOccured Then Exit Sub
'some code
MsgBox "Main has run"
End Sub
Sub MakeAnError
If ErrorOccured Then Exit Sub
'some code
Err.Raise 2
If Err Then ErrorOccured=True
End Sub
Function TellMeRandom
If ErrorOccured Then Exit Function
'some code
Randomize
TellMeRandom =Int((100- 1+ 1) * Rnd + 1)*1
End Function
Function ResetError
ErrorOccured=False
End Function
Call Main 'Main will run because an error has not occured (ErrorOccured is False)
MsgBox TellMeRandom 'This will return a random number 1-100
Call MakeAnError 'This will set the Global ErrorOccured to true
MsgBox TellMeRandom 'This will be blank because the ErrorOccured prevented it from running
Call Main 'This will not run because the ErrorOccured prevented it from running
Call ResetError 'This will set the Global ErrorOccured to false
Call Main 'This will run because ErrorOccured is back to False
MsgBox TellMeRandom 'This will return a random number 1-100 because ErrorOccured is back to false
Just remember to Add If ErrorOccured then Exit Sub for a sub routine or If ErrorOccured then Exit Function for a function.
You could also raise an error such as: Err.Raise 507
This Error will exit your current script : "An exception occurred".

Try-Catch-End Try in VBScript doesn't seem to work

I'm trying the following code:
Try ' DOESN'T WORK
Throw 2 ' How do I throw an exception?
Catch ex
'What do I do here?
End Try
but I'm getting the error Statement expected in the catch clause.
Does anyone know how I can catch/throw exceptions in VBScript using try/catch? (I am not looking for solutions with On Error Do X.)
Handling Errors
A sort of an "older style" of error handling is available to us in VBScript, that does make use of On Error Resume Next. First we enable that (often at the top of a file; but you may use it in place of the first Err.Clear below for their combined effect), then before running our possibly-error-generating code, clear any errors that have already occurred, run the possibly-error-generating code, and then explicitly check for errors:
On Error Resume Next
' ...
' Other Code Here (that may have raised an Error)
' ...
Err.Clear ' Clear any possible Error that previous code raised
Set myObj = CreateObject("SomeKindOfClassThatDoesNotExist")
If Err.Number <> 0 Then
WScript.Echo "Error: " & Err.Number
WScript.Echo "Error (Hex): " & Hex(Err.Number)
WScript.Echo "Source: " & Err.Source
WScript.Echo "Description: " & Err.Description
Err.Clear ' Clear the Error
End If
On Error Goto 0 ' Don't resume on Error
WScript.Echo "This text will always print."
Above, we're just printing out the error if it occurred. If the error was fatal to the script, you could replace the second Err.clear with WScript.Quit(Err.Number).
Also note the On Error Goto 0 which turns off resuming execution at the next statement when an error occurs.
If you want to test behavior for when the Set succeeds, go ahead and comment that line out, or create an object that will succeed, such as vbscript.regexp.
The On Error directive only affects the current running scope (current Sub or Function) and does not affect calling or called scopes.
Raising Errors
If you want to check some sort of state and then raise an error to be handled by code that calls your function, you would use Err.Raise. Err.Raise takes up to five arguments, Number, Source, Description, HelpFile, and HelpContext. Using help files and contexts is beyond the scope of this text. Number is an error number you choose, Source is the name of your application/class/object/property that is raising the error, and Description is a short description of the error that occurred.
If MyValue <> 42 Then
Err.Raise(42, "HitchhikerMatrix", "There is no spoon!")
End If
You could then handle the raised error as discussed above.
Change Log
Edit #1:
Added an Err.Clear before the possibly error causing line to clear any previous errors that may have been ignored.
Edit #2:
Clarified.
Edit #3:
Added comments in code block. Clarified that there was expected to be more code between On Error Resume Next and Err.Clear. Fixed some grammar to be less awkward. Added info on Err.Raise. Formatting.
VBScript doesn't have Try/Catch. (VBScript language reference. If it had Try, it would be listed in the Statements section.)
On Error Resume Next is the only error handling in VBScript. Sorry. If you want try/catch, JScript is an option. It's supported everywhere that VBScript is and has the same capabilities.
Try Catch exists via workaround in VBScript:
http://web.archive.org/web/20140221063207/http://my.opera.com/Lee_Harvey/blog/2007/04/21/try-catch-finally-in-vbscript-sure
Class CFunc1
Private Sub Class_Initialize
WScript.Echo "Starting"
Dim i : i = 65535 ^ 65535
MsgBox "Should not see this"
End Sub
Private Sub CatchErr
If Err.Number = 0 Then Exit Sub
Select Case Err.Number
Case 6 WScript.Echo "Overflow handled!"
Case Else WScript.Echo "Unhandled error " & Err.Number & " occurred."
End Select
Err.Clear
End Sub
Private Sub Class_Terminate
CatchErr
WScript.Echo "Exiting"
End Sub
End Class
Dim Func1 : Set Func1 = New CFunc1 : Set Func1 = Nothing
Sometimes, especially when you work with VB, you can miss obvious solutions. Like I was doing last 2 days.
the code, which generates error needs to be moved to a separate function. And in the beginning of the function you write On Error Resume Next. This is how an error can be "swallowed", without swallowing any other errors. Dividing code into small separate functions also improves readability, refactoring & makes it easier to add some new functionality.

Can I get vbscript to show a friendly error message?

I'm installing a network printers using vbscript and I want to show a friendly error if the queue doesn't exist or the printer server is unavailable, can I do this with VBScript? My code is below.
Dim net
Set net = CreateObject("WScript.Network")
net.AddWindowsPrinterConnection "\\printsrv\HPLaser23"
net.SetDefaultPrinter "\\printsrv\HPLaser23"
Many thanks for the help
Steven
Add the line:
On Error Resume Next ' the script will "ignore" any errors
Before your code
and then do an:
if Err.Number <> 0 then
' report error in some way
end if
On Error GoTo 0 ' this will reset the error handling to normal
After your code
It's normally best to try to keep the number of lines of code between the On Error Resume Next and the On Error GoTo 0 to as few as possible, since it's seldom good to ignore errors.

Difference between Resume and Goto in error handling block

I understand that in the following example a Resume statement should be used instead of a Goto statement.
Sub Method()
On Error Goto ErrorHandler
...
CleanUp:
...
Exit Function
ErrorHandler:
Log error etc
Err.Clear 'Is this line actually necessary?'
Resume CleanUp 'SHOULD USE THIS'
Goto CleanUp 'SHOULD NOT USE THIS'
End Sub
My question is what difference is there in the execution of the two?
Both transfer execution to the Cleanup label. As far as I can remember, the only differences are
Using Goto doesn't clear the Err object (so Err.Clear is necessary if you use Goto) and it leaves your error handler disabled. If an error occurs after the Cleanup label, it won't be handled at ErrorHandler.
Using Resume clears the Err object and it switches your error handler back on (it is disabled while it is handling errors). If an error occurs after the Cleanup lable, it will be handled at ErroHandler
The VB6 manual entry for the Resume statement doesn't explain these differences.
This is BIG misunderstanding!
There is important DIFFERENCE between:
Err.Clear
GoTo CleanUp
and:
Resume CleanUp
NEVER use the first form, ALWAYS use Resume CleanUp (only).
Resume do the RESET of internal VB6 error state, so when OTHER error is occured, "On Error GoTo Label" will be applied. I you use "Err.Clear" then Err object is cleared, but INTERNAL error state is NOT cleared and when another error occure, it is considered as code WITHOUT any exception handler and throws outside of the function. You CAN NOT fix it by using "On Error GoTo Label2"
Consider this code:
Public Sub Test()
On Error GoTo L1
MsgBox 0 / (1 - 1)
Exit Sub
L1:
Err.Clear
L0:
On Error GoTo L2
MsgBox 0 / (1 - 1) 'ERROR!
Exit Sub
L2:
MsgBox Err
End Sub
If you run this, it will interrupt on "ERROR!" line.
If you replace "Err.Clear" with "Resume L0" then execution does not interrupts on "ERROR!" line and code jumps to "L2" label and provides "MsgBox Err"

Resources