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:

Syntax errors

You may have spelled something incorrectly or made some other typographical or syntactical error. When this happens, usually the program won’t run at all.

Logical errors

Although syntactically correct, your program either doesn’t function as you expect or it generates an error message.

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.

Example 4-1. Client-side script with a syntax error
<HTML>
<HEAD>
<TITLE>Syntax Error</TITLE>
<SCRIPT LANGUAGE="vbscript">
Sub cmdButton1_OnClick
  Alert LCase("Hello World"
End Sub
</SCRIPT>
</HEAD>
<BODY BGCOLOR="white">
<INPUT TYPE="button" NAME="cmdButton1" VALUE="OK">
</BODY>
</HTML>
Error message generated by Example 4-1
Figure 4-1. Error message generated by Example 4-1

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.

Example 4-2. ASP page with a syntax error
<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>
ASP error information
Figure 4-2. ASP error information

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.

Example 4-3. Excerpt from an ASP page that generates an error
<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>
Error message generated by Example 4-3
Figure 4-3. Error message generated by Example 4-3

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.

Example 4-4. Division.htm, a web page for developing division skills
<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>
Example 4-5. GetQuotient.asp, the ASP page invoked by division.htm
<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>
Error display from Example 4-5
Figure 4-4. Error display from Example 4-5

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.

Tip

You can also use Visual Interdev or Visual Studio .NET to debug scripts.

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 Microsoft Script Debugger
Figure 4-5. The Microsoft 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.

Example 4-6. A badly behaving web page
<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.

Example 4-7. Runtime error caused by an invalid argument
<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 object
Dim 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 to True, 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 information
Dim 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 the ErrorNumber 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 &nbsp;
<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.

Generating a Type mismatch error at runtime
Figure 4-6. Generating a Type mismatch error at runtime

Table 4-1 lists a few of the most commonruntime errors.

Table 4-1. Some common VBScript error codes

Error number

Description

5

Invalid procedure call

6

Overflow

7

Out of memory

9

Subscript out of range

11

Division by zero

13

Type mismatch

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:

  1. 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 as LANGAUGE 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 valid LANGUAGE attribute, Internet Explorer used its default scripting language, JScript. But when confronted with this situation, it takes a while to recognize the obvious — that the LANGUAGE 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 the Option 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.

  2. 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 (like strMyString = intMyInt * dteMyDate) doesn’t make sense, but you’re none the wiser if your line of code reads a = b * c.

  3. 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.

  4. 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 Thenconditional statement, where x is the variable representing the divisor.



[1] A more complete version of the syntax of the Raise method is:

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.