When Good Scripts Go Bad
Common VBScript problems—and how to solve them
July 25, 2005
You've spent hours writing a script that will automatically delete old inactive user accounts in Active Directory (AD). You're a novice at writing VBScript code, but manually deleting the old AD accounts every month was so tedious that you knew writing this script would be worth the effort. You wait in excited anticipation as you run it on your test network, but—oh no!—your script has screeched to a premature stop. What went wrong? The possibilities are endless. However, the Pareto Principle (aka the 80/20 rule) dictates that most (around 80 percent) of the scripting errors will come from relatively few (around 20 percent) of the possible causes.
Several scripting experts recently talked about common errors they find when reviewing VBScript code on the job and for Windows IT Pro, Windows Scripting Solutions, and other publications. According to these experts and various articles, the most common VBScript syntax errors (errors that occur when the code violates the VBScript language's grammatical rules) and runtime errors (errors that occur when a script attempts to perform an action that the system can't execute) occur when scriptwriters work with variables, error-handling statements, quotes, and special and reserved characters.
Vexing Variables
You've just spent several hours hammering out lines of code that capture information for each printer in your multisite network. Windows Management Instrumentation (WMI) monikers, VBScript constants, and the strComputer, colPrinters, and printer variables fill your head, and your eyes hurt from staring at the screen. Finally, as you handle colPrinter in the last For Each...Next statement, you know that you're on the home stretch. You quickly finish the script and test it, but your hope of going home soon vanishes as the script ends abruptly.
What happened? The answer lies in what you just read. The previous paragraph contains an error that happens too often in scripts: misspellings. In one instance, the collection variable's name included the letter s (colPrinters), but in the other instance, that letter was missing (colPrinter).
"I think the most common problem I've seen is failing to use the Option Explicit keyword, then having the script fail because it uses a variable much like the one it's supposed to be using," says Michael Otey, president of TECA, a software-development and consulting company, and technical director for Windows IT Pro. This problem was also high on the lists of other scripting experts, including Steve Seguis, CEO and chief software architect of SCRIPTMATION, an enterprise system automation firm. "One of the most common problems I've seen with VBScript code is misspelled variables," says Steve. "For example, using a variable called oUser in one place, then trying to read it as oUsr in another. This type of problem can be easily corrected by forcing declaration of variables using Option Explicit."
VBScript can help you avoid misspellings. To use a variable in VBScript code, you simply assign it a value. You don't have to explicitly define (i.e., declare) the variables you use. However, just like eating vegetables or getting daily exercise, it's in your best interest to do so, although it isn't required.
To explicitly declare variables, you simply place the Option Explicit statement at the beginning of your script to tell the scripting engine that you want to allow only those variables that you've explicitly declared with Dim, Private, Public, and ReDim statements. If the scripting engine encounters an undeclared variable, you'll receive the Variable is undefined error message (error number 500) followed by the undeclared variable's name . This one little line can save you dozens of hours of frustrating debugging. Be aware that if you happen to have more than one undefined variable, the error message identifies only the first one found.
In VBScript, quotes (" ") must surround all strings. Using the Option Explicit statement not only catches undeclared variables but also strings not enclosed in quotes. For example, the code
Option ExplicitDim strComputerstrComputer = PC1
results in a Variable is undefined error message because the scripting engine interprets PC1 to be a variable and not a string since it isn't in quotes. If you omit the Option Explicit statement in the previous code so that it reads
Dim strComputerstrComputer = PC1WScript.Echo strComputer
an error doesn't result. Instead, the scripting engine automatically assumes you have two variables: strComputer and PC1. Thus, instead of seeing the equal sign (=) as an assignment operator and assigning the value of PC1 to the strComputer variable, the scripting engine reads = as the equal to operator and makes the strComputer variable equal to the PC1 variable.
Other problems can occur with variables. Have you ever received a Type mismatch or Name redefined error message? Here's a look at what causes these and several other variable-related errors:
Expected end of statement (1025). When a variable name contains an embedded period, you'll receive the Expected end of statement error message. This error message can also result from other errors, such as accidentally using a comma instead of a period in an object name.
Type mismatch (13). When you try to perform an operation by using variables that contain data inappropriate for that operation, you'll receive the Type mismatch error message. For example, the code
Option ExplicitDim A, B, C, DA = 25B = 105C = "Combined weight: "D = A + CWScript.Echo C & D
produces the Type mismatch error message because it's trying to add a string to a number. In "Quick Scripting Tips," April 2002, InstantDoc ID 24232, Christa Anderson recommends that you "Name variables according to the kind of data they represent (e.g., give strings names that begin with the letter s and objects names that begin with o). This practice makes debugging code easier. Some expressions won't work or will work differently with data of an unexpected data type. And if you know what data type you intended to feed a script, unraveling type-mismatch errors is easier."
The VBScript Coding Conventions section in the Windows Script (WS) 5.6 Help file (script56.chm) has a list of recommended prefixes that you can use for variables. For example, if you add the recommended prefixes to the previous code so that it reads
Option ExplicitDim intA, intB, strC, intDintA = 25intB = 105strC = "Combined weight: "intD = intA + strCWScript.Echo strC & intD
you'll probably immediately notice the error and change strC to intB in the sixth line.
Name redefined (1041). When a variable name isn't unique, you'll receive the Name redefined error message. Perhaps the easiest way to avoid this error is to use meaningful variable names. For example, instead of using names such as intA, intB, strC, and intD, you might use the names intMinniesWeight, intBrutusWeight, strLabel, and intBothDogsWeight, respectively.
This list of error messages is in no way exhaustive. You'll probably encounter many more types of error messages. The Learning Path box lists several resources that discuss common scripting error messages.
Ignorance Isn't Bliss
In scripting, what you don't know can hurt you. To avoid a script stopping when it encounters an offline computer, a nonexistent file, or another runtime error, some scriptwriters automatically include VBScript's On Error Resume Next statement at the top of their scripts. Bill Stewart, systems and network administrator for French Mortuary, says that always including this statement can do more harm than good. "While this causes the script to hop past runtime errors, it also skips errors you might not expect—syntax errors and undefined variables being two commonly skipped errors."
Stewart and other scripting experts recommend that you use On Error Resume Next sparingly to trap errors only when needed. You should place the On Error Resume Next statement immediately before the code that might cause a runtime error. Then, after that code, you should use the On Error GoTo 0 statement to reenable the default VBScript error handler.
Quirky Quotes
Another common scripting error involves when and how to use quotes when those quotes are part of a string. Stewart says a classic example occurs when you're trying to run a command-line tool from VBScript code. If you need to specify a pathname when calling the command-line tool into action, you can run into problems. For example, suppose you want to open C:My Filesorg chart.bmp in Microsoft Paint. From a command-shell window, you'd run the command
mspaint.exe "C:My Filesorg chart.bmp"
If you try to run this command from VBScript code without enclosing C:My Filesorg chart.bmp in quotes, as the code at callout A in Listing 1 shows, the command fails because of the space in the filename, and Paint gives you the error message C:My.bmp was not found. However, if you simply add quotes around the pathname, as callout B shows, the command also fails because the scripting engine interprets the second quote (the quote that precedes the capital letter C:) as the end of the string. You'd receive the error message Expected end of statement.
You have several options for correcting this type of scripting problem. The easiest and safest approach is to escape—that is, flag—the character. VBScript lacks a true escape character (a character that's used to preserve the literal value of the next character that follows), so you use another quote. As callout C shows, escaping a quote with another quote can look bewildering. However, when you walk through each quote, the logic becomes apparent. The first quote in the code at callout C is easy to understand. The scripting engine interprets it as the beginning of a string. The scripting engine interprets the two-quote sequence before C: as follows: The first quote is escaping the second quote, so this sequence is interpreted as a literal quote. The scripting engine interprets the three-quote sequence as follows: The first quote is escaping the second quote, so the second quote is interpreted as a literal quote. The final quote in the three-quote sequence is read as the end of the string. For more information about escaping quotes, see the Windows Scripting Solutions article "Rem: Understanding Quotation Marks in VBScript," December 2002, InstantDoc ID 26975.
Another option is to use the numerical representation (aka the ANSI equivalent) for the embedded quote characters around the pathname. The ANSI equivalent is Chr(34). However, you can't simply replace the quotes with the ANSI equivalent. Instead, you must use the concatenation operator (&) to link together the ANSI equivalents and pathname. For this reason, using Chr(34) is complex and not recommended in most cases.
You have other options for handling quotes that are part of strings. For example, you can assign the quoted material to a variable. When the quoted material is a pathname, a common approach is to pass in the pathname from the command line when you launch the VBScript file.
You might find scripts that use single quotes (' ') instead of double quotes. This practice isn't recommended for several reasons. First, although a single quote is a valid character in VBScript, it's also the comment character (a character that indicates that the rest of the characters on that line are comments and not code). If the single quotes aren't embedded in a string that's enclosed in quotes, the scripting engine interprets the code that appears after the first single quote as a comment and the code fails. Second, in the WMI Query Language (WQL), the single quote is a reserved character (more about this topic shortly), which can introduce problems. Finally, although substituting single quotes for double quotes might work when you're displaying a message, this substitution doesn't work in every situation. For example, the code
Option ExplicitDim objShellSet objShell = _ CreateObject("WScript.Shell")WScript.Echo _ "The 'org chart.bmp' file is open."
runs fine. However, if you try to use single quotes in the code at callout C in Listing 1, the code fails and returns an error message stating that you have an invalid path.
One final word of advice about quotes: Write scripts in a text editor. In "Quick Scripting Tips," Christa Anderson offers this sage advice: "Don't write them in a word processor, then copy them to a text editor for saving. Scripts use a lot of quotation marks. If your word processor converts straight quotes to smart quotes, when you copy the script to the text editor, junk characters will replace the quotes and the script won't work."
Special and Reserved Characters
Special characters and reserved characters can also cause problems in VBScript code. Special characters are typically any characters that aren't alphabetic or numeric. For example, the asterisk (*), question mark (?), tab, left angle bracket () are all special characters.
Some special characters are nonprintable, such as the tab. Other special characters are reserved for specific purposes. Which characters are reserved depends on what your code is doing. For example, when using VBScript regular expressions, you have many reserved characters—called metacharacters. These metacharacters include
$ ( ) * + . [ ] ? / ^ { } |
When you use XML tags in your VBScript code, the reserved characters you should watch for include
& ' "
Most of the time, you can use reserved characters in your code, but you need to escape them with the appropriate escape character. For example, suppose you want to use a regular expression to find where dollar signs ($) appear in text. Because $ is the metacharacter that tells VBScript's RegExp object to match the end of an input string, you need to escape it with a backslash (), as this sample code shows:
Option ExplicitDim objRegExpSet objRegExp = _ CreateObject("VBScript.RegExp")objRegExp.Pattern = "$"
For XML tags, you can replace the reserved character with the appropriate escape sequence. For example, you can replace with the > sequence.
Reserved characters' meanings aren't universal, which can also cause problems. A good example appears in the Microsoft article "Hey, Scripting Guy! How Can I Connect to a Folder When There's an Apostrophe in the Folder Name?" at http://www.microsoft.com/technet/scriptcenter/resources/qanda/dec04/hey1217.mspx. The Scripting Guy writes, "Ah, the apostrophe (also known as the single quote mark). It seems like such a simple little character, and yet it has probably caused scriptwriters more grief than any other character on the keyboard. (Yes, even more than ~.) The problem is that the apostrophe is a 'reserved' character in both VBScript and WMI; that means these scripting technologies want to reserve the use of the apostrophe for themselves. That's fine, except when dealing with folder names like Ken Myer's folder or people names like O'Donnell or O'Hara. You want to use the apostrophe, because how else can you write the name Ken Myer's Folder? Meanwhile, VBScript and WMI want exclusive use of the apostrophe for other reasons. As you might expect, that leads to problems."
As the Scripting Guy mentions, one way to solve this problem is to escape the apostrophe. In a WQL query, for example, you do so by preceding the apostrophe with a backslash. The Scripting Guy provides other valuable advice, so the article is definitely worth reading.
One way you can find reserved characters is to use the Escape and Unescape functions. Although these functions aren't documented in the WS 5.6 Help file, they're included in the documentation on the Microsoft Developer Network (MSDN) Web site at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/script56/html/vtorifunctions.asp.
Microsoft designed the Escape and Unescape functions to make special characters in URLs viewable. However, you can use the Escape function for debugging VBScript files that might have special characters. The Escape function works by assessing a string one character at a time. When the function encounters a special character, it replaces the character with a percent sign (%) and the replaced character's hexadecimal number. For example, if you run the code
Option ExplicitDim objShellSet objShell = _ CreateObject("WScript.Shell")WScript.Echo Escape("That darn cat!")
you get the results that Figure 1 shows. These results show that the space (escape code %20) and exclamation point (%21) are special characters. For more information about the Escape and Unescape functions, see the Windows Scripting Solutions article "Special Characters in VBScript," October 2004, InstantDoc ID 43751.
Finally, be aware that VBScript has reserved words as well as characters. For a list of the reserved words, see the Microsoft article at http://support.microsoft.com/default.aspx/kb/216528.
A Good Place to Start
The next time your VBScript script stops unexpectedly, you might want to examine the variables, error-handling statements, quotes, and special and reserved characters in your code for possible clues to the problem. However, be aware that VBScript works with many technologies (e.g., WMI, Active Directory Service Interfaces—ADSI), and each technology has its own set of problems. Thus, your debugging journey might have to take a different path. To help you in that journey, see the Web-exclusive sidebar "Notable Problems with WMI and ADSI" at http://www.windowsitpro.com, InstantDoc ID 46969 and the Learning Path box for additional debugging tips and tools.
About the Author
You May Also Like