Chapter 4. Error Handling and Debugging
Errors, bugs, and therefore, debugging, are a part of life for a programmer. As the saying goes, if you haven’t found any mistakes, then you aren’t trying hard enough.
Dealing with errors actually involves two very different processes: error handling and debugging. Error handling is a combination of coding and methodology that allows your program to anticipate user and other errors. It allows you to create a robust program. Error handling does not involve weeding out bugs and glitches in your source code, although some of the error-handling techniques covered in this chapter can be used to great advantage at the debugging stage. In general, error handling should be part of your overall program plan, so that when you have an error-free script, nothing is going to bring it to a screeching halt. With some sturdy error handling in place, your program should be able to keep running despite all the misuse that your users can — and certainly will — throw at it.
The following ASP page illustrates some simple error handling:
<HTML> <HEAD><TITLE>Error Checking</TITLE> <BODY> <SCRIPT LANGUAGE="VBSCRIPT" RUNAT="SERVER"> Dim n, x n = 10 x = Request.Form.Item("txtNumber") If x = 0 Or Not IsNumeric(x) Then Response.Write "x is an invalid entry" Else y = n / x Response.Write y End If </SCRIPT> </BODY> </HTML>
The error handling in this example is the best kind — it stops an
error before it can occur. Suppose you hadn’t used the conditional
If...Else
statement and had allowed
any value to be assigned to x
. Sooner or
later, some user will fail to enter a value or will enter a zero. In the
former case, it would generate a type mismatch error, while in the
latter, it would generate divide by zero error. So error handling, as
this code fragment illustrates, is as much about careful data validation
as it is about handling actual errors.
While preventing an error before it can occur is one approach to handling errors, the second is to handle the error after it occurs. For example, the following code fragment is a “real” error handler that we’ll examine later in this chapter, so don’t worry about the syntax at this stage. Like the previous code fragment, it aims at handling the “cannot divide by zero” runtime error—in this case, only after it occurs:
<HTML> <HEAD><TITLE>Error Checking</TITLE> <BODY> <SCRIPT LANGUAGE="VBSCRIPT" RUNAT="SERVER"> On Error Resume Next Dim n, x, y n = 10 x = Server.HTMLEncode(Request.Form.Item("txtNumber")) y = n / x If Err.Number <> 0 Then y = "Oops! " & Err.Description End If Response.Write y </SCRIPT> </BODY> </HTML>
As both of the previous examples show, the code itself is error-free and doesn’t contain any bugs, but without either the data validation code or the error handling code, this program would be brought to its knees the first time a user enters a zero in the text box. Error handling therefore is a way to prevent a potentially disastrous error from halting program execution. Instead, if an error does occur, your program can inform the user in a much more user-friendly manner, and you can still retain control over the program.
Debugging , on the other hand, involves finding errors and removing them from your program. There are many types of errors that you can unwittingly build into your scripts, and finding them provides hours of fun for all the family. Errors can result from:
Including language features or syntax that the scripting engine does not support within the script.
Failing to correctly implement the intent of the program or some particular algorithm. This occurs when code produces behavior or results other than those you intend, although it is syntactically correct and does not generate any errors.
Including components that contain bugs themselves. In this case, the problem lies with a particular component, rather than with your script, which “glues” the components together.
The single most important thing you need when debugging is patience: you have to think the problem through in a structured logical fashion in order to determine why you are experiencing a particular behavior. The one thing that you do have on your side is that programs will never do anything of their own free will (although they sometimes seem to). Let’s begin by looking more closely at this structured, logical approach to debugging your scripts.
Debugging
You’ve designed your solution and written the code. You start to load it into the browser with high hopes and excitement, only to be faced with an big ugly gray box telling you that the VBScript engine doesn’t like what you’ve done. So where do you start?
When confronted with a problem, you first need to know the type of error you’re looking for. Bugs come in two main flavors:
Bugs appear at different times, too:
- At compile time
If a compile-time error is encountered, an error message appears as the page is loading. This usually is the result of a syntax error.
- At runtime
The script loads OK, but the program runs with unexpected results or fails when executing a particular function or subroutine. This can be the result of a syntax error that goes undetected at compile time (such as an undefined variable) or of a logical error.
Let’s look at each type of bug individually. We’ll begin by looking at syntax errors—first at compile time and then at runtime—before looking at logical errors.
Syntax Errors
Ordinarily, objects containing script are compiled as they are loaded, and are then immediately executed. Errors can occur at either stage of the process. Although the distinction between compile-time and runtime errors is rapidly losing its importance, it is sometimes helpful to know that the entire script compiled successfully and that the error was encountered at a particular point in the script.
Syntax errors at compile time
Syntax errors at compile time are usually the easiest to trace and rectify. When the script loads, the host calls the scripting engine to compile the code. If the VBScript engine encounters a syntax error, it cannot compile the program and instead displays an error message.
For instance, an attempt to run the client-side script shown in Example 4-1 produces the error message shown in Figure 4-1. In this case, it’s very easy to identify the source of the error: in the call to the LCase function, the closing parenthesis is omitted.
When using ASP, diagnosing and fixing compile-time errors is a bit more difficult, since errors appear on the client browser, rather than in a dialog displayed on the server. For example, the simple ASP page shown in Example 4-2 displays the error message shown in Figure 4-2. This is a fairly standard ASP message display. The error code (which is expressed as a hexadecimal number in this case) appears to be meaningless. The line number causing the error, however, is correctly identified, and the description informs us of the exact cause of the error. So we can quickly see that we’ve omitted a closing quotation mark around the argument we passed to the ServerVariables property of the Request object.
<HTML> <HEAD> <TITLE>ASP Syntax Error</TITLE> </HEAD> <BODY> <SCRIPT LANGUAGE="VBScript" RUNAT="Server"> Function BrowserName() BrowserName = Request.ServerVariables("HTTP_USER-AGENT) End Function </SCRIPT> <H2><CENTER>Welcome to Our Web Page!</CENTER></H2> We are always happy to welcome surfers using <%= BrowserName %>. </BODY> </HTML>
Syntax errors at runtime
Very often, a syntax error in VBScript appears only at runtime. Although the VBScript engine can successfully compile the code, it cannot actually execute it. (Note, though, that you may not actually be able to tell the difference between compile-time and runtime behavior in a relatively short script, since these two behaviors occur one after the other.) Example 4-3 shows a part of an ASP page that, among other things, tries to determine whether an ISBN number is correct. But attempting to access this page generates a runtime error, which is shown in Figure 4-3.
<HTML> <HEAD> <TITLE>Verifying an ISBN</TITLE> </HEAD> <BODY> <SCRIPT LANGUAGE="VBScript" RUNAT="Server"> Function VerifyISBN(sISBN) Dim sCheckSumDigit, sCheckSum Dim iPos, iCtr, iCheckSum Dim lSum Dim sDigit iPos = 1 sCheckSumDigit = Right(Trim(sISBN), 1) ' Make sure checksum is a valid alphanumeric If Instr(1,"0123456789X", sCheckSumDigit) = 0 Then VerifyISBN = False Exit Function End If ' Calculate checksum For iCtr = 1 to Len(sISBN) - 1 sDigit = Mid(sISBN, iCtr, 1) If IsNumeric(sDigit) Then lSum = lSum + (11 - iPos) * CInt(sDigit) iPos = iPos + 1 End If Next iCheckSum = 11 - (lSum Mod 11) Select Case iCheckSum case 11 sCheckSum = "0" case 10 sCheckSum = "X" case else sCheckSum = CStr(iCheckSum) End Select ' Compare with last digit If sCheckSum = sCheckSumDigit Then VerifyISBN = True Else VerifyISBN = False End If End Function </SCRIPT> <H2><CENTER>Title Information</CENTER></H2> Title: <%=Server.HTMLEncode(Request.Form("txtTitle")) %> <P> ISBN: <% sISBN = Server.HTMLEncode(Request.Form("txtISBN")) If Not VerifyIBN(sISBN) Then Response.Write "The ISBN you've entered is incorrect." Else Response.Write sISBN End If %> </BODY> </HTML>
In this example, all code has successfully compiled, since
the server was able to begin returning output from the page. At
compile time, even though the VerifyIBN
(instead of VerifyISBN) function does not
exist, the line of code appears to the compiler to identify a
valid function, since it contains the correct syntax for a
function call:functioname
is followed
by argumentlist
. The VBScript engine
can therefore compile the code into a runtime program, and an
error is generated only when the engine tries to pass
argumentlist
to the nonexistent
function VerifyIBN .
Logical Errors
Logical errors are caused by code that is syntactically correct — that is to say, the code itself is legal — but the logic used for the task at hand is flawed in some way. There are two categories of logical errors. One category of errors produces the wrong program results; the other category of errors is more serious, and generates an error message that brings the program to a halt.
Logical errors that affect program results
This type of logical error can be quite hard to track down, because your program will execute from start to finish without failing, only to produce an incorrect result. There are an infinite number of reasons why this kind of problem can occur, but the cause can be as simple as adding two numbers together when you meant to subtract them. Because this is syntactically correct (how does the scripting engine know that you wanted “-” instead of “+”?), the script executes perfectly.
Logical errors that generate error messages
The fact that an error message is generated helps you pinpoint where an error has occurred. However, there are times when the syntax of the code that generates the error is not the problem.
For instance, Example 4-4 shows a web page that invokes an ASP page shown in Example 4-5. The ASP page in turn generates a runtime error, which is shown in Figure 4-4.
<HTML> <HEAD> <TITLE>A Test of Division</TITLE> </HEAD> <BODY> <FORM METHOD="POST" ACTION="GetQuotient.asp"> Enter Number: <INPUT TYPE="Text" NAME="txtNum1"> <P> Enter Divisor: <INPUT TYPE="Text" NAME="txtNum2"> <P> Enter Quotient: <INPUT TYPE="Text" NAME="txtQuotient"> <P> <INPUT TYPE="Submit"> </FORM> </HEAD> </HTML>
<HTML> <HEAD> <TITLE>Checking your division...</TITLE> </HEAD> <BODY> <SCRIPT LANGUAGE="VBScript" RUNAT="Server"> Dim nNum1, nNum2, nQuot Public Function IsCorrect( ) nNum1 = CDbl(Server.HTMLEncode(Request.Form("txtNum1"))) nNum2 = CDbl(Server.HTMLEncode(Request.Form("txtNum2"))) nQuot = CDbl(Server.HTMLEncode(Request.Form("txtQuotient"))) If (nNum1 / nNum2 = nQuot) Then IsCorrect = True Else nQuot = nNum1 / nNum2 End If End Function </SCRIPT> <% If IsCorrect( ) Then Response.Write "<H2><CENTER>Correct!</H2></CENTER>" Response.Write "Your answer is correct.<P>" Else Response.Write "<H2><CENTER>Incorrect!</H2></CENTER>" Response.Write "Your answer is wrong.<P>" End If %> <%=nNum1 %> divided by <%=nNum2 %> is <%=nQuot %> <P> Answer <A HREF="Division.htm">another division problem</A>. </BODY> </HTML>
The problem here is not one of syntax. Line 16 (the line
with the If
statement in the
IsCorrect function) is syntactically correct.
We won’t get this error every time that we display the HTML page
and it invokes the ASP page in Example 4-5. However, the
values of variables can change (after all, that’s why they’re
called variables), and here, the values of the variables in the
ASP page are defined by the values that the user enters into the
web page’s text boxes—in this case, by the user entering a 0 into
the txtNum2
text box in Example 4-4. It could be
said that this type of logical error produces a syntax error
because the following syntax:
If (nNum1 / 0 = nQuot) Then
entails a division by zero and is therefore illegal.
In this case, we should have checked the value of the divisor to make sure that it was nonzero before calling the function. But more generally, this scenario — in which the value of a variable is incorrect either all of the time or, more commonly, only under certain conditions — is the essence of the logical error.
The Microsoft Script Debugger
The Script Debugger has been designed to allow you to debug your scripts while they are running in the browser. You can trace the execution of your script and determine the value of variables during execution. The Script Debugger is freely downloadable from the Microsoft web site. (For details, see the Microsoft Scripting home page at http://msdn.microsoft.com/scripting/.) It arrives in a single self-extracting, self-installing archive file, so that you can be up and running with the debugger in minutes.
Launching the Script Debugger
The Script Debugger is not a standalone application in the sense that you cannot launch it on its own. Instead, the Script Debugger runs in the context of the browser or of WSH. When you are runningInternet Explorer, there are two ways to access the debugger:
- Select the Script Debugger option from the View menu
A submenu is displayed that allows you to open the debugger to cause a break at the next statement.
- Automatically when a script fails for any reason
This launches the debugger and displays the source code for the current page at the point where the script failed.
When you are running a WSH script, you can launch the debugger if an error
occurs by supplying the //D
switch, or you can run a script in the context of the debugger by
supplying the //X
switch. Figure 4-5 shows the
Script Debugger.
The Script Debugger interface
When you launch the Script Debugger, you’re faced with a number of different windows, each with its own special function:
- The Script window
This contains the code for the current HTML page just as if you’d selected the View Source option to launch Notepad. It is from the script window that you control how the debugger steps through program execution and that you watch the execution of the script. The script in this window is read-only.
- The Running Documents window
This displays a graphical view of the applications that support debugging and the documents that are active in them. To open a particular document, simply double-click its name in the Running Documents window.
- The Call Stack window
This displays the current hierarchy of calls made by the program. If the Call Stack window is hidden, you can display it by selecting the Call Stack option from the View menu. The Call Stack window allows you to trace the path that program execution has followed to reach the current routine (and, implicitly, that it must also follow in reverse to “back out” of these routines). For example, let’s say you have a client-side script attached to the OnClick event of a button called cmdButton1, which in turn calls a function named sMyFunction. When sMyfunction is executing, the call stack will be:
cmdButton1_OnClick sMyFunction
This allows you to see how program flow has reached the routine it’s currently in. It is all too easy when you have a breakpoint set in a particular function to lose track of how the script reached the function. A quick glance at the Call Stack window will tell you.
- The Command window
This is the most important part of the debugger. If you have experience in Visual Basic, you can now breath a sigh of relief! The Command window allows you to interrogate the script engine and find the value of variables, expressions, and built-in functions. If the Command window is not visible, you can open it by selecting the Command Window option from the View menu. To use the Command window, type a question mark (
?
) followed by the name of the variable or value you wish to see, then press Enter. For example:? sMyString "Hello World"
Tracing execution with the Script Debugger
The goal of tracing program execution is to discover, in a logical sequence, how your program is operating. If your program runs but generates an error message — or produces results that are inconsistent with what you expected — it is obviously not operating according to plan. You therefore need to follow the flow of your program as it executes, and at various stages, test the value of key variables and build up an overall “picture” of what is really happening inside of your program. This should enable you to discover where and why your program is being derailed.
To trace the execution of your script, you need a way to “break into” the script while it is running, and then to step through each line of code to determine what execution path is being followed or perhaps where the script is failing. The Script Debugger gives you two ways to halt execution and pass control over to the debugging environment:
- Break at Next Statement
The simplest option is to select the Break at Next Statement option from the Script Debugger’s Debug menu (or from the Script Debugger submenu of the Internet Explorer View menu). Then run your script in the normal way in the browser. As soon as the first line of scripting code is encountered by the browser, execution is suspended, and you have control over the script in the debugger. However, the part of the script you want to concentrate upon may be many lines of code further on from the first, in which case you will waste time stepping through to the portion that interests you.
- Set Breakpoint
You will mostly have a good idea where your code is either failing or not producing the desired results. In this case, you can set abreakpoint by placing your cursor on the line of code at which to halt execution, and then either pressing F9 or selecting Toggle Breakpoint from the Script Editor’s Debug menu. A line’s breakpoint set is highlighted in red. Run your script from the browser. When the code containing the breakpoint is reached, execution is suspended; you have control over the script in the Debugger.
When the code has been suspended, it must be executed manually from the debugger. There are three methods you can use for stepping through a script one line at a time. For each method, you can either select an option from the debugger’s Debug menu or press a keyboard combination. The options are:
- Step Into (F8)
This executes the next line of code. Using Step Into, you can follow every line of execution even if the next line to be executed is within another subroutine or function.
- Step Over (Shift-F8)
This executes the next line of code only within the current subroutine or function. If a call is made to another subroutine or function, the procedure executes in the background before control is passed back to you in the current subroutine.
- Step Out (Ctrl-Shift-F8)
This is required only if you have chosen Step Into and your script has called a function or subroutine. In some cases, you may realize that this is a lengthy procedure that has no consequence to your debugging. In this case, you can select Step Out to automatically execute the rest of the function and break again when control returns to the original subroutine or function.
Determining the value of a variable, expression, or function at runtime
One of the main functions of the Immediate window is to allow you to check the value of a particular variable while the script is running. The most frustrating part about debugging a script prior to the release of the Script Debugger was that you could see the results of your script only after it had run (or failed). Most debugging requires you to get inside the script and wander around while it’s in the middle of execution.
In the absence of a debugger, many programmers and content providers inserted calls to the Window.Alert method (for client-side scripting), to the Response.Write method (for server-side scripting), or to the MsgBox function (for WSH scripts and Outlook forms) to serve as breakpoints in various places in a script. The dialog would then display thevalues of particular variables or expressions selected by the programmer. Although this can still be the most efficient method of debugging when you have a very good idea of what’s going wrong with your code, it becomes very cumbersome to continually move these calls and to change the information the dialogs display when you don’t really have a very good idea of where or why your script is failing.
In contrast, using the Command window to display the
value of any non-object variable is easy. Simply type a question
mark (?) followed by a space and the variable name, then press
Enter. The Script Debugger will then evaluate the variable and
display its value in the Immediate window. Note, though, that if
your script requires variable declaration because you’ve included
the Option
Explicit
statement, you must have
declared the variable and it must be in scope for the debugger to
successfully retrieve its value; otherwise, an error dialog is
displayed. The debugger cannot evaluate the result of user-defined
functions; it can evaluate only intrinsic
functions (functions that are a built-in part of the
scripting language).
But you aren’t limited to using the Command window to view the values of variables; you can also use it to inspect the values of expressions, of VBScript intrinsicfunctions, and of the properties and methods of particular objects. To see how this works, and also to get some experience using the Script Debugger, let’s try out the web page and client-side script in Example 4-6. Basically, the user should be able to enter a number and, if it is actually between zero and two, be shown the element of the array at that ordinal position. Somewhere in this code is a sneaky little bug causing problems. The script always tells the user that the number entered into the text box is too large, which indicates that it is greater than the upper boundary of the array. But this isn’t the case; the user can enter the numbers 0 or 2 and still be told that the number is too large.
<HTML> <HEAD><TITLE>Testing the Script Debugger</TITLE></HEAD> <BODY> <SCRIPT LANGUAGE="VBSCRIPT"> Dim sTest sTest = Array("Hello World", "Some Data", "AnyData") Sub cmdButton1_OnClick Dim iTest iTest = Document.frmForm1.txtText1.Value Alert sGetValue(iTest) End Sub Function sGetValue(iVal) If iVal > UBound(sTest) Then sGetValue = "Number too big" Elseif iVal < 0 Then sGetValue = "Number too small" Else sGetValue = sTest(iVal) End If End Function </SCRIPT> <FORM NAME="frmForm1"> Input a Number (0-2): <INPUT TYPE="text" NAME="txtText1"> <P> <INPUT TYPE="button" NAME="cmdButton1" VALUE="OK"> </FORM> </BODY> </HTML>
To debug the script in Example 4-6, you can
place a breakpoint on the first line of the
sGetValue function, since this is probably
where the problem lies. Then run the script and enter the number 2
into the text box txtText1. When execution is suspended, you can
investigate the values of the program’s variables. As you can see,
the call to the sGetValue function has a
single argument, iTest
, which is passed
to the function as the iVal
parameter.
So our first step is to determine the value of
iVal
at runtime by entering the
following into the Command window:
? iVal
Press Enter, and the debugger displays the result:
2
Next, find out what the script thinks the upper boundary of the array is by entering the following in the immediate window and pressingEnter:
? UBound(sTest)
Note that here you’re not simply asking for the value of a
variable; you’re actually asking the debugger to evaluate the
UBound function on the
sTest
array and return the result,
which is:
2
So iVal
is not greater than
UBound(sTest)
. Next, go back to
the script window and press F8 to follow the flow of program
control. Execution is suspended on the following line, where the
string “Number too big” is assigned to the variable
sGetValue
. That indicates that the
scripting engine has evaluated the expression incorrectly and has
decided that iVal
is greater then
UBound(sTest)
. So go back to
the Command window, and this time try to evaluate the complete
expression:
? iVal > UBound(sTest)
As you might expect from the appearance of the “Number too
big” dialog when the script is run, the result of the expression
is True
, even though the
expression that is evaluated (once we replace the variable and
expression with their values) is 2 >
2
, which is clearly False
. Given this apparent incongruity,
it seems likely that our problem may be centered in the data types
used in the comparison. So try the following:
? TypeName(UBound(sTest))
Here, you’re asking the debugger to evaluate the
UBound function on the
sTest
array, and, by calling the
TypeName function, to indicate the data type
of the value returned by the UBound function.
The result is:
Long
Now find out what data type iVal
is:
? TypeName(iVal):
The debugger returns:
String
Aha! The Script Debugger shows that, in reality, you’re performing the following comparison:
If "2" > 2 Then
which of course is nonsense! Remember that
iVal
is the name within the
sGetValue function of the
iTest
variable in the button’s OnClick
event procedure. And iTest
in turn
represents the value retrieved from the textbox, which of course
must be string data, as typing the following into the Command
window establishes:
? TypeName(iTest) String
Try this in the debugger:
? CLng(iVal) > UBound(sTest)
Success! The Command window shows:
False
You can see from this debugging exercise that the Command window is a powerful tool allowing you to perform function calls, evaluate complete expressions, and try out different ways of writing your code.
Changing variable values at runtime
Another use for the Command window is to assign a
new value to a variable. For example, if you open the web page and
client-side script shown in Example 4-7 and click the
button, you’ll find that an error halts execution on line 10 with
the message “Invalid procedure call or argument”. If you use the
Command window to determine the value of
myNum
, which specifies the starting
position of the InStr search, you’ll find
that it was erroneously set to -1, an invalid value that generated
the runtime error.
<HTML> <HEAD><TITLE>Logical Error</TITLE> <SCRIPT LANGUAGE="vbscript"> Sub cmdButton1_OnClick Dim myNum Dim sPhrase sPhrase = "This is some error" myNum = GetaNumber(CInt(Document.frmForm1.txtText1.Value)) If Instr(myNum, sPhrase, "is") > 0 Then Alert "Found it!" End If End Sub Function GetaNumber(iNum) iNum = iNum - 1 GetaNumber = iNum End Function </SCRIPT> </HEAD> <BODY BGCOLOR="white"> <FORM NAME="frmForm1"> <INPUT TYPE="hidden" NAME="txtText1" VALUE=0> <INPUT TYPE="button" NAME="cmdButton1" VALUE="Click Me"> <FORM> </BODY> </HTML>
You can, however, correct the error and continue executing
the script. Just place a breakpoint on the offending line and
click on the button when the browser displays it so that the
script executes. When program execution halts, you can check
the value
of
myNum
:
? myNum -1
Error Handling
Error handling does not involve finding errors in your scripts. Instead, use error-handling techniques to allow your program to continue executing even though a potentially fatal error has occurred. Ordinarily, all runtime errors that are generated by the VBScript engine are fatal, since execution of the current script is halted when the error occurs. Error handling allows you to inform the user of the problem and either halt execution of the program or, if it is prudent, continue executing the program.
The On Error Resume Next Statement
There are two main elements to error handling in VBScript. The
first is the On
Error
statement,
which informs the VBScript engine of your intention to handle errors
yourself, rather than to allow the VBScript engine to display a
typically uninformative error message and halt the program. This is
done by inserting a statement like the following at the start of a
procedure:
On Error Resume Next
This tells the VBScript engine that, should an error occur, you want it to continue executing the program starting with the line of code that directly follows the line in which the error occurred. For example, in the simple WSH script:
On Error Resume Next x = 10 y = 0 z = x / y Alert z
a “Cannot divide by Zero” error is generated on the fourth
line of code because the value of y
is 0.
But because you’ve placed the On
Error
statement in line 1,
program execution continues with line 5. The problem with this is
that when an error is generated, the user is unaware of it; the only
indication that an error has occurred is the blank Alert box (from
line 5) that’s displayed for the user.
Tip
A particular On
Error
statement is valid until another
On
Error
statement in the line of execution
is encountered, or an On Error Goto
0
statement (which turns off error handling) is
executed. This means that if Function A contains an On Error
statement, and Function A calls
Function B, but Function B does not contain an On
Error
statement, the error handling from
Function A is still valid. Therefore, if an error occurs in
Function B, it is the On
Error
statement in Function A
that handles the error; in other words, when an error is
encountered in Function B, program flow will immediately jump to
the line of code that followed the call to Function B in Function
A. When Function A completes execution, the On
Error
statement it contains also goes
out of scope. This means that, if the routine that called Function
A did not include an On
Error
statement, no error
handling is in place.
This is where the second element of VBScript’s error handling
comes in. VBScript includes an error object, named Err, which, when
used in conjunction with On
Error
Resume
Next
, adds much more functionality to
error handling, allowing you to build robust programs and relatively
sophisticated error-handling routines.
The Err Object
The Err object is part of the VBScript language and
contains information about the last error to occur. By checking the
properties of the Err object after a particular piece of code has
executed, you can determine whether an error has occurred and, if
so, which one. You can then decide what to do about the error — you
can, for instance, continue execution regardless of the error, or
you can halt execution of the program. The main point is that error
handling using On Error
and the
Err object puts you in control of errors, rather than allowing an
error to take control of the program (and bring it to a grinding
halt). To see how the Err object works and how you can use it within
an error-handling regimen within your program, let’s begin by taking
a look at its properties and methods.
Err object properties
Like all object properties, the properties of the Err object can be accessed by using the name of the object, Err, the dot (or period) delimiter, and the property name. The Err object supports the following properties:
- Number
The Number property is a Long value that contains an error code value between -2,147,483,648 and 2,147,483,647. (The possibility of a negative error code value seems incongruous but results from the fact that error codes are unsigned long integers, a data type not supported by VBScript.) VBScript itself provides error code values that range from 0 to 65,535. COM components, however, often provide values outside of this range. If the value of
Err.Number
is 0, no error has occurred. A line of code like the following, then, can be used to determine if an error has occurred:If Err.Number <> 0 Then
Although the properties of the Err object provide information on the last error to occur in a script, they do not do so permanently. All the Err object properties, including the Number property, are set either to zero or to zero-length strings after an End Sub, End Function, Exit Sub, or Exit Function statement. In addition, you can explicitly reset
Err.Number
to zero after an error by calling the Err object’sClear method. The WSH script in Example 4-8 illustrates the importance of resetting theErr object after an error occurs.Example 4-8. Failing to reset the Err objectDim x, y ,z On Error Resume Next x = 10 y = 0 z = x / y If Err.Number <> 0 Then MsgBox "There's been an error #1" Else MsgBox z End IF z = x * y If Err.Number <> 0 Then MsgBox "There's been an error #2" Else MsgBox z End If End Sub
The division by zero on the fifth line of the script in Example 4-8 generates an error. Therefore, the conditional statement on line 6 evaluates to
True
and an error dialog is displayed. Program flow then continues at line 12. Line 12 is a perfectly valid assignment statement that always executes without error, but the Err.Number property still contains the error number from the previous error in line 5. As a result, the conditional statement on line 13 evaluates toTrue
, and a second error dialog is displayed. Despite the two error messages, there’s only been a single error in the script.- Description
The Description property contains a string that describes the last error that occurred. You can use the Description property to build your own message box alerting the user to an error, as the WSH script in Example 4-9 shows.
Example 4-9. Using the Description property to display error informationDim x, y ,z On Error Resume Next x = 10 y = 0 z = x / y If Err.Number <> 0 Then MsgBox "Error number " & Err.Number & ", " & _ Err.Description & ", has occurred" Err.Clear Else MsgBox z End If z = x * y If Err.Number <> 0 Then MsgBox "Error No:" & Err.Number & " - " & _ Err.Description & " has occurred" Err.Clear Else Alert z End If
- Source
The Source property contains a string that indicates the class name of the object or application that generated the error. You can use the Source property to provide users with additional information about an error—in particular, about where an error occurred.
The value of the Source property for all errors generated within scripted code is simply “Microsoft VBScript runtime error.” This is true of all VBScript scripts, whether they’re written for Active Server Pages, Windows Script Host, Internet Explorer, or Outlook forms. Obviously, this makes the Source property less than useful in many cases. However, you can assign a value to the Source property in your own error-handling routines to indicate the name of the function or procedure in which an error occurred. In addition, the primary use of the Source property is to signal an error that is generated by some other object, like an OLE automation server (such as Microsoft Excel or Microsoft Word).
Err object methods
The two methods of the Err object allow you to raise or clear an error, while simultaneously changing the values of one or more Err object properties. The two methods are:
- Raise
The Err.Raise method allows you to generate a runtime error. Its syntax is:[1]
Err.Raise(
ErrorNumber
)where
ErrorNumber
is the numeric code for the error you’d like to generate. At first glance, generating an error within your script may seem like a very odd thing to want to do! However, there are times, particularly when you are creating large, complex scripts, that you need to test the effect a particular error will have on your script. The easiest way to do this is to generate the error by using the Err.Raise method and providing the error code to theErrorNumber
parameter, then sit back and note how your error-handling routine copes with the error, what the consequences of the error are, and what side effects the error has, if any. The client-side script in Example 4-10, for instance, allows the user to enter a number into a text box, which is passed as the error code value to the Err.Raise method. If the value of the error code is non-zero, an Alert box opens that displays the error code and its corresponding description. Figure 4-6, for instance, shows the Alert box that is displayed when the user enters a value of 13 into the text box.Example 4-10. Calling the Err.Raise method<HTML> <HEAD> <TITLE>Using the Err Object</TITLE> <SCRIPT LANGUAGE="vbscript"> Sub cmdButton1_OnClick On Error Resume Next errN = Document.frm1.errcode.value Err.Raise(errN) If Err.Number <> 0 Then Alert "Error No:" & Err.Number & " - " & Err.Description Err.Number = 0 End If End Sub </SCRIPT> </HEAD> <BODY BGCOLOR="white"> <CENTER> <H2>Generating an Error</H2> <P> <FORM NAME="frm1"> Enter an Error Code <INPUT TYPE="text" NAME="errcode"> <INPUT TYPE="button" NAME="cmdButton1" VALUE="Generate Error"> </CENTER> </BODY> </HTML>
Tip
An Error Code Generator (ERRCODES1.HTML, ERRCODES1.ASP, and ERRCODES1.VBS), which allows you to generate a complete list of current VBScript error codes, can be found on the O’Reilly Visual Basic web site at http://vb.oreilly.com.
Table 4-1 lists a few of the most commonruntime errors.
- Clear
The Clear method clears the information that the Err object is storing about the previous error; it takes no parameters. It sets the values of
Err.Number
to 0 and the Err object’s Source and Description properties to a null string.
Common Problem Areas and How to Avoid Them
There is much to be said for the old maxim, “The best way to learn is by making mistakes.” Once you have made a mistake, understood what you did wrong, and rectified the error, you will — in general — have a much better understanding of the concepts involved and of what is needed to build a successful application. But to save you from having to experience this painful process of trial and error in its entirety, we’d like to share with you some of the most common errors that ourselves and other programmers we’ve worked with have made over the years. These types of errors are actually not unique to VBScript, nor in fact to VB, but to programming in general. In approximate order of frequency, they are:
Syntax errors generated by typing errors. This is a tough one. Typing errors — the misspelled function call or variable name — are always going to creep into code somewhere. They can be difficult to detect, particularly because they are typing errors; we frequently train our eyes to see what should be there, rather than what is there. When the effect of the typing error is subtle, it becomes even more difficult to detect. For instance, in a client-side script, we had spelled
LANGUAGE
asLANGAUGE
in coding the<SCRIPT>
tag. The result was that Internet Explorer immediately began reporting JavaScript syntax errors. This isn’t surprising, given that in the absence of a validLANGUAGE
attribute, Internet Explorer used its default scripting language, JScript. But when confronted with this situation, it takes a while to recognize the obvious — that theLANGUAGE
attribute for some reason is improperly defined; instead, it seems that Internet Explorer and VBScript are somehow mysteriously “broken.” One way to reduce the time spent scratching your head is to build code in small executable stages, testing them as you go. Another good tip is to use individual small sample scripts if you are using a function or set of functions for the first time and aren’t sure how they’ll work. That allows you to concentrate on just the new functions rather than on the rest of the script as well. And perhaps the most effective technique for reducing troublesome misspelling of variables is to include theOption Explicit
directive under the first<SCRIPT>
tag in ASP, Internet Explorer, and WSH/XML scripts, and at the top of the page of WSH and Outlook form scripts. This way, any undefined variable — which includes misspelled variables — is caught at runtime.Type mismatches by everyone’s favorite data type, the variant. Type mismatches occur when the VBScript engine is expecting data of one variant type — like a string — but is actually passed another data type — like an integer.) T ype mismatch errors are fairly uncommon in VBScript, since most of the time the variant data type itself takes care of converting data from one type to another. That tends, though, to make type mismatch errors all the more frustrating. For instance, in Example 4-5, if we hadn’t used the statements:
nNum1 = CDbl(Server.HTMLEncode(Request.Form("txtNum1"))) nNum2 = CDbl(Server.HTMLEncode(Request.Form("txtNum2"))) nQuot = CDbl(Server.HTMLEncode(Request.Form("txtQuotient")))
to convert the form data submitted by the user to numeric data, our application would not have functioned as expected. The best way to reduce or eliminate type mismatch errors is to adhere as closely as possible to a uniform set of VBScript coding conventions. (For a review of coding conventions and their significance, see Chapter 2.) For instance, when you know that a variable is going to hold a string, use a variable name like
strMyVar
to indicate its type, etc. Code becomes easier to use if you can tell instantly that some operation (likestrMyString = intMyInt * dteMyDate
) doesn’t make sense, but you’re none the wiser if your line of code readsa = b
* c
.Subscript Out Of Range is an error that occurs frequently when usingarrays. It actually doesn’t take much to eliminate this error for good. All you have to do is check the variable value you’re about to use to access the array element against the value of the UBound function, which lets you know exactly what the maximum subscript of an array is.
The next most common error is division by zero. If you try to divide any number by zero, you’ll kill your script stone dead. While it’s very easy to generate a division by zero error in a script, it’s also not at all difficult to prevent it. A division by zero error is easy to diagnose: whenever a variable has a value of zero, it’s likely to cause a problem. So all your script has to do is check its value and, if it turns out to be zero, not perform the division. There’s no rocket science here! Simply use an
If x = 0
Then
conditional statement, wherex
is the variable representing the divisor.
Get VBScript in a Nutshell, 2nd Edition now with the O’Reilly learning platform.
O’Reilly members experience books, live events, courses curated by job role, and more from O’Reilly and nearly 200 top publishers.