VBscript Custom Errors  Hot PDF Print E-mail
Tag it:
Delicious
Furl it!
Digg
NewsVine
Reddit
YahooMyWeb
Technorati
Articles Reviews Visual Basic
Written by Phil Harrison   
Saturday, 14 October 2006

So far in this chapter we have been discussing how, when, and why to react to errors coming out of other people's code. However, VBScript also provides the ability to 'raise' errors yourself. Any VBScript code you write can at any time stop execution and generate an error. The key to this ability is the Raise method of the Err object.

 


Using Err.Raise

You might remember that the Err object is always available and is of global scope. That means you can refer to it any time you want without having to declare a variable for it. The following code demonstrates a call to Err.Raise.

Err.Raise vbObjectError + 10000, _
"MyScript.MyFunction", _
"The Age argument of MyFunction may not be greater than 150."

In this code example we are using the first three arguments of the Err.Raise method.

The first argument is the error number. You are free to any error number between 0 and 65535, but using zero is not recommended since, as we illustrated earlier in this chapter, many programmers will consider 0 as the absence of an error. In addition, Microsoft strongly recommends that you add the value of the vbObjectError constant to your error number. The detailed explanation of why this is necessary is out of the scope of this book, but to summarize, adding vbObjectError to your error number makes sure your error number will not clash with 'official' Microsoft error numbers.

The second argument is the error source. This is an optional parameter that many people omit or leave blank, but if you are raising an error from a specific script, page, and/or function, including some kind of description of where the error originated, is a good idea. A programmer who receives your error (maybe even yourself) will thank you someday for filling in the Source argument.


The third argument is the error description. It's a good idea to be as specific as possible in your description. Include any additional information you can think of that would be helpful to someone trying to diagnose and eradicate the cause of your error. Remember those useless ADO error descriptions we mentioned earlier in the chapter? Don't stick yourself or your fellow programmers with useless messages like Errors occurred or Undefined error.

The Err.Raise method accepts two additional arguments that are seldom used in a VBScript context: helpfile and helpcontext. If your VBScript project does have a companion Windows help file, by all means include the path to it in the helpfile argument. And if your help file has a specific section that explains the cause and solution for your custom error, then providing the 'context id' for that section is a great idea too.

Now that you know how to generate a custom error, the obvious follow-up questions are why and when to raise a custom error. First, though, let's talk about when it's not a good idea to raise custom errors. Errors are generally created for the benefit of other programmers. When something goes wrong (like a lost database connection), or when a programmer tries to do something illegal (like dividing by zero), an error is the most common way to inform a program or programmer of that fact.

However, your users generally do not appreciate errors. Ideally, the only time a user should see an error is when something unexpected occurred either in your script or in a script or component being used by your script. Furthermore, you want errors to make it only as far as the user's eyes when you did not have a specific way of dealing with the error. (For example, if your script received an error indicating a lost database connection, you could try to reconnect to the database rather than stopping your script and displaying the error to the user.)

It is useful to distinguish between an error and a 'problem message.' Certainly, there are times that you must communicate bad news to your user, or ask your user to fix some problem. You see this all the time on Web pages with forms. If you forget to fill in a certain field, the Web page will tell you about it so that you can fix the problem. This kind of 'problem message' is different from an error.

Remember the script at the beginning of this chapter that caused a divide by zero error? What if you had a script asking the user to enter a number which your script will divide into 100, like so:

Option Explicit
Dim x, y
x = InputBox("Please enter a number to divide into 100.")
y = 100 / x
MsgBox "100 divided by " & x & " is " & y & "."


What if the user enters 0? With this code as-is, run under the WSH, the user will see an unfriendly divide by zero error.

What if, instead, you tested the value that the user typed in before attempting the division, as in this script (PROBLEM_MESSAGE.VBS)?Option Explicit

Dim x, y
x = InputBox("Please enter a number to divide into 100.")
If x <> 0 Then
y = 100 / x
MsgBox "100 divided by " & x & " is " & y & "."
Else
MsgBox "Please enter a number other than zero."
End If


This time, the user sees a nice, friendly 'problem message' instead of an ugly, scary error. Users would always prefer to see a cordial message that informs them of what they can do to fix a problem rather than an error that leaves them feeling stupid and helpless.

The point of all this is to say that it is not a good idea to abuse Err.Raise by using it to inform your users of when they have done something wrong. Instead, as described in the next section, use Err.Raise to catch programming problems or to report problems in the environment. The following script ERR_MSG_UGLY.VBS) illustrates how not to use Err.Raise. The error description is sarcastic, but it reflects how your users will feel if you use Err.Raise in this way.

Option Explicit
Dim x, y
x = InputBox("Please enter a number to divide into 100.")
If x <> 0 Then
y = 100 / x
MsgBox "100 divided by " & x & " is " & y & "."
Else
Err.Raise vbObjectError + 15000, _
"ERR_MSG_UGLY.VBS", _
"Hey, stupid, you can't enter a zero! It will " & _
"cause a divide by zero error!"
End If

Now that we've discussed how to generate custom errors-and when not to-the question that is left is when should you generate a custom error? The answer has to do with assumptions. Whenever we write computer programs, we are forced to operate on a set of assumptions.

Different types of programs running in different type of environments have different sets of assumptions, but assumptions are always there.

Certain assumptions are foundational: you assume a certain operating system and computer configuration; you assume the ability to open files for reading and writing; you assume a certain database configuration; you assume that the Customer table is structured in a certain way; you assume that the Web server works in a certain way.

Other assumptions are more specific, and often take the form of rules; for example, you assume that the CustomerID argument passed into your LoadCustomer function is never less than or equal to zero and that the number will match an actual record in the Customer table.

