When I started using VBScript for systems administration, I had no prior experience with Windows programming or scripting other than light use of batch files. Working without the benefit of experience or peers who used the technologies, I often spent hours finding the answer to a problem or used ridiculous workarounds. Particularly annoying was that in most cases, simple fixes existed; the hard part was finding the fix.
After I began helping other people with scripting problems, I quickly realized that they were having the same problems that I'd experienced, so I've compiled a list of some crucial tips that can make a significant difference to scripters. Eventually, you'll likely run into problems that these tips address, and you'll be prepared.
1. Get the WSH documentation and use it.
Although the Windows Script Host (WSH) documentation isn't perfect, it's very good. Get it and use it; it answers the vast majority of the questions people ask about WSH, VBScript, and JScript. You can find the Windows scripting documentation (and other scripting downloads) at http://msdn.microsoft.com/scripting. Another good Microsoft scripting resource is http://www.microsoft.com/technet/scriptcenter.
2. Use On Error sparingly.
The worst habit I see scripters indulging in is global error suppression. The line
On Error Resume Next
at the beginning of a script makes debugging the script impossible, lets the script run with no concern about hazardous side effects, and promotes sloppy scripting style.
Although you shouldn't globally suppress errors, error suppression is useful in a deployed script in places in which you want to silently handle failures. At a point in the code where an error is likely to occur, turn on error suppression, test for errors, then immediately turn off error suppression. To illustrate the proper use of error suppression, Listing 1, page 6, shows a piece of scripting code that tests for a missing server path. Note that the script ends if it encounters an error that you didn't anticipate and that it immediately turns error control back on after the special test cases by using the statement
on error goto 0Inappropriate error suppression can cause a multitude of problems. For example, if you delete local files without ensuring that an appropriate network resource containing their replacements is available, you could wind up with an unbootable system. In contrast, appropriate error control lets you make your scripts work better and possibly do more; for example, testing for unavailable shares can warn you of minor network problems before they become a big hassle for users.
3. Use extra double quotes with the Run method.
If you use WshShell's Run method with a path that contains spaces, you need to use extra double quotes. The shell splits the path into arguments. For example, suppose you want to run the executable at C:\my app\app.exe. If you use the code
Set WshShell = _ CreateObject("WScript.Shell") WshShell.Run _ "C:\My App\app.exe"
you'll get the error The system cannot find the file specified (unless you insist on using On Error Resume Next). Or, if you have a folder named \my in the root of the C drive, the folder will pop open and the script will continue on its way.
Why the error on the \my app file? The Run method behaves similarly to the command prompt—it doesn't assume it's receiving one argument, but rather it assumes it's receiving an executable or shell-launchable document named C:\my followed by an argument named app\app.exe.
I firmly believe that paths with spaces are evil incarnate, but important Windows system paths (e.g., paths that include documents and settings and program files) use spaces, so we have to understand how to work with them. To make Run work correctly with a path that has spaces, you must enclose the path in double quotes, then enclose each of those double quotes in another set of double quotes. The corrected line looks like this:
Some people find this trick easier: Type the string exactly as it should be passed (i.e., with a double quote at the beginning and at the end), replace every double quote with a pair of double quotes, and add a double quote at the beginning and end to make it a VBScript string. Alternatively, if you don't want to see all those double quotes in your code, you can use the VBScript Chr method to add the ASCII character for the double quote (i.e., ASCII character 34) before and after the path, as in the following code:
WshShell.Run Chr(34) & _ "C:\My App\app.exe" & Chr(34)
I advise taking two other steps to help prevent problems:
- Get a text editor that highlights VBScript. They range in price from free (SciTE) to inexpensive (Helios Software Solutions' TextPad) to costly (SAPIEN Technologies' PrimalScript). Script editors that highlight typically show literal strings in a different color, making syntactic errors in quoting instantly apparent.
- Test your Run argument to make sure that you've coded it properly. You can use WScript.Echo to see exactly what VBScript passes out; if the string you see in the result of WScript.Echo works in a command-prompt window, it will work as the first argument for the Run method.
Enclosing double quotes in double quotes is necessary with WshShell's Run method because the shell reads the string and tries to break it into arguments. But don't try to insert extra quotes when, for example, you pass a file or folder name to a Scripting.FileSystemObject method; methods that take a file or folder name as an argument expect the argument to be one complete name and won't break it up, so no quotes are necessary.
4. Use the CurrentDirectory property to change the directory.
People frequently ask how to change directories in WSH, even though the documentation explicitly covers this topic. The WshShell object has a CurrentDirectory property that you can set as well as read. If you want to set the directory to C:\program files, for example, you can use the following code:
Set WshShell = _ CreateObject("WScript.Shell") WshShell.CurrentDirectory = _ "C:\Program Files"
Note that I don't use extra quotes even though the directory's path has a space. The CurrentDirectory property takes only one argument and automatically assumes the entire thing is a literal path to one file; embedded spaces cause no problems. If you did add quotes, the statement would throw an error.
5. Arrays always start with 0.
When you create or use an array in VBScript, it will always have 0 as the lower bound for any dimension. The documentation is clear about that fact, but the presence of an LBound method in VBScript still causes confusion because it seems to imply that you can set the lower bound of an array dimension to an arbitrary integer as you can in Visual Basic (VB).
VBScript includes an Lbound method because COM method calls can return arrays with arbitrary lower bounds, so you might encounter an array with an arbitrary lower bound in VBScript, even though you can't create one. The following example shows how the lower bound of an array can be larger than the array's upper bound:
Option Explicit dim a: dim b() a = array("paper", "rock", _ "scissors") WScript.Echo LBound(a) ' returns 0 always redim b(-1) WScript.Echo "Lower bound of _ b() is greater than upper:", _ LBound(b), UBound(b)
6. MsgBox has a 1023-character limit.
When your script needs to write more than a few lines of text to output, don't use MsgBox—use WScript.Echo. MsgBox truncates strings at 1023 characters, whereas WScript.Echo can handle a lot of text—I've had it successfully echo 300,000 characters in one call. Better yet, WScript.Echo works appropriately for either WScript (displaying a GUI message) or CScript (echoing text to the console) as the hosting executable.
If you want to stop execution until the user acknowledges a message (one use of MsgBox in CScript-hosted scripts), use WshShell's PopUp method instead; if you don't specify a timeout value, the PopUp message will remain until closed manually, acting exactly like MsgBox.
If you do use MsgBox and have to deal with Unicode characters, be aware that MsgBox's character limit is truly based on the number of characters. Even if you use wide Unicode characters, MsgBox will show the first 1023.
7. Look for object documentation in the right places.
When people realize the power of COM automation, they frequently ask the question, "Where can I get a list of all the scriptable objects?" Unfortunately, no such list could possibly exist—developers can write scriptable COM objects in most modern Windows-based programming languages, and the average PC comes with hundreds of COM objects installed. The details about how to script each object depend on how the programmer designed the object, so your best bet is to look at the Help documentation from the object's manufacturer.
COM objects are designed to be self-documenting to a certain extent, so if an object has no formal documentation, you can still explore the object by looking at it with an object browser; in fact, that's my first method for looking at any scriptable component. The VBA Editor integrated into all the Microsoft Office applications has an object browser; you can also use standalone object browsers such as Microsoft's OLE/COM Object Viewer (http://www.microsoft.com/com/resources/oleview.asp) or the TLViewer applet (http://mysite.verizon.net/res1ur2j/tlviewer.htm). Most recent Windows programming environments, including Visual Studio .NET, include object browsers; see your product documentation for details.
Active Directory Service Interfaces (ADSI) and Windows Management Instrumentation (WMI) are two of the most frequently used object libraries. Unfortunately, unlike most COM objects, the fairly complex and important ADSI and WMI models aren't browsable with an object browser. For more information about these libraries, go to http://msdn.microsoft.com/library/en-us/netdir/adsi/active_directory_service_interfaces_adsi.asp and http://msdn.microsoft.com/library/enus/wmisdk/wmi/wmi_start_page.asp. More useful (in my opinion), though, is the popular Scriptomatic tool, available from http://www.microsoft.com/technet/scriptcenter/tools/wmimatic.asp. Scriptomatic searches your system and returns a list of all the WMI classes on it. When you select a class, Scriptomatic automatically writes a script to read all the properties in the class and displays the script for you to edit, copy, or run. (You'll note that Scriptomatic code uses global error suppression. One of the few safe places to do so is in code that only displays data, as Scriptomatic code does.)
8. You can't call Win32 APIs from a script.
A common question from people who are used to working with VB or Visual Basic for Applications (VBA) or who are trying to convert VB/VBA code to VBScript is how to perform an API call from a script. You can't directly call Win32 APIs from a script, as the VBScript documentation notes. I've heard various reasons for this limitation, but the original reason is probably that scripting is supposed to be comparatively safe and easy, and calling Win32 APIs introduces concerns about code safety.
You can use functionality exposed by the WMI and ADSI technologies to handle many classic administrative tasks that might otherwise require API access. If you think you need to use an API call, first ask an experienced scripter for advice. Make sure you explain your underlying need; don't just ask how to accomplish the equivalent of an API call. In my experience, most situations in which someone wants to make an API call can be addressed by well-tested components that are already built into Windows.
If you can't find a WMI or ADSI object to solve your problem, another strategy is to write a COM object in a programming language that can access APIs. You can then call the COM object from VBScript. However, be forewarned that writing a COM object is no simple task.
9. Find answers in Web forums.
Special problems will always crop up, and I've covered only what could be considered common problems. What can you do about the not-so-common challenges that I haven't addressed here?
Probably the simplest answer is to ask someone. You might not work with any hard-core Windows scripters, but a healthy online scripting community is full of people willing and able to answer scripting questions.
Windows & .NET Magazine's scripting forums at http://www.winnetmag.com/forums are an excellent entry point for finding other scripters. The relevant Microsoft newsgroups, all accessible from Microsoft's public news server at news://news.microsoft.com, are microsoft.public.windows.admin_scripting, microsoft.public.scripting.wsh, and microsoft.public.scripting.vbscript. You can also find dedicated groups dealing with specific tools, such as ADSI (microsoft.public.adsi.general) and WMI (microsoft.public.win32.programmer.wmi), as well as a wide range of other Microsoft technologies.