WSH fundamentals

For more than a year, I've used this column to demonstrate tasks that you can use Windows Scripting Host (WSH) 1.0 to automate. Although my demonstrations are informative for some readers, I've been a little remiss in providing WSH fundamentals. Because readers email me numerous questions about WSH and Visual Basic Scripting Edition (VBScript), I think that many systems administrators are looking for basic scripting information.

I'm going to take a small step backward for the next couple of columns to discuss some WSH scripting fundamentals. I'll include a few tips and tricks to keep scripting veterans interested, and I'll provide information about the forthcoming WSH 2.0. (You will probably be able to obtain a beta copy of WSH 2.0 from the Microsoft Windows Script Technologies Web site—http://msdn.microsoft.com/ scripting—by the time you read this column.)

Why the Confusion?
WSH can be confusing for several reasons. I attribute most readers' confusion about WSH scripting to a lack of high-quality resources. WSH is still new, and I expect better resources to appear as the technology matures. In the meantime, for more information about WSH scripting, refer to the publications and Web sites that the sidebar "Win32 Scripting Resources," page 191, lists.

Another factor that makes WSH appear confusing is the number of seemingly disparate technologies that you must leverage to make a WSH script perform certain functions. For example, to write WSH logon scripts that test for a user's group membership, you must know something about WSH, VBScript or JScript, and Active Directory Service Interfaces (ADSI). You need to know less information about fewer technologies to perform the same task using a specialized scripting language such as KiXtart or a batch file and a standalone utility such as the Microsoft Windows NT Server 4.0 Resource Kit's Ifmember.

Not only does using WSH often require knowledge of multiple scripting technologies but the technologies' overlap complicates decisions about which technology to use to complete a certain task. For example, both ADSI and Windows Management Instrumentation (WMI)—Microsoft's implementation of Web-Based Enterprise Management (WBEM)—provide access to NT services via automation interfaces. If you want to use WSH to start or stop services on an NT server, you must figure out whether to use WMI or ADSI.

When you consider the lack of WSH documentation and the variety of scripting technologies that WSH can leverage, Microsoft's new Windows scripting paradigm might seem complex. However, if you put a little effort into learning WSH, you can use the technology to accomplish far more than you can accomplish with any other scripting technology. Trust me—the knowledge you'll gain will be worth your effort.

I'm going to use the VBScript engine for my series of columns about WSH scripting fundamentals. WSH scripts are increasingly prevalent on the Internet, and most of these scripts are VBScript routines. In addition, the example WSH scripts that come with several Microsoft products and technologies (e.g., the ADSI software development kit—SDK, Internet Information Server—IIS, Site Server, the WBEM SDK) are VBScript routines. You can download VBScript 5.0 from Microsoft's VBScript Web site (http://msdn.microsoft.com/ scripting/vbscript/default.htm). In this first installment of my scripting fundamentals series, I explain how to create a VBScript routine, how to use multiple VBScript routines to perform one function, and how to organize VBScript routines.

The .vbs File
VBScript WSH scripts are simply ASCII text files with the extension .vbs. You can create and edit VBScript files in any editor that saves as text-only (e.g., Visual Notepad, Visual InterDev). Of course, the file's content must be valid VBScript code for the script to run. Scripts can range in size from one line to thousands of lines of code. The only limit on the size of a VBScript routine is the limit that the OS imposes. On NT systems, VBScript routines can be as large as the file system's maximum file size. (To verify that WSH can handle large scripts, I created a script that is more than 25,000 lines long; the script ran perfectly.)

Although WSH doesn't limit file size, WSH scripts can't currently span more than one file. WSH 1.0 doesn't allow include files, files that contain code that running scripts can leverage. You can work around this limitation in several ways. As you might expect, you can use the WSH Shell object's Run method to call external scripts just as you would call any other external command. The caveat to this approach is that the two scripts don't share a namespace, so the scripts can't access the same variables and subroutines.

VBScript 5.0 introduces the Execute statement, which provides a better workaround for VBScript's exclusion of include files. Execute takes one argument in the form of a string and executes that argument. Because Execute executes the string within the script, the string must contain valid VBScript code. One cool way to use Execute is to define subroutines in external files, slurp those files into a running script, and call them. Let's look at an example.

Listing 1 contains the include.vbs script, and Listing 2 contains the main.vbs script. Include.vbs defines a subroutine named Usage. The Usage subroutine accepts one argument in the form of a string and uses WScript's Echo method to echo that argument to the screen.

Main.vbs pulls the Usage subroutine from include.vbs and subsequently calls the subroutine. First, main.vbs copies the Usage subroutine into a variable named strCode. The script uses the OpenTextFile method of VBScript's FileSystemObject object to create a reference to a TextStream object that points to the file include.vbs. Main.vbs then uses the TextStream object's ReadAll method to import all the code in include.vbs into strCode. (For another example of this VBScript functionality, see "Professor Fun," May 1999.)

When strCode contains the code from include.vbs, main.vbs passes strCode to the Execute statement. The Execute statement makes the Usage subroutine part of main.vbs. The script's last line calls the Usage subroutine and passes the subroutine a string to echo to the screen. Screen 1 shows the result of running main.vbs.

Although main.vbs's call to the Usage subroutine appears to occur across file boundaries, it doesn't. The Usage subroutine becomes part of the main.vbs namespace when main.vbs passes the code string to the Execute statement. Therefore, you never want to pull a subroutine into a script if the subroutine has the same name as one of the script's subroutines, unless you want to override the script's subroutine.

WSH 2.0 will provide support for include files and will let you create a library of common functions or subroutines that other scripts can easily access, regardless of the scripts' language. WSH 2.0 will also let you call JScript functions from within VBScript routines, and vice versa.

Structuring Code
Part of the beauty of scripting is that scripting languages include very few rules about the format, structure, and flow of scripts. This flexibility can confuse administrators who are new to scripting.

You can find as many different methods for structuring a script as you find developers. However, although scripting languages don't come with many rules, using one approach to write all your scripts makes sense. Whether you use a template for your scripts or just stick to a methodology, keeping your scripts consistent can speed up the script-development process, make scripts easier to use, and simplify script maintenance.

I divide my WSH script files into three sections: declarations and initializations, the script's body, and function and subroutine definitions. I include script directives, variable declarations, constants, and variable initializations in the declaration and initialization section, which I place at the beginning of each script. I place a script's main statements and the logic that controls the script's behavior in the script's body section. I instantiate and use objects, invoke methods, set properties, and call subroutines and functions within this middle section of my scripts. Finally, I use the functions and subroutines section at the end of each script to compartmentalize the code. I place blocks of code that the script calls multiple times in this section.

Of the three script sections I use, only the script's body is mandatory. A script must contain at least one valid scripting statement to be a script. However, VBScript doesn't require you to declare or initialize variables before you use them, nor does the language require you to carve scripts into functions and subroutines. The way you organize scripts is completely up to you. I place my declarations and initializations at the top of the script and my functions and subroutines at the bottom of the script to make my code easy to read and maintain, but my approach isn't the only way to organize a script. Some developers define all functions and subroutines at the top of the script. Others define each function and subroutine immediately before the script's first invocation of the function or subroutine. (Several example scripts that Microsoft provides with WSH 1.0 use that structure.)

Next month, I'll dissect each section of my scripts in gory detail. For now, check out the resources in "Win32 Scripting Resources." Chances are good that you'll learn something new.