The concept rules is closely related to the concept of assumptions. For example, there is a rule that you cannot divide by zero.

If you try to use VBScript (or just about any other programming language) to divide by zero,
VBScript is going to generate an error. To put it another way, VBScript 'assumes' that you are not going to try to divide by zero, and if you do, VBScript is going to generate an error.

And therein lies the key point when it comes to assumptions, rules, and errors: rather than simply trusting that all VBScript programmers will never attempt to divide by zero (which would no doubt cause troubles at the operating system and even CPU levels), the VBScript compiler/runtime engine double checks any division operation to make sure that the code is not attempting to divide by zero.

If the code is attempting it, VBScript generates an error: dividing by zero is not allowed.

A rule was broken; an assumption failed. Generate an error. That is the most basic answer to the questions of when and why to generate custom errors.


Before we look at a specific example, let's reconsider the previous section, which explained that it is not a good idea to use custom errors as a way to tell your users about things they have done wrong. Instead, we suggested using more friendly and helpful 'problem messages,' which are not the same as 'errors' per se. The distinction between this advice from the previous section about when not to use errors to report a problem and the advice in this section about when you do want to use errors to report a problem is one of 'audience.'Take another look at this ERR_MSG_UGLY.VBS script.

Option Explicit
Dim x, y
x = InputBox("Please enter a number to divide into 100.")
If x <> 0 Then
y = 100 / x
MsgBox "100 divided by " & x & " is " & y & "."
Else
Err.Raise vbObjectError + 15000, _
"ERR_MSG_UGLY.VBS", _
"Hey, stupid, you can't enter a zero! It will " & _
"cause a divide by zero error!"
End If

Besides the obviously rude wording of the error message, the reason this technique is undesirable is that the 'audience' of the error in this script is the user. Ideally, we don't want to use errors to communicate with our users. If we must report bad news to the user or tell him or her that he or she did something against the assumptions/rules of our script, we want to do this in a more friendly and helpful manner.

The reason we say that the 'audience' of this script is the user is that the Err.Raise method is being used in code that is directly interacting with a user-as opposed to lower level code, in a subprocedure or class method.

However, when the 'audience' of a block of code is other code (or to put it another way, the programmer himself or herself), then an error is appropriate-because we can assume that the programmer is equipped to do something about an error. Take a look at this reworking of the script (ERR_MSG_NICE.VBS).

Option Explicit
Dim x, y
x = InputBox("Please enter a number to divide into 100.")
On Error Resume Next
y = DivideNumbers(100, x)
If Err.Number = (vbObjectError + 15000) Then
On Error GoTo 0
MsgBox "Please enter a number other than zero."
Else
On Error GoTo 0
MsgBox "100 divided by " & x & " is " & y & "."
End If
Private Function DivideNumbers(dblNumber, dblDivideBy)
If dblDivideBy = 0 Then
Err.Raise vbObjectError + 15000, _
"ERR_MSG_NICE.DivideNumbers()", _
"Division by zero not allowed."
Else
DivideNumbers = dblNumber / dblDivideBy
End If
End Function


Notice that we have moved our division code into a function called DivideNumbers. Our script has two logical sections now. In terms of 'audience,' the top part of the script is at the 'outer edge' of the script, directly interacting with the user. The DivideNumbers function, on the other hand, is lower down in the script, interacting only with other code. Since the 'audience' of the lower level DivideNumbers function is other code, if something goes wrong, the best way for DivideNumbers to tell the other code about it is with a runtime error.

However, the 'outer edge' code that is interacting with the user anticipates that this error could come back from the DivideNumbers function and has a trap to catch it if it does. The whole purpose of the error trap is to turn the unfriendly runtime error into a friendly 'problem message' for the user. The 'outer edge' code and the lower level code are communicating the same problem in two different ways based on the 'audience.'

The other thing to take notice of with the DivideNumbers function is that it is proactively verifying assumptions. This is what programmers call defensive programming, which involves anticipating problems and failed assumptions as early as possible. It is as if the programmer of DivideNumbers said to himself or herself, 'The primary purpose of this function is to divide two numbers.

The division operation has an assumption/rule that division by zero is not allowed. Therefore, I am going to test this assumption, and if the test fails, I will raise an error.' This is one of the primary situations in which it is good to generate custom errors from your code.

Before moving on, let's consider another situation in which generating a runtime error would be appropriate. In the example above, the DivideNumbers function is doing a before-the-fact verification of an assumption. In other situations you must instead report failed assumptions after the fact.Consider a function called GetCustomerName, which takes a CustomerID argument and returns a customer name as a string.

The function uses the CustomerID value to query the Customer table in a database. If the customer record is found based on the CustomerID argument, the function returns the customer's name from the database record. What if, however, no Customer record was found with that CustomerID?

One way to handle this is for GetCustomerName to use Err.Raise to generate a custom No Customer record with specified CustomerID runtime error. Then the code that called GetCustomerName can respond to this error in whatever way is appropriate given the situation.

There are other examples you can think of along these lines. If a procedure called OpenFile can't find the specified file, it might generate a File Not Found error. If a procedure called WriteFile is denied access to the specified file based on security settings in the file system, it might generate a Permission denied error.

At this point, we have discussed how to generate custom errors using Err.Raise, when and why to generate them, and when not to. Other than the syntax rules for the Err.Raise method, none of the restrictions or suggestions we have offered are built into VBScript itself.


User reviews

There are no user reviews for this item.

Add new review




Powered by jReviews

Last Updated ( Monday, 01 September 2008 )
 
< Prev   Next >