Chapter 3. WSH Primer

Windows Script Host (WSH), a feature of the Microsoft® Windows® 2000 family of operating systems, is a powerful multi-language scripting environment ideal for automating system administration tasks. Scripts running in the WSH environment can leverage the power of WSH objects and other COM-based technologies that support Automation, such as Windows Management Instrumentation (WMI) and Active Directory Service Interfaces (ADSI), to manage the Windows subsystems that are central to many system administration tasks.

In This Chapter

WSH Overview

The first time people encounter Windows Script Host (WSH), they often express some confusion. What exactly is WSH? Is it a language, like VBScript or JScript? No; although WSH enables you to run programs written in these languages, it is not a language itself. Is it an object model, like WMI or ADSI? No; WSH does provide a simple object model, but providing an object model is not its primary purpose.

So then what is WSH? As the name implies, WSH is a script host. A script host is a program that provides an environment in which users can execute scripts in a variety of languages, languages that use a variety of object models to perform tasks.

You are probably already familiar with other script hosts. Microsoft® Internet Explorer, for example, enables users to execute scripts that use the Dynamic HTML object model. Shell programs (such as C Shell, Bourne Shell and Korn Shell) enable you to write scripts that use an object model capable of manipulating the file system. Even the command prompt can be thought of as a scripting environment because it can run scripts written in the “batch file” language.

WSH is an unusual script host in that it was designed to be general-purpose. Unlike most of the scripting tools mentioned above, WSH imposes restrictions on neither the language used to write scripts nor the object models used by scripts.

WSH capabilities can be divided into four major areas. These areas, which will be discussed in detail throughout the remainder of this chapter, include:

  • The ability to intrinsically carry out system administration tasks

  • The ability to use COM objects to carry out system administration tasks

  • The ability to add standard programming features to WSH-compatible scripting languages

  • The ability to run command-line tools

The ability to intrinsically carry out system administration tasks

In many ways, this might be the least important of the WSH capabilities. WSH is primarily a scripting runtime; it provides the environment in which scripts can run, in much the same way that the command processor provides the environment in which batch files can run.

However, even though WSH primarily serves as the conduit through which other scripting languages and technologies operate, it is still possible to use “pure” WSH scripts to carry out system administration tasks. You can use WSH to do such things as map and unmap network drives, and add and remove printer connections. For example, this simple two-line script maps drive X to the network share \atl-fs-01public:

Set objNetwork = CreateObject("WScript.Network")
objNetwork.MapNetworkDrive "X:", "\atl-fs-01public"

The ability to use COM objects to carry out system administration tasks

As noted, WSH can be used to map network drives and to add printer connections. Beyond that, however, few system administration tasks can be carried out using WSH alone. You cannot use WSH to take inventory of computer hardware or to determine the software that is installed on a computer. You cannot use WSH to manage disk quotas or to list the members of the Enterprise Administrators group in the Active Directory® directory service.

But even though WSH has no intrinsic methods for carrying out these tasks, you can still perform system administration chores by using a WSH script. This is possible because WSH allows you to use COM (Component Object Model) objects within your scripts.

COM objects are a standard way for applications (.exe files) or programming libraries (.dll files) to present their capabilities as a series of objects. In turn, WSH can bind (connect) to these objects and harness these capabilities.

For example, WSH provides no methods for managing services on a computer. However, WMI — which is made up of a series of COM objects — can be used to manage services; WMI can perform such tasks as retrieve service properties, start and stop services, and configure service settings. Rather than provide its own methods for managing services, WSH can instead use the methods available through WMI.

In fact, WSH can access any COM object that supports Automation. Automation refers to a standard way of accessing a COM object. For the most part, scripting languages can access only COM objects using Automation; full-featured programming languages, such as C++, can access COM in additional ways. On the typical Windows-based computer, scores of Automation objects are available to manage everything from services to software to disk quotas to Active Directory. Because WSH can access all these objects, you can write scripts to manage everything from services to software to disk quotas to Active Directory.

For example, this WSH script uses ADSI to create a user account in Active Directory:

Set objOU = Wscript.GetObject("LDAP://OU=management,dc=fabrikam,dc=com")
Set objUser = objOU.Create("User", "cn=MyerKen")
objUser.Put "sAMAccountName", "myerken"
objUser.SetInfo

The ability to add standard programming features to WSH-compatible scripting languages

Applications vary widely both in what they are intended to do and in how they go about doing it; Calculator, for example, bears little resemblance to Ipconfig.exe, which bears even less resemblance to Microsoft® Word. Despite these wide variations, however, applications share some basic attributes. Many applications provide for input and output: They allow users to enter data, and they provide a method for displaying data to users. Many applications can read and write to the registry. Many applications accept command-line arguments that affect how the application runs or what the application does. This is true even of graphical applications: For example, the following command, which uses the /n argument, starts Microsoft Word without loading a blank document:

winword.exe /n

These same standard features are needed in system administration scripts; for example, how valuable would scripting be if a script could not display information? As it turns out, WSH can be used to add many of these standard features to a script: WSH can provide for input and output, it can read and write to the registry, and it can allow a script to accept command-line arguments. This ensures that any WSH-compatible language will be able to use these features, even if the language itself has no intrinsic support for them.

For example, suppose you need a script that can delete any folder from a computer; whenever you ran the script, you would pass the name of the folder to be deleted as a command-line argument:

deletefolder.vbs c:scripts

You can use the FileSystemObject to delete folders, and you can write the script using the VBScript scripting language. But how do you handle command-line arguments? After all, neither the FileSystemObject nor VBScript have any knowledge of command-line arguments or how to use them.

Fortunately, you can use WSH to handle command-line arguments. For example, the following script actually uses three technologies:

  • Line 1 uses VBScript to instantiate the FileSystemObject (although this could also have been done using WSH).

  • Line 2 uses WSH to read the value of the command-line argument and assign it to a variable.

  • Line 3 uses the FileSystemObject to delete the specified folder.

Set objFSO = Wscript.CreateObject("Scripting.FileSystemObject")
strFolder = Wscript.Arguments.Item(0)
objFSO.DeleteFolder(strFolder)

This same script can be easily rewritten in Jscript, PythonScript or any other WSH-compatible scripting language. Do these languages support the use of command-line arguments? It does not matter; because they are WSH-compatible, you can use WSH to provide this capability.

The ability to run command-line tools

Obviously, WSH is not required to run command-line tools; command-line tools can be run as stand-alone programs or can be called from batch files. However, WSH is extremely useful if you want to add “intelligence” to these tools or batch files. For example, suppose you want to map a drive only if a user logs on from the fabrikam domain. If the user is from the fabrikam domain, you want to use the net use command to map drive X to \atl-fs-01public. If the user is not from the fabrikam domain, you do not want the script to do anything.

Can this be done from within a batch file? Yes, although the solution is not particularly straightforward. (You need to figure out how to get the domain name, how to pipe that name into the script, and how to use the shell language to take action based on the value of that name.) By contrast, this simple six-line script will accomplish the same task:

Set objNetwork = Wscript.CreateObject("Wscript.Network")
Set objShell = WScript.CreateObject("WScript.Shell")
strDomain = objNetwork.DomainName
If strDomain = "fabrikam" Then
    objShell.Run "net use x: \atl-fs-01"
End If

In other words, not only can you run command-line tools from within your WSH scripts, but you can also augment those tools with capabilities that would be very difficult to replicate in a batch file.

WSH vs. Cmd.exe

At this point, it might be useful to briefly compare WSH and Cmd.exe, the command-line interpreter found in Windows. Both are scripting environments: WSH allows you to run WSH scripts; Cmd.exe allows you to run batch files (sometimes referred to as shell scripts). Both WSH and Cmd.exe require interaction with scripting languages and scripting tools: It is difficult to write useful scripts with nothing more than WSH, and it is difficult to write useful batch files with nothing more than the shell language.

This is where the differences between the two runtimes become more apparent. WSH provides access to a large number of sophisticated scripting languages; Cmd.exe limits you largely to the simplistic syntax of the batch file language. The only tools available to Cmd.exe are command-line utilities; WSH not only offers access to these same utilities but provides access to Automation objects as well. The scripting tools available to Cmd.exe represent a very small subset of the tools available to WSH. These differences make WSH a superior scripting environment.

Does this mean that you should throw away all your command-line tools and batch files and switch exclusively to WSH? Of course not — if you have a solution that works, there is no reason to get rid of it. But for problems that batch files and command-line tools cannot solve, WSH, with its access to the myriad capabilities of VBScript, JScript, WMI, ADSI, and other Automation objects, might provide the solution you have been looking for.

A Note About WSH Versions

The sample scripts in this chapter were authored and tested with WSH version 5.6. Some of the WSH functionality described is available only under WSH version 5.6 or later. Therefore, you should determine which version of WSH you currently have installed on your computer; if it is earlier than version 5.6, you should upgrade it before proceeding with the chapter.

Note

Note

Of course, the added functionality found in WSH 5.6 makes it a very worthwhile upgrade regardless of whether you intend to test the scripts found in this chapter.

To display the version of WSH installed on a computer, type cscript at the command prompt and then press ENTER. If you have WSH 5.6 installed, you should see output similar to this:

C:WINDOWS>cscript
Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2000. All rights reserved.

You can also retrieve this information using the following script:

Wscript.Echo Wscript.Version

For information about downloading WSH version 5.6, go to the Windows Script Technologies link on the Web Resources page at http://www.microsoft.com/windows/reskits/webresources.

WSH Architecture

When you learn how to drive a car, you do not need to first become an expert on the internal combustion engine or fluid dynamics. If you can distinguish between the gas and the brake pedal and figure out how the steering wheel works, you probably will be able to get from Point A to Point B.

And that is perfectly fine, assuming that after you get to Point B you will get out of the car and never drive again. But what if you want to drive on a regular basis? In that case, it helps to understand a little bit about how cars work, and why they might not work. You should know that cars require gas, that tires require air, and that batteries will run down if the lights are left on. If you do not understand these basic principles of cars, you are likely headed for some unpleasant surprises.

The same is true of scripting. If all you want to do is use a script to stop the Alerter service on the local computer, there is no need to read this book and to memorize the ins and outs of scripting. Instead, just copy and run the following:

strComputer = "."
Set objWMIService = GetObject("winmgmts:" _
    & "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
Set colServices = objWMIService.ExecQuery _
    ("SELECT * FROM Win32_Service WHERE Name = 'Alerter'")
For Each objService in colServices
    errReturnCode = objService.StopService()
Next

But what happens if you want to stop a different service, or you want to stop a service on a remote computer? What happens if you want to start the Alerter service? If you want to modify existing scripts or if you want to create your own scripts, you need to understand how scripting works. This understanding requires at least a passing familiarity with the WSH architecture.

Components of the WSH Environment

WSH has a modular architecture: It is made up of separate parts, each responsible for performing a specific function. This modularity gives WSH two important capabilities: it can make use of multiple scripting languages and it can leverage COM objects.

Figure 3.1 illustrates the major components of the WSH environment and their interactions. The WSH environment includes script files, script hosts, scripting language engines, and COM objects. In this diagram, the shaded boxes represent the items that are installed when you install WSH 5.6. The major components shown in this diagram, and the ways in which they interact, are explained in subsequent sections of this chapter.

Major Components of the WSH Environment

Figure 3.1. Major Components of the WSH Environment

Script Files

You create WSH script files to automate system administration tasks. Script files (more commonly referred to simply as scripts) are plain-text files that contain instructions describing how to accomplish a task or set of tasks. (Plain-text means that the files cannot include any special formatting characters.) For example, the following script will fail because line 2 uses “smart quotes.” Because these are not standard characters, WSH cannot interpret them, and the script fails with the error message, “Invalid character.”

Wscript.Echo "This line is correct."
Wscript.Echo "This line is incorrect."

This is important to keep in mind because most word processing applications use special formatting characters by default. This means that word processing applications do not make the best environment for creating scripts: It is too easy to save a script with special formatting characters that will prevent the script from running. Instead, text editors designed to work with plain text (such as Notepad) are the best tools for creating WSH scripts. Because these editors typically do not support special formatting characters, there is less chance that you will inadvertently include such a character in your script.

Note

Note

You should also avoid the temptation of creating scripts in a word processor and then copying and pasting the code into a text editor; there is no guarantee that the pasted lines of code will actually be in plain-text format. These problems can be especially difficult to diagnose because the code might look as though it is in plain-text format. In reality, the code might still contain special characters that will cause the script to fail.

The instructions included in a script can be written in any WSH-compliant scripting language for which a corresponding scripting language engine has been installed. You should save the file with the file-name extension that corresponds to that language. Table 3.1 lists three example script files along with the language that corresponds to their file-name extensions.

Table 3.1. Script File Name Extensions

File Extension

Sample File Name

Scripting Language

.VBS

EnumPrinters.vbs

VBScript

.JS

EnumProcesses.js

JScript

.PLS

EnumDisks.pls

PerlScript

In other words, if you are writing a script using VBScript, save the file with the .vbs file name extension. If you are writing a script using JScript, save the file with the .js file name extension.

Note

Note

It is possible to run scripts even if you use a nonstandard file name extension (such as .xyz). This is explained later in this chapter.

After you have typed your script into Notepad and saved it, it is ready to run. This is one of the primary advantages of scripting: you do not need to create any supporting files nor run the script through a compilation process; instead, you simply write it, save it, and run it. For example, type the following two lines of code into Notepad:

Set objNetwork = Wscript.CreateObject("Wscript.Network")
Wscript.Echo objNetwork.ComputerName

Save the file with the .vbs file name extension, and you have a script that returns the name of the local computer.

Script Hosts

The script host initiates and coordinates the running of your script; it reads your script file and interacts with components of the WSH environment and any COM objects required by the script. It is also the responsibility of the script host to determine which language engine to use when running the script. For example, if the script has a .vbs extension, the script host will load the VBScript language engine and begin working with that engine to execute the code.

The WSH environment includes two script hosts: the console-based CScript and the GUI-based WScript. The two script hosts provide nearly identical capabilities, and in most cases, it does not matter which of the script hosts you use to run your scripts.

The two exceptions lie in how you interact with a script; that is, how you get information into a script (input) and how the script displays information it has retrieved (output). In general, CScript receives input from the command prompt and displays output in a command window. WScript, by contrast, receives input through a graphical dialog box and displays output in a graphical message box.

Otherwise, the two script hosts are largely identical: If you have a script that does not require user interaction, you can run that script under either CScript or WScript. For example, the following script maps a network drive. Because it neither requires input nor displays output, it runs exactly the same under either script host:

Set objNetwork = Wscript.CreateObject("WScript.Network")
objNetwork.MapNetworkDrive "g:", "\atl-fs-01Sales"

On the other hand, the following script — which displays a series of messages — runs much differently under CScript (where the messages are displayed as individual lines within a command window) and WScript (where the messages are displayed as a series of message boxes). If you are interested in seeing the difference for yourself, copy the script into Notepad, save it with a .vbs file extension, and then run it under both CScript and WScript. (For more information about running scripts under a script host, see “Running WSH Scripts” later in this chapter.)

Wscript.Echo "Line 1."
Wscript.Echo "Line 2."
Wscript.Echo "Line 3."
Wscript.Echo "Line 4."

Scripting Language Engines

Although the script host is responsible for initiating and coordinating the running of a script, it is not capable of interpreting any scripting language. The WSH environment separates the logic necessary to interpret a given scripting language from the script host.

It is this separation that enables WSH to be a multi-language scripting environment. This is because WSH does not attempt to “speak” VBScript, JScript, ActivePerl, Rexx, Python, or any other scripting language. Instead, it is up to the language engine to translate a script into commands that WSH can understand. You can write a WSH script in VBScript because the VBScript language engine can translate the code in your scripts into commands that WSH can understand and act upon. You cannot write a WSH script in C++ because there is no language engine that can translate C++ code into WSH commands.

When a scripting language engine is installed, at least one mapping is recorded in the registry. The mapping associates a file name extension with the dynamic link library (DLL) that implements the scripting language engine. The script host usually determines the language used in a script by examining the file name extension and then checking the registry to determine the corresponding scripting language engine.

Note

Note

You can force the script host to use the scripting language engine of your choice by specifying the //E: command-line option. (See Table 3.2.) This option allows you to use any file name extension on your script files, regardless of the scripting language in which they are written.

Table 3.2. Script Host Options

Parameter

Description

//B

Batch mode; suppresses display of user prompts and script errors. For example, if your script includes messages displayed using Wscript.Echo, these messages will not appear when the script runs in Batch mode. Batch mode also suppresses the use of VBScript functions such as Msgbox.

The default is Interactive mode.

//D

Turns on the Microsoft Script Debugger if this program is installed. The Script Debugger ships as part of Windows 2000, although it is not installed by default. The Script Debugger does not ship with Windows XP.

If the Script Debugger is not installed, no error will occur. Instead, the script will simply run.

//E:engine

Executes the script with the specified script engine. Among other things, this allows you to run scripts that use a custom file name extension. Without the //E argument, you can run only scripts that use registered file name extensions. For example, if you try to run this command: cscript test.admin

You will receive this error message:

Input Error: There is no script engine for file
Script Host Options extension
".admin".

To run a script that uses a custom file extension, include the //E argument: Cscript //E:vbscript test.admin

One advantage of using nonstandard file name extensions is that it guards against accidentally double-clicking a script and thus running something you really did not want to run.

This does not create a permanent association between the .admin file name extension and VBScript. Each time you run a script that uses a .admin file name extension, you will need to use the //E argument.

//H:CScript -or- //H:WScript

Registers Cscript.exe or Wscript.exe as the default application for running scripts. When WSH is initially installed, WScript is set as the default script host.

//I

Interactive mode; allows display of user prompts and script errors. This is the default mode and is the opposite of Batch mode.

//logo

Displays a logo when the script runs under CScript (this is the default setting for WSH). The logo, which appears prior to any of the output from the script, looks like this:

Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2000. All
Script Host Options rights
reserved.

//nologo

Prevents display of the logo at run time (by default, the logo is displayed).

The //nologo option is often used for scripts whose output is redirected to a text file. Suppressing the logo ensures that this information does not appear within the text file. This makes it easier to write scripts that parse the information found in the text file or that import the contents of the file to a database, because these scripts do not have to account for the logo.

//S

Saves the Timeout and Logo options for this user. For example, this command ensures that the logo will be suppressed anytime a script runs under CScript: cscript //nologo //S

You can also modify these settings by right-clicking a script file and then clicking Properties.

//T:nn

Determines the maximum number of seconds the script can run. (The default is no limit.) The //T parameter prevents excessive execution of scripts by setting a timer. When execution time exceeds the specified value, the script host interrupts the script engine and terminates the process.

//X

Starts the program in the Microsoft Script Debugger. If the Script Debugger is not installed, the script simply runs.

//?

Displays a brief description of command parameters (the usage information). The usage information is similar to the information presented in this table, although with less explanation. For example, here is the usage information for the //E argument: //E:engine Use engine for executing script

COM Objects

WSH includes the WScript object and three COM-based objects: WshShell, WshNetwork, and WshController. Although they are included with the WSH environment, you use them in your scripts in the same way you use other COM objects.

The WSH COM objects possess neither the depth nor the breadth of the system administration capabilities found in WMI or ADSI. Nevertheless, you are likely to find these objects useful in several situations:

  • If you need to carry out a task that cannot be carried out using another Automation object. For example, the WshNetwork object allows you to map network drives; this capability is not available in either WMI or ADSI.

  • If you have down-level clients that are not running WMI or ADSI. For example, ADSI might be the preferred method to retrieve the name of the local computer, but ADSI did not ship with Windows 98. For Windows 98 computers, you can use the WshNetwork object to retrieve the name of the local computer.

  • If you need to run a script on a remote computer, and neither WMI nor ADSI is capable of carrying out this task remotely. In that case, you can use the WshController object to run the script on the remote computer.

How the Components of the WSH Environment Work Together

Components in the WSH environment must interact with one another to run scripts. These interactions include the following:

Script Host and Script FileWhen a script host is invoked to run a script, it begins by examining the file-name extension of the script file. The script host searches the registry to determine the scripting language engine that corresponds to the extension and then loads the script file in preparation for interpreting its instructions. All this happens before a single line of code is actually executed.

Script Host and Scripting Language EngineAfter determining the language used in the script, the script host works with the corresponding scripting language engine to interpret the instructions in the script. The script host communicates with the scripting language engine through the Windows Script Interfaces. The entire script is read and checked for syntax errors before any code is executed. For example, the following script has an error in line 3; the syntax of the If...Then statement is incorrect:

Wscript.Echo "Line 1."
Wscript.Echo "Line 2."
If x = 1
    Wscript.Echo "X is equal to 1."
End If

You might expect that the script would execute lines 1 and 2 — and thus display two messages — before encountering the error on line 3. Instead, the error is caught in the pre-execution syntax check. Instead of displaying two messages, the script displays the error message “Expected ’Then’.”

WScript Library and COM ObjectsWhen instructions within a script indicate that a COM object is to be used, the built-in WScript library interacts with the COM runtime on behalf of the script.

Note

Note

Many scripting languages provide the ability to interact with COM objects directly, in which case WScript is not part of the interaction. Because this chapter is about WSH, the examples use the WScript CreateObject and GetObject methods. However, VBScript also enables you to bind to COM objects and has a slightly easier syntax. Consequently, the VBScript functions are used in nearly all the other scripts in this book. For a more thorough comparison of the two functions, see “VBScript Primer” in this book.

WSH Object Model

People typically speak of WSH as though it were a single item. In truth, however, WSH is made up of multiple components. For example, the WSH environment includes a built-in object, WScript, and three COM objects: WshShell, WshNetwork, and WshController. Together, WScript, WshShell, WshNetwork, and WshController are referred to as the WSH objects. Your scripts access the capabilities of these objects through the WSH Object Model.

Figure 3.2 presents the WSH Object Model. Each of the items detailed in the diagram will be discussed at some point in this chapter.

WSH Object Model Diagram

Figure 3.2. WSH Object Model Diagram

Running WSH Scripts

If you were to ask a group of people how to start Microsoft Word, you would likely get a number of different answers. After all, you can start Word by clicking the Start menu and then clicking Microsoft Word, or by typing winword.exe in the Run dialog box. Some people might start Word from the command prompt, some from the Quick Launch bar, and others by using a keyboard shortcut. You can also start Word implicitly, by double-clicking a file with the .doc file name extension. Regardless of the method employed, in each case the end result is the same: Microsoft Word will run. Whether or not one method is better than another depends on such factors as convenience, personal preference, and individual needs (for example, whether you want a specific document to be loaded when Word starts).

While you are working through the examples in this chapter, it is recommended that you run the scripts from the command line using CScript (unless otherwise indicated). Often times it makes no difference which script host you use to run a script. However, many system administration scripts should be run under CScript, the console-based script host, for at least two reasons.

For one, running your scripts under CScript enables them to run an external program and retrieve the output from the program. Perhaps more important, though, returned data is displayed in a command window rather than in a serious of message boxes. This is particularly useful for scripts that might return hundreds of items of data, such as scripts that retrieve events from the event logs.

The next few sections of this chapter will discuss the ins and outs of running scripts in more detail.

Running Scripts from the Command Line

Although this might be the age of the graphical user interface, many system administrators are still more comfortable working from the command prompt than within the GUI. This is not a problem with WSH; you can run scripts from the command prompt or from the GUI. Not only do you not lose any capabilities by choosing the command prompt over the GUI, but running scripts from the command line and under CScript also has at least two benefits:

  • It is easier to pass arguments to the script. These arguments might be used by the script itself (for example, you might pass the name of a folder to be deleted), or the script host might use them when running the script.

  • It is easier to cancel a script running under CScript. When a script runs from the command prompt, you can cancel the script either by pressing CTRL+C or by closing the command window in which it is running. If a script is running under WScript, the only way to cancel it is to terminate the Wscript.exe process.

You can run script files from the command line in one of two ways:

  • Type the name of the script, including its file name extension, at the command prompt:

    HardwareAudit.vbs
    
  • Type the name of one of the script hosts followed by the name of the script:

    cscript HardwareAudit.vbs
    
    wscript HardwareAudit.vbs
    

When you use the first method, the command interpreter must determine which script host to call. If you do not specify either CScript or WScript, the script will run under the default script host as configured on the computer. When you use the second method, you explicitly specify the script host under which the script should be run. The command interpreter runs cscript.exe or wscript.exe, whichever was specified, passing it the script file HardwareAudit.vbs.

Script Host Options

Both CScript and WScript accept a number of options that either affect how the script host will run a script or modify some aspect of the WSH environment. The two script hosts share a common set of options; CScript also has a few options, most notably //logo and //nologo, which have no effect in WScript.

When WSH is first installed, WScript is configured as the default script host. (If you do not specify either CScript or WScript when starting a script, WSH runs scripts using the default script host.) To set the default script host to CScript, type the following at the command prompt:

cscript //H:cscript

To reset WScript as the default script host, type this:

wscript //H:wscript

Table 3.2 lists a number of the more commonly used WSH options.

Redirecting Script Output to a Text File

Sometimes you run a script because you need to do something right away. For example, you might need to check the status of a particular service or the amount of free space on a particular hard drive. Other times you run a script with the intention of going back and analyzing the data later; for example, you might run a script that retrieves a list of all the software installed on all your domain controllers. Sometime in the future, you will examine that list and determine whether your domain controllers have been properly configured.

If your script retrieves data that needs to be accessed later on, it is a good idea to save this data, perhaps in a database or a text file. It is possible to include code within a script that saves data in either of these formats. If a script is designed to always save data, it is best to include the code to carry out that procedure.

But what if there are times when you want to save the output from a script and other times when, using that same script, you prefer to view that output on the screen? If you use Wscript.Echo to display data, you actually have two choices: display the data on the screen or write the data to a text file. If you choose to write the data to a text file, all you have to do is run the script using one of the two command-line redirection characters.

The command interpreter provides two ways to redirect output from the screen to a file. (That is, output is saved to a file instead of being displayed on the screen.) If you use the > character followed by a file name, output will be saved in a text file, overwriting anything that might already be saved in that file. For example, this command saves the output of a script to the file c:scriptsservices.txt:

cscript service_info.vbs > c:scriptsservices.txt

The >> characters append data to the specified file; new output from the script is added to anything already in the file:

cscript service_info.vbs >> c:scriptsservices.txt

You might also want to use the //nologo option, to ensure that the logo is not included within the text file:

cscript //nologo service_info.vbs > c:scriptsservices.txt

When you redirect the output of the script, no messages of any kind appear in the command window. Instead, all output, including error messages, is redirected to the text file.

Scheduling the Running of Scripts

Tasks that you script often need to be done repeatedly according to a prescribed schedule. You can use the Windows 2000 Task Scheduler or At.exe to schedule the running of these scripts. The scripts still run under one of the script hosts, but they run at the designated times without your interaction.

The ability to run scripts without the need for any human intervention is one of the major advantages scripting has over other administrative tools. For example, suppose you have a script that runs once a week and backs up and clears the event logs on all your domain controllers. There is no need for you to remember to manually run this script each week, and no need for you to arrange for someone else to run this script should you be out of the office. Instead, the script can be scheduled to run once a week, and it can do so without any need for human intervention. As an added bonus, the script can be scheduled to run during off-hours, thus minimizing any disruption to users.

You can also use WMI to create, delete, and manage scheduled tasks. (For more information about scheduling the running of scripts using WMI, see “Creating Enterprise Scripts” in this book.) For example, this script creates a scheduled task that runs a script named Monitor.vbs every Monday, Wednesday, and Friday at 12:30 P.M.

Set Service = GetObject("winmgmts:")
Set objNewJob = Service.Get("Win32_ScheduledJob")
errJobCreated = objNewJob.Create _
    ("cscript c:scriptsmonitor.vbs", "********123000.000000-420", _
        True , 1 OR 4 OR 16, , , JobID)
Wscript.Echo errJobCreated

Other Methods of Running Scripts

For the most part, system administrators either run scripts from the command line or schedule scripts to run periodically. However, a number of other methods are available for running scripts.

Running Scripts on Remote Computers. The WshController object, a COM object included with WSH, allows your scripts to run other WSH-based scripts on remote computers. For more information about the WshController object, see “WSHController Object” later in this chapter.

Running Scripts from Windows Explorer. You can run a WSH script by double-clicking the script file in Windows Explorer. This will cause the script to run using the default script host. If the default script host is CScript, a command window will open and the script will run. However, the command window will close as soon as the script finishes running. This means that any output generated by the script will disappear as soon as the script finishes and the command window closes.

Running Scripts from the Context-Sensitive Menu. You can run a WSH script by right-clicking the script file in Windows Explorer and selecting the appropriate option: Open with Command Prompt if CScript is your default script host or Open if WScript is your default script host.

Running Scripts by using Drag and Drop. You can run a script by dragging one or more files or folders onto the script file in Windows Explorer. The script runs under the default host, and the full path to each file or folder dropped onto the script file is passed to the script host as a command-line argument.

For example, this script echoes the path name of each file or folder dropped onto it:

For Each strArgument in Wscript.Arguments
    Wscript.Echo strArgument
Next

This method is commonly used for passing a list of file names as arguments to a script that performs an action on a set of files. For example, you might drag several files onto a script, and the script, in turn, might copy each of those files to a remote server.

The command interpreter limits the number of characters that can be passed by a program (or script) and all of its arguments to roughly 2048 characters. If you use the drag-and-drop feature to provide arguments to a script, be sure the total number of characters does not exceed the 2048 limit. The total number of characters can be determined by adding the length of the script’s fully qualified path and file name, the length of each argument, and any white space used to separate each argument.

WSH Objects

The WSH environment includes the built-in WScript object and three COM objects: WshShell, WshNetwork, and WshController. Your scripts can use these objects to help automate system administration tasks.

The WSH objects provide your scripts with functionality that might not be available elsewhere, including the ability to work with command-line arguments, control script execution, and run scripts on remote computers. This means that the scripting language does not have to supply these elements. VBScript, for example, does not include any methods for working with command-line arguments. However, you can still use command-line arguments with VBScript by using the argument capabilities built into WSH.

WSH and the WSH objects do not include all the things system administrators might want to do; far from it. In addition, even tasks that are covered by the WSH objects are often better handled by using technologies such as WMI and ADSI.

For example, the WshShell object has a method named RegRead that allows your scripts to read a fixed registry entry. This method works fine if you know in advance the registry entry the script needs to read. Suppose you want to determine the wallpaper that the user is displaying. In a very specific situation such as that, a simple little WSH script such as this will suffice:

Set WshShell = WScript.CreateObject("WScript.Shell")
strWallpaper = WshShell.RegRead "HKCUControl PanelDesktopWallpaper"
Wscript.Echo strWallpaper

But what if you have a more open-ended task, such as enumerating all of the entries within a given subkey? In a case such as that, WSH is of little use. Instead, you should use the WMI StdRegistry provider to enumerate the entries of a subkey. The StdRegistry provider gives your scripts more complete and more flexible methods for managing the registry than the WshShell object.

In addition, WMI, for the most part, works exactly the same on remote computers as it does on the local computer. WSH, by contrast, is designed to work only on the local computer. To run WSH scripts remotely, you must use the WshController object and actually create two scripts: the script to be run and the script that allows that script to run remotely. (The WshController object is discussed in detail later in this chapter.)

Nevertheless, there are still times when you might want to use WSH rather than WMI. For example, “pure” WSH scripts are more likely to be supported by Windows NT 4.0-based and Windows 98-based computers. WSH is included with both those operating systems; however, neither WMI nor ADSI shipped with Windows NT 4.0 or Windows 98. If you need to support these older operating systems, WSH might allow you to write a single script that will run on all your computers rather than requiring you to write separate scripts for each platform.

WScript Object

The WScript object provides a wide range of capabilities to WSH scripts regardless of the language in which that script is written. As such, the WScript object ensures that a WSH-compliant language will always be able to carry out such key tasks as binding to COM objects and parsing command-line arguments.

Depending on the scripting language you use and the task you have chosen, you might or might not need to use the WScript object. For example, VBScript includes a GetObject function that allows your script to bind to COM objects. Because the syntax of the VBScript GetObject function is slightly simpler, you will likely use it rather than the WScript equivalent.

On the other hand, VBScript does not include functions for parsing command-line arguments. Fortunately, you can still use command-line arguments within VBScript; you simply make use of the argument parsing functionality provided by the WScript object. This ability to mix and match methods from WSH with methods and functions from the scripting language is one primary advantages of using WSH as a scripting environment.

Figure 3.3 shows the properties and methods of the WScript object as well as the properties and methods of the WshArguments, WshUnnamed, and WshNamed collections, all of which are accessed through the WScript object.

WScript Object Model

Figure 3.3. WScript Object Model

Accessing the WScript Object

The WScript object is available to all WSH scripts without the script needing to bind to the object. No call to the WScript CreateObject method is required prior to using WScript properties and methods. This means you can use WSH functions such as Echo or Sleep without having to bind to the WScript object; as a result, this single line of code is a perfectly valid WSH script:

Wscript.Echo "No binding required."

The WScript object is always available for one reason: CreateObject and GetObject are methods found within this object. If the WScript object were not always available, neither CreateObject nor GetObject would be. Thus, you would not be able to bind to any COM object (including the WScript object).

WScript Capabilities

The primary purpose of the WScript object is to provide your script with basic functionality as opposed to carrying out a particular system administration task. In other words, you will not find the capability to manage services or event logs; instead, you will find capabilities that enable you to bind to other objects that can be used to manage services or event logs.

Table 3.3 lists the capabilities provided by the WScript object, along with the methods and properties that your scripts can use to access this functionality. Each of these methods and properties is explained in more detail in subsequent sections of this chapter.

Table 3.3. Capabilities Provided by the WScript Object

Category

Methods or Properties

Using COM objects

CreateObject, GetObject

Handling input and output

Echo, StdOut, StdIn, StdErr

Working with command-line arguments

Arguments

Controlling script execution

Quit, Sleep, Timeout, Interactive

Obtaining WSH environment info

Application, BuildVersion, FullName, Name, Path, ScriptFullName, ScriptName, Version

Handling events

CreateObject, GetObject, ConnectObject, DisconnectObject

Using COM Objects

If you have a question about practically anything — sports, history, science, gardening — it is likely that you can find the answer at the public library. However, this does not mean that you can walk into the library, pick out a book at random, and expect to find the answer. Instead, answers to specific questions are found in specific books, and you need to locate the correct book if you want to have your question answered.

The same thing applies to COM objects. There is likely to be a COM object that can be used to script most of your system administration needs. However, you cannot use just any COM object; COM objects have specific, oftentimes unique capabilities. By the same token, you cannot simply start using a COM object, just as you cannot start reading a book without first finding that book. (The one exception is the WScript object, which you can simply start using.) Instead, before a script can use a COM object, it must first bind to that object. The WScript object provides two methods for creating COM objects: CreateObject and GetObject.

Creating a New Instance of a COM Object

To create a new instance of a COM object, a script can call the WScript CreateObject method and pass it the Programmatic Identifier (ProgID) of the COM object by using the following syntax:

WScript.CreateObject("ProgID")

To continue the analogy with the library, the ProgID is roughly equivalent to the call number assigned to a book. If you go to the library and give the librarian the call number, he or she can locate the book for you. Likewise, if you pass the scripting host a ProgID, the scripting host can look in the registry and locate the COM object you want to create. ProgIDs are unique to each COM object, just as call numbers are unique to each book.

How do you know the correct ProgID for a given COM object? Unfortunately, there is no simple answer to that question; your best course of action is to look at the documentation that accompanies the object. All ProgIDs are stored in the HKEY_CLASSES_ROOT portion of the registry, as shown in Figure 3.4. However, this listing is of only limited use because not all of these ProgIDs can be accessed using scripts.

ProgIDs in the Registry

Figure 3.4. ProgIDs in the Registry

To be able to use a newly created object, the script needs to store a reference to the object in a variable by using the following syntax:

Set objVariable = WScript.CreateObject ("ProgID")

After a reference to the object is stored in a variable, the script can call a method or access a property of the object by using dot notation. (Dot notation is discussed in the “VBScript Primer” chapter of this book.)

Scripts call methods by using the following syntax:

objVariable.MethodName

Scripts access properties by using the same syntax:

objVariable.PropertyName

The script in Listing 3.1 creates a new instance of the ADSI System Information object (using the ProgID ADSystemInfo), stores a reference to it in the objSysInfo variable, and then displays the Domain Name System (DNS) domain name for the logged-on user.

Example 3.1. Using a COM Object

1 Set objSysInfo = Wscript.CreateObject("ADSystemInfo")
2 Wscript.Echo "Domain DNS name: " & objSysInfo.DomainDNSName

As shown in Figure 3.5, only two portions of the code statement must change when you create different COM objects: the ProgID, and the name of the reference variable (if your script must reference multiple COM objects).

Elements of a Statement That Creates a COM Object

Figure 3.5. Elements of a Statement That Creates a COM Object

For example, the following lines of code bind to various COM objects. From line to line, the only item that changes is the ProgID:

Set objReference = Wscript.CreateObject("Word.Application")
Set objReference = Wscript.CreateObject("InternetExplorer.Application")
Set objReference = Wscript.CreateObject("Scripting.Dictionary")
Set objReference = Wscript.CreateObject("Wscript.Network")

Attaching to an Existing Instance of a COM Object

If the COM object you want to use is already running, you can use that existing object rather than create a new instance. The WScript GetObject method lets you reference and use a previously instantiated object instead of creating a new one.

When you write scripts that use WMI or ADSI, you will typically use the GetObject method; this is because both WMI and ADSI are always available. (For example, you can stop the WMI service, but if you run a script that uses WMI, the service will automatically restart.) Although there are exceptions, a typical WMI script might start like this:

Set objWMIService = Wscript.GetObject("winmgmts:")

When you are writing scripts that use the WSH objects, you will usually use the CreateObject method because the WSH objects are not usually preinstantiated.

Comparing VBScript CreateObject and GetObject Functions with WSH

The VBScript language also provides CreateObject and GetObject functions. The VBScript CreateObject function and the WScript CreateObject method both instantiate COM objects when they are called with a single parameter, the ProgID of the COM object to instantiate. For example, these two lines of code — the first using the VBScript version of CreateObject and the second using the WSH version — are functionally identical; both instantiate an instance of the FileSystemObject:

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFSO = Wscript.CreateObject("Scripting.FileSystemObject")

Both versions of CreateObject can also accept a second parameter; however, each interprets this second parameter in a completely different way. Consider these two lines of code. The first line uses VBScript, and the second uses WSH:

Set objExcel = CreateObject("Excel.Application", "Parameter2")
Set objExcel = Wscript.CreateObject("Excel.Application", "Parameter2")

The VBScript CreateObject function interprets the second parameter as a remote computer name and tries to create the COM object on that remote computer; in this example, it tries to instantiate an instance of Microsoft Excel on a remote computer named Parameter2. The WScript CreateObject method interprets a second parameter as a subroutine prefix to be used in handling events from the object. The two GetObject functions are similarly related.

To simply create a COM object, you can use either the VBScript function or the WScript CreateObject method. After the object has been created, there are no differences in capabilities; all the methods and properties of the object available using Wscript CreateObject are also available using VBScript CreateObject. Furthermore, these properties and methods are all called in identical fashion.

On the other hand, if you want to use the remote object creation or event-handling capabilities, you need to choose the method or function that provides that additional functionality. For more information about the VBScript CreateObject and GetObject functions, see “VBScript Primer” in this book.

Handling Input and Output

Scripts have to interact with users. Output, for example, is a key element of many system administration scripts: A script that parses event logs for specific entries should display any entries it finds. Likewise, scripts need to be able to prompt users for input and then make use of that input. Suppose you write a script that retrieves information about the connectivity between two computers. You need to provide users with a simple way to specify the two computers being checked for connectivity.

WSH provides a means for interacting with users and, perhaps more important, provides a way to receive input from and direct output to the command window. Among other things, this allows you to create VBScript scripts that can run as command-line utilities, accepting input from the command prompt and displaying output within a command window. This is more useful than it might first seem, because VBScript has no intrinsic methods for receiving input from or directing output to the command window.

The WScript Echo method provides a simple way to display messages. It accepts an item (a string, a number, a date) as its only parameter and displays that item as a message to the user. The script in Listing 3.2 uses the WScript Echo method to display the version of WSH installed on the computer on which the script runs.

Example 3.2. Displaying the Version of WSH on a Computer

1 Wscript.Echo "The version of WSH on this computer is: " & WScript.Version

In addition, the WScript object has properties that enable your scripts to access three objects that provide input and output functionality: StdOut, StdIn, and StdErr. These objects provide their own methods and properties for working with input and output, which are shown in Figure 3.6. In general, only scripts run under CScript can access StdOut, StdIn, and StdErr.

Methods and Properties of the TextStream Objects

Figure 3.6. Methods and Properties of the TextStream Objects

Displaying Messages to Script Users

One of the primary purposes of system administration scripting is to answer questions. How much free hard disk space is available on this server? Which user account is Internet Information Service running under? Available memory is quite low on this mail server; what processes are running and using up the memory?

For a script to answer questions such as these, it must retrieve the answers and have a way to communicate those answers back to you. Although scripts can save retrieved information in text files or databases, it is more common to have the script display that information on the screen. WSH can display information by using the Echo method.

The WScript Echo method takes one or more items as parameters and displays those items to the user. If a script is run under CScript, the items are displayed in the command window. If a script is run under WScript, the items appear in message boxes. The Echo method can display almost any kind of data, including strings, numbers, dates and times, and even the results of mathematical calculations. For example, this line of code displays the value 4, the result of adding 2 and 2:

Wscript.Echo 2 + 2

The Echo method is easy to implement; you simply call the method, followed by the information you want to display on the screen. To display the value of a variable, specify the variable name as the parameter:

Wscript.Echo strMyVariable

This also holds true for VBScript variables such as Now and Time:

Wscript.Echo Now

To display a string, simply enclose it in quotation marks:

Wscript.Echo "This is my string."

In fact, the only real trick to working with Wscript.Echo is understanding the difference between what happens when a script using this method runs under CScript and what happens when that same script runs under WScript. For example, consider the script in Listing 3.3, which displays three status messages by using the WScript.Echo method.

Example 3.3. Using the Echo Method to Display Three User Messages

1 Wscript.Echo "Examining System Drives"
2 Wscript.Echo "Determining Free Drive Space"
3 Wscript.Echo "Mapping Network Drives"

When run under CScript, the script displays the following in a command window — all at once, without stopping or requiring any user interaction.

Examining System Drives
Determining Free Drive Space
Mapping Network Drives

When run under WScript, however, the script creates three separate message boxes, as shown in Figure 3.7. The message boxes are presented one at a time, and each requires a user to click the OK button before the next one can be displayed.

Three Separate Message Boxes Produced by the Echo Method Running Under WScript

Figure 3.7. Three Separate Message Boxes Produced by the Echo Method Running Under WScript

Under WScript, each call to the WScript.Echo method results in the creation of a new message box. Depending on the script, this can be very important. If your script simply returns the amount of free disk space on drive C, the fact that this information is displayed in a message box rather than in the command window might be irrelevant. Suppose, however, that the script returns a list of all the services installed on a computer. Using WScript, you would need to respond to 100 or so message boxes, one for each service. In addition, as each message box was dismissed, that piece of information would disappear from the screen.

Under CScript, not only is the information displayed in the command window without the need for any user intervention (such as dismissing message boxes), but all the information also remains on the screen until the command window is dismissed. This allows you to copy the data and paste it into another application.

Getting Text into and out of Scripts

Command-line tools (including batch files) typically interact with three standard input and output streams. These are known as Standard In (StdIn), Standard Out (StdOut), and Standard Error (StdErr). Unless you specify otherwise, the command processor assumes that input will be received from the keyboard (StdIn) and output (StdOut) and error messages (StdErr) should be sent to the command window.

StdIn, StdOut, and StdErr (available only when your scripts run under CScript) provide your scripts with access to each of these streams. These streams serve several important purposes:

  • They provide a way to display output in a command window.

  • They provide a way for users to type input from a command prompt and have that input read by a script.

  • They provide a way for scripts to access output and standard error information generated by a script, a batch file, or a command-line tool.

Displaying Output Using StdOut

StdOut can be used to display output within a command window. StdOut includes the properties shown in Table 3.4.

Table 3.4. StdOut Methods

Method

Description

Write

Writes the supplied characters to the screen but does not append a carriage return/linefeed. For example, this script uses the Write method four times:

Wscript.StdOut.Write "ABCD"
Wscript.StdOut.Write "EFGHIJKLMN"
Wscript.StdOut.Write "OPQRSTUV"
Wscript.StdOut.Write "WXYZ"

When this script is run, the following output appears in the command window:

ABCDEFGHIJKLMNOPQRSTUVWXYZ

WriteLine

Similar to Wscript.Echo, WriteLine writes the supplied characters to the screen and then appends a carriage return/linefeed (as though a user had pressed ENTER). For example, this script uses the WriteLine method four times:

Wscript.StdOut.WriteLine "ABCD"
Wscript.StdOut.WriteLine "EFGHIJKLMN"
Wscript.StdOut.WriteLine "OPQRSTUV"
Wscript.StdOut.WriteLine "WXYZ"

When this script is run, the following output appears in the command window:

ABCD
EFGHIJKLMN
OPQRSTUV
WXYZ

WriteBlankLines

Inserts a blank line in the output, as though a user had pressed ENTER twice without typing any characters. WriteBlankLines accepts a single parameter: the number of blank lines to insert.

For example, this script uses WriteBlankLines to insert first 1 and then 2 blanks lines in the output:

Wscript.StdOut.WriteLine "ABCD"
Wscript.StdOut.WriteBlankLines 1
Wscript.StdOut.WriteLine "EFGHIJKLMN"
Wscript.StdOut.WriteLine "OPQRSTUV"
Wscript.StdOut.WriteBlankLines 2
Wscript.StdOut.WriteLine "WXYZ"

When this script is run, the following output appears in the command window:

ABCD

EFGHIJKLMN
OPQRSTUV

WXYZ

The script in Listing 3.4 displays a message in the command window by using the Write and WriteLine methods of the StdOut TextStream object.

Example 3.4. Using the Write and WriteLine Methods to Display Messages in the Command Window

 1 Set objNetwork = Wscript.CreateObject("Wscript.Network")
 2 Set objStdOut = WScript.StdOut
 3 objStdOut.Write "User: "
 4 objStdOut.Write objNetwork.UserDomain
 5 objStdOut.Write ""
 6 objStdOut.Write objNetwork.UserName
 7 objStdOut.WriteBlankLines(1)
 8 objStdOut.WriteLine objNetwork.ComputerName
 9 objStdOut.Write "Information retrieved."
10 objStdOut.Close

The following is output from the script in Listing 3.4:

User: FABRIKAMkenmyer

atl-wk-01
Information retrieved.

By contrast, here is what output from the same script would look like if Wscript.Echo were substituted for the StdOut methods:

User:
FABRIKAM

kenmeyer

atl-wk-01
Information retrieved.

Reading Input by Using StdIn

One way to provide a script with input is to use arguments (discussed later in this chapter). For example, the following command runs a script named DeleteUser.vbs, passing as an argument the name of the user account to be deleted:

cscript DeleteUser.vbs kenmyer

Arguments provide a quick and easy way to add input to a script. On the other hand, arguments require the user running the script to know which arguments need to be supplied and to know how to supply them. Instead of requiring users to memorize the syntax for your scripts, you can prompt users to supply the correct information after the script has started. For example:

C:Scriptscscript DeleteUser.vbs
Please enter the name of the user account to be deleted: _

StdIn can be used to read information entered at the command line. StdIn includes the methods and properties shown in Table 3.5.

Table 3.5. StdIn Methods and Properties

Method/Property

Description

Read

Reads the specified number of characters and then stops. For example, the following reads and echoes 3 characters at a time from StdIn until the entire line has been read:

Do Until Wscript.StdIn.AtEndOfLine
    strInput = Wscript.StdIn.Read(3)
    Wscript.Echo strInput
Loop

If StdIn consists of the string “abcdefghijklmnopqrstuvwxyz”, output from the script will look like this:

abc
def
ghi
jkl
mno
pqr
stu
vwx
yz

ReadLine

Reads one line from StdIn and then stops before reaching the newline character. ReadLine is particularly useful for reading input typed by users because it reads all the characters typed by the user before he or she pressed ENTER:

strInput = Wscript.StdIn.ReadLine
Wscript.Echo strInput

If StdIn consists of the string “abcdefghijklmnopqrstuvwxyz”, output from the script will look like this:

abcdefghijklmnopqrstuvwxyz

ReadLine is also useful for reading the output generated by a spawned command-line tool. For more information about this, see “Running Programs” later in this chapter.

ReadAll

Used only for reading the output generated by a spawned command-line tool, batch file, or shell command.

Skip

Skips the specified number of characters and then stops. For example, this script skips the first 23 characters in StdIn and then reads any remaining characters one at a time:

Wscript.StdIn.Skip(23)
Do Until Wscript.StdIn.AtEndOfLine
    strInput = Wscript.StdIn.Read(1)
    Wscript.Echo strInput
Loop

If StdIn consists of the string “abcdefghijklmnopqrstuvwxyz”, output from the script will look like this:

x
y
z

SkipLine

Used to skip a line when reading the output generated by a spawned command-line tool, batch file, or shell command.

AtEndOfLine

Boolean value indicating whether the end of a line has been reached. When the Read method is used to retrieve input typed by a user, this property — when True — informs the script that the entire line has been read.

Do Until Wscript.StdIn.AtEndOfLine
    strInput = Wscript.StdIn.Read(1)
    Wscript.Echo strInput
Loop

AtEndOfStream

Boolean value indicating whether the end of the stream has been reached. Used only for reading the output generated by a spawned command-line tool, batch file, or shell command.

You can use StdIn to retrieve information from the user. To obtain input from the user, you do the following:

  1. Use the Write method to display a prompt on screen (such as, “Please enter your name:”). The Write method is used to ensure that the prompt and the response are included on the same line. Your screen will look similar to this, with the underscore representing the command prompt cursor:

    C:Scripts> Please enter your name: _
    
  2. Use the ReadLine method to read anything the user types at the command prompt, and store this information in a variable. Information is read until the user presses ENTER. For example, if you type Ken Myer and then press ENTER, the value Ken Myer will be stored in the variable.

    ReadLine can read as many as 254 characters typed at the command prompt.

For instance, suppose you create a script that converts numbers from decimal to hexadecimal. You need to prompt the user to enter the decimal value to convert. The script in Listing 3.5 uses the ReadLine method of the WScript StdIn object to retrieve a decimal value from the user and store it in the strDecimal variable. The VBScript Hex function is used to convert the decimal value to hexadecimal, and the WriteLine method of the WScript StdOut object is used to display the results.

Example 3.5. Convert Between Decimal and Hexadecimal

1 Wscript.StdOut.Write "Enter a Decimal Number: "
2 strDecimal = Wscript.StdIn.ReadLine
3
4 Wscript.StdOut.WriteLine strDecimal & " is equal to " & _
5     Hex(strDecimal) & " in hex. "

When run under CScript, the interaction between the script and the user looks similar to this:

C:Scripts>cscript test.vbs
Microsoft (R) Windows Script Host Version 5.6
Copyright (C) Microsoft Corporation 1996-2001. All rights reserved.

Enter a Decimal Number: 256
256 is equal to 100 in hex.

Working with Command-Line Arguments

Command-line arguments are values that you enter at the command line when you run a script. If you have worked with command-line tools, you are already familiar with the concept of arguments. For example, when you run Ping.exe, you must supply, at a minimum, the name or the Internet Protocol (IP) address of the computer being pinged as a command-line argument:

ping 192.168.1.1

Providing values to a script by means of the command line is convenient in cases in which you expect the values to be different each time the script runs. If the values were hard coded within the script, changing the values would require you to edit the script. Using command-line arguments saves time in these cases.

Again, this is no different from working with command-line tools. Ping.exe uses command-line arguments to provide flexibility: This way, you can attempt to ping any computer anywhere. If Ping.exe did not accept command-line arguments, you would either need a separate version of the program for each computer you wanted to ping, or you would have to edit and recompile the source code any time you wanted to ping a different computer.

Command-line arguments are stored in the WshArguments collection, which you access through the Arguments property of the WScript object as shown in Figure 3.8. In addition to storing all command-line arguments in the WshArguments collection, WSH automatically filters each argument into the WshNamed or WshUnnamed collection based on the format of the argument.

WSH Command-Line Argument Collections

Figure 3.8. WSH Command-Line Argument Collections

How Command-Line Arguments Are Stored and Filtered

When you run a script with command-line arguments, the WSH runtime stores these values in a location in memory represented by the WshArguments collection. The WSH runtime stores the arguments in the WshArguments collection in the order in which they were entered on the command line: WScript.Arguments.Item(0) contains the first command-line argument, WScript.Arguments.Item(1) contains the second argument, and so on.

In addition to storing all command-line arguments in the WshArguments collection, WSH automatically filters the command-line arguments into one of two subcollections: WshNamed or WshUnnamed. Arguments that conform to /name:value format are stored in the WshNamed collection, and arguments that do not follow the /name:value format are stored in the WshUnnamed collection. The purpose behind filtering arguments will become clear later in this section.

For example, the following command runs the ServerStats.vbs script and passes three command-line arguments to the script: /s:atl-dc-01, /u:admin, and perf.

cscript serverstats.vbs /s:atl-dc-01 /u:admin perf

The command-line arguments follow the script name and are separated by one or more spaces. Because of this, command-line arguments that contain white space must be enclosed in quotation marks to be treated as a single argument. For example, in this command, the third argument contains a blank space. As a result, the entire argument must be enclosed in quotation marks:

cscript serverstats.vbs /s:atl-dc-01 /u:admin "perf monitor"

Without the quotation marks, perf and monitor would be treated as separate arguments.

Figure 3.9 illustrates the contents of the three argument collections having run status.vbs with the three command-line arguments /s:atl-dc-01, /u:admin, and perf.

Mechanics of the WSH Argument Collections

Figure 3.9. Mechanics of the WSH Argument Collections

All command-line arguments are stored in the WshArguments collection exactly as they are typed on the command line. Count and Length return the total number of command-line arguments entered on the command line.

The WshNamed filtered collection contains the two named arguments. Named arguments are arguments that consist of two parts: a name and a value. The name must be prefaced with a forward slash, and a colon must separate the name from the value. The slash prefix and the colon separator are fixed and cannot be changed. For example, you cannot use a hyphen in place of the slash; the following command will not pass Server as a named argument; instead, it will treat -Server:atl-dc-01 as the value of a single unnamed argument:

cscript serverstats.vbs -Server:atl-dc-01

If you examine Figure 3.9 closely, you will see that named arguments are modified before they are stored in the WshNamed collection. The name portion of the argument becomes the index, or key, and is used with the WshNamed Item property to identify the argument to retrieve. The name is also used with the WshNamed Exists method to check whether a named argument was provided to the script at run time. The slash prefix and the colon separator are discarded, and only the value portion of the named argument is stored in the Item property of the WshNamed collection. Like WshArguments, the WshNamed Count method and Length property return the number of filtered arguments in the WshNamed collection.

The WshUnnamed filtered collection contains the one unnamed argument: perf. The WshUnnamed Count method and Length property return the number of filtered arguments in the WshUnnamed collection.

There are three ways to access command-line arguments:

  • You can access the entire set of arguments using the WshArguments collection.

  • You can access the arguments that have names using the WshNamed collection.

  • You can access the arguments that have no names using the WshUnnamed collection.

Using the Default Arguments Collection

As described in the preceding topic, all command-line arguments are stored in the default arguments collection, WshArguments. The WshArguments collection is accessed through the WScript Arguments property. WshArguments provides two methods and four properties to read and work with command-line arguments.

Methods

Count. Returns the total number of arguments in the WshArguments collection.

ShowUsage. Echoes usage instructions about the script. This is constructed from information provided in the <runtime> section of a Windows Script File (.wsf). Windows Script Files are not discussed in this book.

Properties

Named. Provides access to the WshNamed collection. WshNamed is a filtered collection of arguments, where each argument conforms to the following format: /name:value. Command-line arguments that conform to the /name:value format are called named arguments in WSH.

Unnamed. Provides access to the WshUnnamed collection. WshUnnamed is a filtered collection of arguments, drawn from WshArguments, that do not conform to the /name:value format. Windows Script Host refers to these as unnamed arguments.

Length. Returns the total number of arguments in the WshArguments collection.

Note

Note

You might have noticed that the Length property is identical to the Count method. Length was provided to maintain a level of consistency with the ECMAScript Language Specification, Standard ECMA-262, which JScript is based on. Although either Count or Length can be used to determine the number of arguments passed to VBScript, you must use Length with JScript. Any attempt to use Count with JScript will result in a run-time error.

Item(n). Retrieves the element from the WshArguments collection that corresponds to the index number enclosed in parentheses.

The following command runs a fictitious script named GetEvents.vbs with three arguments:

cscript getevents.vbs atl-dc-01 "Directory Service" 1130

WSH stores the three arguments in the WshArguments collection in the order in which they were entered, and exactly as they were typed on the command line. To read the three arguments, you use the WScript Arguments property in combination with the WshArguments Item property as shown here:

ServerName = WScript.Arguments.Item(0)
EventLog = WScript.Arguments.Item(1)
EventID = WScript.Arguments.Item(2)

Because collections are zero-based, WScript.Arguments.Item(0) points to the first argument in the WshArguments collection. WScript.Arguments.Item(1) points to the second argument, which is enclosed inside quotation marks because the argument, Directory Services, contains a space. Omitting the quotation marks would cause the two words to be treated as separate arguments, which would lead to errors because incorrect values would be assigned to the EventLog and EventID variables. WScript.Arguments.Item(2) points to the third argument.

The collection looks like the one shown in Table 3.6.

Table 3.6. Sample WSH Arguments Collection

Item

Value

0

atl-dc-01

1

Directory Service

2

1130

If the fictitious script employed additional arguments, WScript.Arguments.Item(3) would point to the fourth argument, WScript.Arguments.Item(4) would point to the fifth argument, and so on.

There is no fixed limit on the number of arguments that can be stored in the WshArguments collection. However, the entire command line, which includes the host name, host options, script name, and script arguments, cannot exceed the maximum command-line length. Exceeding the maximum command-line length generally is not a problem unless you use the WSH Drag and Drop feature to populate WshArguments. The maximum command-line length also applies to WSH Drag and Drop.

You can reduce the amount of typing necessary to access each argument by setting a reference to the WshArguments collection by way of the WScript Arguments property. Use the VBScript Set keyword followed by the variable name you want to use to access the WshArguments collection. Set is required because collections are standard COM objects. The following example is functionally equivalent to the preceding example despite some syntactical differences.

Set args = WScript.Arguments
ServerName = args.Item(0)
EventLog = args.Item(1)
EventID = args.Item(2)

The previous three examples assume GetEvents.vbs is always passed three command-line arguments. While omitting any kind of argument verification might be OK for a quick ad hoc script, failing to perform some level of verification can lead to error-prone scripts, especially when the script is shared with other users.

The following command runs the fictitious GetEvents.vbs script without any arguments.

cscript getevents.vbs

Running GetEvents.vbs without arguments using one of the three previous examples would result in the following run-time error:

C:ScriptsGetEvents.vbs(2, 1) Microsoft VBScript runtime error: Subscript out of
range

Any attempt at using the Item property to access an argument that does not exist will result in a Subscript out of range run-time error. Listing 3.6 demonstrates how to use the WshArguments Count method to verify that the correct number of command-line arguments is provided to the script at run time.

Example 3.6. Using Count to Verify the Number of Arguments Used

1 If WScript.Arguments.Count = 3 Then
2     ServerName = WScript.Arguments.Item(0)
3     EventLog = WScript.Arguments.Item(1)
4     EventID = WScript.Arguments.Item(2)
5 Else
6     Wscript.Echo "Usage: GetEvents.vbs ServerName EventLog EventID"
7     Wscript.Quit
8 End If

In Line 1 of Listing 3.6, the script uses the WshArguments Count method to obtain the number of command-line arguments in the WshArguments collection. If the value is equal to 3, the script initializes the ServerName, EventLog, and EventID variables with the three arguments in the WshArguments collection. Otherwise, the script echoes usage instructions and immediately exits.

Unnamed Command-Line Arguments

Unnamed arguments are entered on the command line as values only, without an associated name. At times the order of your arguments might be irrelevant; for example, a script might require you to type in three computer names, and it makes no difference which computer the script runs against first. Otherwise, unnamed arguments must be entered in the order that the script requires.

You might want to use unnamed arguments when your script:

  • Accepts only one or two command-line arguments. If your script accepts only the name of a folder as its sole command-line argument, there is no reason to make this a named argument.

  • Accepts arguments that are all of the same type and are each used in the same way within your script. For example, you might have a script that backs up and clears event logs on all computers whose names are entered as command-line arguments. Because all the arguments are the same (names of servers), and because the order is probably irrelevant, there is no reason to use named arguments.

When you run a script with unnamed command-line arguments, the WSH runtime stores the arguments in the WshArguments collection in the order in which they were entered on the command line. The arguments can then be referenced in the script using index numbers that represent the order in which they were entered. The sequence of index numbers begins with 0.

The script in Listing 3.7 uses the unnamed command-line arguments collection to retrieve and display the command-line arguments provided to it.

Example 3.7. Retrieving Command-Line Arguments Using the Arguments Collection

1 strServer = WScript.Arguments.Item(0)
2 strPacketSize = WScript.Arguments.Item(1)
3 strTimeout = WScript.Arguments.Item(2)
4
5 Wscript.Echo "Pinging Server: " & strServer
6 Wscript.Echo "Packet Size: " & strPacketSize
7 Wscript.Echo "Timeout: " & strTimeout

The following command runs the script with the command-line arguments entered in the order required by the script.

EchoUnnamedArgs.vbs DCServer01 100 5000

This command produces the following output:

Pinging Server: DCServer01
Packet Size: 100
Timeout: 5000

This command runs the same script with the command-line arguments entered in the wrong order.

EchoUnnamedArgs.vbs 100 DCServer01 5000

This command produces the following output:

Pinging Server: 100
Packet Size: DCServer01
Timeout: 5000

The output from the first command is correct but the output from the second command is incorrect. Because the script is written using unnamed arguments, the order in which the command-line arguments are entered is crucial for the script to work properly. If the script actually tried to run Ping.exe, the procedure would fail because there is no server named 100 and no such thing as a packet size of DCServer01.

Named Command-Line Arguments

As the preceding example showed, the order in which unnamed arguments are entered can make the difference between a script that runs successfully and a script that does not. This places a considerable burden on administrators running the script; not only must they supply the correct arguments, but they must also supply them in the correct order. One mistake will likely cause the script to fail.

To make it easier for people to run scripts that use multiple arguments, you can use named arguments instead. Named arguments are entered on the command line as values with associated names. They can be entered in any order.

A named argument begins with a slash (/), and the name and the value are separated by a colon (:). The following command runs a script named ServerTest.vbs with two named arguments:

ServerTest.vbs /Server:HRServer01 /Timeout:3000

The name of the first argument is Server, and its value is HRServer01. The name of the second argument is Timeout, and its value is 3000. These arguments could also be entered in the reverse order, as follows:

ServerTest.vbs /Timeout:3000 /Server:HRServer01

When you run a script with named command-line arguments, each argument’s name and value are stored in the WshNamed collection.

The script in Listing 3.8 uses the named command-line arguments collection to retrieve and display the command-line arguments it receives in the command window.

Example 3.8. Retrieving Command-Line Arguments Using the Named Arguments Collection

1 Set colNamedArguments = WScript.Arguments.Named
2
3 strServer = colNamedArguments.Item("Server")
4 strPacketSize = colNamedArguments.Item("PacketSize")
5 strTimeout = colNamedArguments.Item("Timeout")
6 Wscript.Echo "Server Name: " & strServer
7 Wscript.Echo "Packet Size: " & strPacketSize
8 Wscript.Echo "Timeout (ms): " & strTimeout

The following command runs the script with three named arguments.

EchoNamedArgs.vbs /Server:HRServer01 /PacketSize:300 /Timeout:8000

This command produces the following output:

Server Name: HRServer01
Packet Size: 300
Timeout (ms): 8000

This command runs the same script with the order of the named arguments changed.

EchoNamedArgs.vbs /Timeout:8000 /PacketSize:300 /Server:HRServer01

This command produces the following output:

Server Name: HRServer01
Packet Size: 300
Timeout (ms): 8000

The output from the first command and the second command is the same. You can enter named arguments in any order without affecting the outcome of the script.

Named arguments are also useful when you want to include optional parameters within your scripts. The script in Listing 3.9 is similar to the script in Listing 3.8, but the PacketSize argument is an optional argument. If the script user does not enter a packet size, the script uses a default packet size.

The script uses the Exists method to check whether the user entered an argument named PacketSize. If the result of the test is True, the script proceeds to line 7. If the result of the test is False, the script proceeds to line 9, where the PacketSize variable is set to 100, using the DEFAULT_PACKET_SIZE constant.

Example 3.9. Retrieving Command-Line Arguments and Using Default Argument Values

 1 Const DEFAULT_PACKET_SIZE = 100
 2
 3 Set colNamedArguments = WScript.Arguments.Named
 4
 5 strServer = colNamedArguments.Item("Server")
 6 If colNamedArguments.Exists("PacketSize") Then
 7     strPacketSize = colNamedArguments.Item("PacketSize")
 8 Else
 9     strPacketSize = DEFAULT_PACKET_SIZE
10 End If
11 strTimeout = colNamedArguments.Item("Timeout")
12
13 Wscript.Echo "Server Name: " & strServer
14 If colNamedArguments.Exists("PacketSize") Then
15     Wscript.Echo "Packet Size :" & strPacketSize
16 Else
17     Wscript.Echo "Packet Size [default]: " & strPacketSize
18 End If
19 Wscript.Echo "Timeout (ms): " & strTimeout

Using Both Unnamed and Named Arguments

In most cases, you should not use both named and unnamed arguments in the same script. Using both types of arguments makes the required command-line syntax more difficult to remember. If your script accepts two or fewer arguments or multiple arguments of the same type, you might want to use unnamed arguments. If your script accepts three arguments of distinct types or has optional arguments, you should probably use named arguments.

Occasionally, however, it can be useful to mix named and unnamed arguments in a script. For example, if you have a script that has one required argument and a number of optional arguments, you can use an unnamed argument for the required argument and named arguments for all of the optional arguments. The following command contains a required server name (HRServer01) plus three optional arguments. In this command, the unnamed argument must be listed first, or the script will likely fail.

CheckServer.vbs HRServer01 /timeout:200 /logfile:serverlog.txt /verbose:true

Verifying Command-Line Arguments

Many of your scripts will require you to enter the required number and types of command-line arguments for the scripts to run correctly. You might need to perform two verifications on command-line arguments:

  • Number of arguments entered is within the acceptable range

  • Required arguments have been entered

Verifying the Number of Arguments Entered

Some scripts require a specific number of command-line arguments. For example, suppose you have a script that copies files from one computer to another. This script likely requires two, and only two, command-line arguments: the name of the source computer and the name of the target computer. Trying to run this script with one command-line argument or with three command-line arguments would not make any sense; there would be no way for the script to correctly determine the source and target computers.

In a situation such as this, one of the first things the script should do is verify that two command-line arguments were entered. This requires little effort on your part because arguments are returned as part of a collection, and collections typically include a Count property. The purpose of the Count property is to tell you how many items are in the collection. If Wscript.Arguments.Count equals 2, two arguments are in the collection.

The script in Listing 3.10 verifies the number of command-line arguments entered. The script accepts up to four arguments, any two of which are optional. Therefore, the user must enter at least two but no more than four arguments.

Example 3.10. Verifying the Number of Command-Line Arguments Entered

1 iNumberOfArguments = WScript.Arguments.Count
2 If iNumberOfArguments >= 2 And iNumberOfArguments <= 4 Then
3     Wscript.Echo iNumberOfArguments & " arguments entered. " & _
4         "This is a valid number."
5 Else
6     Wscript.Echo "Error: invalid number of arguments entered. " & _
7         "Please enter 2, 3, or 4 arguments."
8     Wscript.Quit
9 End If

In line 1 of Listing 3.10, the script uses WScript.Arguments.Count to determine the number of command-line arguments entered. The script stores this value in the iNumberOfArguments variable.

In line 2, the script uses iNumberOfArguments >=2 And iNumberOfArguments <=4 to test whether the user entered 2, 3, or 4 arguments:

  • If the result of the test is True, the script proceeds to line 3 and uses the Echo method to display a message indicating that the correct number of arguments was entered.

  • If the result of the test is False, the script proceeds to line 6 and uses the Echo method to display an error message indicating that the wrong number of arguments was entered.

The following command tries to run the script with more than four arguments.

CheckNumArgs.vbs HRServer01 RASServer01 SQLServer01 1000 300

This command produces the following output:

Error:    invalid number of arguments entered. Please enter 2, 3, or 4 arguments.

The output informs you that you have entered the wrong number of arguments. You are told that you should have entered between two, three, or four arguments instead of the five you entered.

This command runs the script with three arguments, an acceptable number for the script.

CheckNumArgs.vbs HRServer01 1000 300

This command produces the following output:

3 arguments entered. This is a valid number.

The output validates the fact that you have entered the correct number of arguments and that, as a result, the script is proceeding as expected. The script then displays the number of arguments you entered.

Verifying That All Required Arguments Are Entered

When you use command-line arguments in a script, the script should check to ensure that any required arguments have been entered before it proceeds. Unfortunately, WSH does not allow you to specify that an argument is required, and then notify you if the argument is not found. For example, if you run a command-line tool without a required argument, the tool typically will not run; instead, it displays usage instructions. Your scripts can do the same thing, but it is up to you to write code that checks the arguments collection and ensures that all the required arguments are present.

If you need to use required arguments in a script, it is recommended that you use named arguments. One advantage of using named arguments is that you can then use the Exists method to check whether an argument has been provided. For example, this line of code checks whether an argument named FolderName is present in the arguments collection. If the argument exists, the value –1 (True) will be echoed to the screen. Otherwise, the value 0 (False) will be displayed:

Wscript.Echo colNamedArguments.Exists("FolderName")

Suppose you enter the correct number of arguments but omit a required argument. The script in Listing 3.11 builds on the script in Listing 3.10. It uses the WshNamed collection Exists method to ensure that the named argument, Server, is among the arguments that you entered. In line 4, the script uses Not colNamedArgument.Exists(“Server”) to test whether the user has entered the named argument Server:

  • If the test reveals that the argument was not entered, the script proceeds to line 5. There the script uses the Echo method to display a message indicating that the named argument Server is required for the script to run. In line 6, Wscript.Quit is used to stop the script from running because the required argument, Server, was not entered.

  • If the test reveals that the argument was entered, the script proceeds to line 7.

    In lines 9 and 10, the script uses colNamedArguments.Item(“Server”) to retrieve the value of the Server named argument entered by the user. The script then displays a message constructed from this value. The message tells the user the value they entered for the Server named argument.

Example 3.11. Verifying That All Required Arguments Are Entered

 1 iNumberOfArguments = WScript.Arguments.Count
 2 Set colNamedArguments = WScript.Arguments.Named
 3
 4 If Not colNamedArguments.Exists("Server") Then
 5     Wscript.Echo "Usage: /Server:<servername> is required."
 6     Wscript.Quit
 7 ElseIf iNumberOfArguments >= 2 Or iNumberOfArguments <= 4 Then
 8     Wscript.Echo iNumberOfArguments & " arguments entered"
 9     Wscript.Echo "including Server Name: " & _
10         colNamedArguments.Item("Server")
11 Else
12     Wscript.Echo "Usage: Please enter between 2 and 4 arguments."
13     Wscript.Quit
14 End If

The following command tries to run the script without including the Server named argument.

CheckReqArgs.vbs 1000 300

This command produces the following output:

Usage: /Server:<servername> is required.

The output reflects the fact that you have not entered the Server named argument, which is required, by displaying a usage message.

This command runs the script with the required argument and an acceptable number of arguments.

CheckReqArgs.vbs /Server:HRServer01 1000 300

This command produces the following output:

3 arguments entered
including Server Name: HRServer01

The output reflects the fact that you have entered the correct number of arguments, including the required Server named argument. The script displays the value entered for the Server argument to reinforce the fact that this required argument was actually entered.

Controlling How a Script Runs

When you run a script, the commands in the script are typically executed in the order in which they appear in the script file. There are no pauses in the running of the script, and the script ends only after the last command is completed.

The WScript object provides two properties (Timeout and Interactive) and two methods (Sleep and Quit) that enable you to alter how a script runs. A script can use these properties and methods to cause itself to:

  • Pause for a specified length of time.

  • Immediately stop running.

  • Stop running after a specified length of time.

Pausing a Script

Most of the time, WSH attempts to complete a script as quickly as possible; as soon as WSH finishes executing the first line of code, it begins executing the second line of code. More often than not, this is the desired behavior: In general, the faster a script can complete its tasks, the better.

There are times, however, when you do not want a script to run as quickly as possible. For example, suppose you need to monitor processor use on a computer. To do this, you might want to record processor use every 10 seconds until you have collected 100 measurements. In this case, you do not want the script to take these 100 measurements as quickly as it can. Instead, you want it to take the first measurement and then wait 10 seconds before taking the second measurement.

A script can force itself to pause by calling the WScript Sleep method. The Sleep method accepts a single parameter that indicates how long, in milliseconds, to pause the script. (There are 1,000 milliseconds in a second and 60,000 milliseconds in a minute.)

You will need to use the Sleep method in your scripts when:

  • Your script must wait for an event to occur before continuing to run.

  • You want your script to periodically check the status of a system parameter (for example, checking processor usage every 10 seconds).

  • Your script is interacting with a user and you want to slow the interaction to a usable speed. For example, when displaying events retrieved from an event log, you might want to insert a pause, giving the user time to read the first event before displaying the next event.

The script in Listing 3.12 checks the amount of free space on all the disk drives on a computer. The script then pauses for 5 minutes (300,000 milliseconds) and then checks free disk space again.

Example 3.12. Using the Sleep Method to Pause a Script

 1 strComputer = "."
 2 Set objWMIService = GetObject("winmgmts:" _
 3     & "{impersonationLevel=impersonate}!\" & strComputer & "
ootcimv2")
 4 Do While True
 5     Set colDisks = objWMIService.ExecQuery _
 6         ("SELECT * FROM Win32_LogicalDisk")
 7     For Each objDisk in colDisks
 8         Wscript.Echo "DeviceID: " & objDisk.DeviceID
 9         Wscript.Echo "Free Disk Space: " & objDisk.FreeSpace
10     Next
11     Wscript.Sleep 300000
12 Loop

Note

Note

The preceding script is designed to run indefinitely. To stop the script, you will need to terminate the process under which it is running.

Quitting a Script

In general, scripts begin by executing line 1 and continue to run until each line in the script has been executed. (This is not entirely true, but it will do for the purposes of this discussion. In reality, some lines of code — such as those in a particular function — might be skipped rather than executed.) After each line of code has been executed, the script automatically terminates itself. For example, this simple little script echoes three numbers to the screen and then quits. The script does not include any special instructions telling it to quit; it automatically terminates after executing the last line:

Wscript.Echo "1"
Wscript.Echo "2"
Wscript.Echo "3"

Most of the time, this is exactly what you want a script to do. However, there might be occasions when you want a script to terminate before every line of code has been executed. For example, suppose you have a script that requires two command-line arguments: a source computer and a target computer. At the very beginning of the script, you check to see whether two (and only two) arguments have been supplied. If they have, the script continues.

But what if they have not? What if the user supplied only one argument, or what if the user supplied four arguments? In that case, there is no reason to proceed; after all, the script is likely to fail and could leave one or more computers in an indeterminate state. In this case, the best course of action might be to echo a message stating that the user must supply two command-line arguments, and then immediately terminate the script, regardless of how many lines of code remain unexecuted.

A script can force itself to stop running before it reaches its last command by calling the Quit method. This can be done using a single line of code:

Wscript.Quit

When a script executes the Quit method, it immediately terminates. For example, this script echoes three messages and then quits. The last three Wscript.Echo commands will never execute:

Wscript.Echo "1"
Wscript.Echo "2"
Wscript.Echo "3"
Wscript.Quit
Wscript.Echo "4"
Wscript.Echo "5"
Wscript.Echo "6"

Setting a Time-out Value for a Script

It is good practice to set the time-out value on any script, but it is particularly important if the script is liable to run into issues that might force it to run indefinitely.

For example, suppose you have a script that must first connect to a remote computer. What if this connection cannot be made? In that case, you might include code that tells the script to wait 60 seconds after a failed connection and then try again. The script will continue in this loop until the connection is made.

But what if the remote computer has been permanently removed from the network? In that case, the script could theoretically run forever, taking up computer processing cycles and using up network bandwidth as it tries to make a connection that can no longer be made. In a situation such as that, it might be beneficial to set the Timeout property. This instructs the script to run only for a set amount of time. If it cannot complete its task when the time period expires, the script simply terminates.

The WScript Timeout property sets a time period, in seconds, after which a script will stop running. (By default, WSH does not time out the running of a script.) Setting a Timeout ensures that no script will run forever.

The script in Listing 3.13 sets a time-out value of 5 seconds. If the script has not finished executing 5 seconds after it starts, the script will automatically terminate. Because the script includes a 60-second pause (line 2), the timeout will always expire before the script completes. As a result, line 3, which echoes a message to the screen, will never run.

Example 3.13. Setting the Time-out Value of a Script

1 Wscript.Timeout = 5
2 Wscript.Sleep 60000
3 Wscript.Echo "Script is finished."

Obtaining WSH Environment Information

The WScript object has a number of properties that provide information about the script host being used and the script being run. This metadata includes the information shown in Table 3.7.

Table 3.7. WSH Environment Properties

Property

Description

ScriptFullName

Full path of the currently running script (for example, C:ScriptsMonitor.vbs).

ScriptName

File name of the currently running script (for example, Monitor.vbs).

Version

WSH version number (for example, 5.6).

Build

WSH build number. The full version number of Windows Script Host consists of the product release version number followed by the build version number. For example, if the Windows Script Host product release version number is 5.6, and the build version number is 6626, the full version number is 5.6.6626.

The build number is perhaps most useful if you are testing beta copies of WSH. Otherwise, the version number will typically suffice.

Name

Always returns Windows Script Host.

FullName

Full path to the script host (either Wscript.exe or CScript.exe).

Path

Full path to the folder where the script host is located. Does not include the file name (Wscript.exe or Cscript.exe).

This metadata can be extremely useful. For example, the version number can be used to verify that a computer has the correct version of WSH installed; if you have a script that requires WSH 5.6, the script can first check the version number and then terminate if Version is not 5.6. For example:

If Wscript.Version <> "5.6" Then
    Wscript.Echo "This script must be run under WSH 5.6."
    Wscript.Quit
End If

Likewise, you might have a script that makes extensive use of Wscript.Echo. In that case, you probably do not want to run the script under WScript; this can result in hundreds of message boxes that must be responded to. Consequently, your script might check to see which script host it is running under and then terminate if the host is not CScript.

You can determine the script host by using the FullName property and using the VBScript function Right to check the last 11 characters. (To ensure that the check is done correctly, you should also convert these 11 characters to uppercase using the UCase function.) If the last 11 characters equal WSCRIPT.EXE, the script is running under WScript. If the last 11 characters are CSCRIPT.EXE, the script is running under CScript.

Note

Note

Why the last 11 characters? The value of ScriptFullName will vary depending on where WSH has been installed; it might be C:WinntSystem32CScript.exe, or it might be E:WindowsSystem32Wscript.exe. However, the last 11 characters in the path will always be either Wscript.exe or Cscript.exe.

For example, the following script snippet checks the script host and then displays a message and terminates if the host is WSCRIPT.EXE:

If UCase(Right(Wscript.FullName, 11)) = "WSCRIPT.EXE" Then
    Wscript.Echo "This script must be run under CScript."
    Wscript.Quit
End If

The script in Listing 3.14 displays the available WSH environment properties by retrieving them and echoing them to the screen.

Example 3.14. Displaying Information About the WSH Environment

1 Wscript.Echo "Script Full Name: " & Wscript.ScriptFullName
2 Wscript.Echo "Script Name: " & Wscript.ScriptName
3 Wscript.Echo "Version: " & WScript.Version
4 Wscript.Echo "Build: " & Wscript.BuildVersion
5 Wscript.Echo "Name: " & Wscript.Name
6 Wscript.Echo "Full Name: " & Wscript.FullName
7 Wscript.Echo "Path: " & Wscript.Path

The following is typical output from the script:

Script Full Name: C:scriptswsh_info.vbs
Script Name: wsh_info.vbs
Version: 5.6
Build: 6626
Name: Windows Script Host
Full Name: C:WINDOWSSystem32CScript.exe
Path: C:WINDOWSSystem32

Handling Events

Windows is an event-based operating system. Events generated within Windows are often the result of a user action such as the click of an OK button or the movement of a mouse. Typically, most of the actions performed by a Windows program are the result of handling these user-generated events. For example, when you start an application such as Microsoft Word, the application loads and then waits for you to do something (for example, type something on the keyboard or select something with the mouse). Word will wait indefinitely until you trigger an event that it can respond to.

The event mechanism also enables software components to communicate with one another. When an occurrence takes place in one component (firing component), another component (handling component) is notified of the occurrence. The handling component can respond by performing some action.

Event handling mechanism used by WSH is not commonly employed in system administration scripts. Scripts tend to be procedure-driven; that is, after they have been set in motion, they run on their own, neither looking for nor responding to outside events.

Note

Note

The ability to monitor resources and respond to changes in these resources is extremely important. However, this type of event handling is best done using WMI.

Event handling can be important in scripts that automate Windows GUI applications, and such scripts are sometimes useful to system administrators. For example, WSH scripts can use Microsoft Internet Explorer to provide a GUI interface for users. Simple examples of this can be found in the chapter “Creating Enterprise Scripts” in this book.

WshShell Object

The shell is the component of Windows that presents users with an interface to the operating system and provides familiar elements of the Windows desktop experience, including Windows Explorer, the Start menu, shortcuts, and desktop themes.

The WshShell object gives your scripts the ability to work with the Windows shell. Your scripts can use the WshShell object to perform a number of system administration tasks, including running programs, reading from and writing to the registry, and creating shortcuts.

Figure 3.10 shows the properties and methods of the WshShell object.

WshShell Object Model

Figure 3.10. WshShell Object Model

Accessing the WshShell Object

The WshShell object is a COM object, and using the following code statement can create an instance of the object:

Set objShell = WScript.CreateObject("WScript.Shell")

WshShell Capabilities

The WshShell object enables your script to automate tasks in a number of categories related to the Windows shell. Table 3.8 lists these categories along with the methods and properties of the WshShell object that your scripts can use to access this functionality.

Table 3.8. Capabilities Provided by the WshShell Object

Category

Method or Property

Running Programs

Run, Exec

Working with Special Folders

SpecialFolders

Working with Shortcuts

CreateShortcut

Working with Environment Variables

Environment, ExpandEnvironmentStrings

Working with the Event Log

LogEvent

Working with the Registry

RegRead, RegWrite, RegDelete

Sending Keystrokes to an Application

AppActivate, SendKeys

Obtaining a Script’s Current Directory

CurrentDirectory

Creating Timed Dialog Boxes

Popup

Running Programs

One of the most important lessons to learn about scripting is this: Scripting is not the answer to all your system administration needs.

For one thing, scripting does not provide 100 percent coverage for all the administrative tasks that can be performed under Windows. For example, you can use scripts to create shared folders. But what if you want to set the offline folder options for that share? This procedure cannot be scripted using WSH or WMI. Instead, you must configure offline folder options using either the GUI or the Net.exe command.

For another, there is no reason to write a script if a tool already exists that fills your needs. Suppose you want to see the list of files that are stored in a folder on the local computer. You can spend the time creating a script to return this information. Alternatively, you can simply type dir at the command prompt and be done with it.

Using WSH scripts to help automate tasks does not require that you abandon the command-line tools or batch scripts that you currently use. If you have a batch script that performs well at a given task, there is no reason to create a WSH script that performs the same task.

On the other hand, you might want to augment the capabilities of a batch script with capabilities available only in the WSH scripting environment. For example, you might have a disk cleanup utility that you want to run only if free disk space falls below a specified level. It is possible to create a batch file that can determine free disk space and then decide whether to run the cleanup utility; however, the procedure for creating such a batch file is not particularly straightforward. By contrast, this same task can be accomplished quite easily in WSH: You can use the FileSystemObject or WMI to determine the free disk space and then either run or not run the disk cleanup utility.

One of the advantages of using WSH is that you do not have to choose WSH or choose batch files and command-line tools. The two approaches are not at odds with one another; instead, they complement one another. For example, you can use the WshShell Run and Exec methods in scripts to incorporate existing batch files or command-line tools.

Although running command-line tools and running batch scripts are important uses of the Run and Exec methods, these methods are not restricted to running a particular type of program. The Run and Exec methods enable your scripts to run any Windows program, including GUI applications. In addition, you can also run other scripts from within a script.

Comparing Run and Exec

The fact that there are two ways to run programs from a script leads to an obvious question: which method should you use in your scripts? The answer to that question depends on the script and what it needs to accomplish.

A script can use either the Run method or the Exec method to run a program in a manner similar to using the Run dialog box from the Start menu. Regardless of the method used, the program starts, and runs in a new process.

However, when you use the Run method, your script will not have access to the standard input, output, and error streams generated by the program being run. A script cannot use the Run method to run a command-line tool and retrieve its output.

For example, suppose you want to run Ping.exe and then examine the output to see whether the computer could be successfully contacted. This cannot be done using the Run command. Instead, you would need to ping the computer, save the results of the ping command to a text file, open the text file, read the results, and then parse those results to determine the success or failure of the command.

The following script uses the Run method to call Ping.exe, redirecting the output to a temporary file. The script opens and reads the text file, checks to see whether the command succeeded (by determining whether any of the lines of output begin with the word Reply), and then closes and deletes the temporary file:

Set objFSO = Wscript.CreateObject("Scripting.FileSystemObject")
Set objShell = Wscript.CreateObject("Wscript.Shell")
objName = objFSO.GetTempName
objTempFile = objName
objShell.Run "cmd /c ping -n 3 -w 1000 157.59.0.1 >" & objTempFile, 0, True
Set objTextFile = objFSO.OpenTextFile(objTempFile, 1)
Do While objTextFile.AtEndOfStream <> True
    strText = objTextFile.ReadLine
    If Instr(strText, "Reply") > 0 Then
        Wscript.Echo "Reply received."
        Exit Do
    End If
Loop
objTextFile.Close
objFSO.DeleteFile(objTempFile)

Although this approach works, it is somewhat complicated. If you need access to command-line output, you should use the Exec method instead. The following script also parses the output generated by Ping.exe. However, it does so by using the Exec method and by directly reading the output. There is no need to create, open, read, and delete a temporary file, and the script is only 9 lines long, compared with the 15 lines required to perform this same task using the Run method:

Set objShell = WScript.CreateObject("WScript.Shell")
Set objExecObject = objShell.Exec("cmd /c ping -n 3 -w 1000 157.59.0.1")
Do While Not objExecObject.StdOut.AtEndOfStream
    strText = objExecObject.StdOut.ReadLine()
    If Instr(strText, "Reply") > 0 Then
        Wscript.Echo "Reply received."
        Exit Do
    End If
Loop

In many respects, this makes the Exec method a better choice than the Run method. However, the Run method is still useful in a number of situations:

  • You might want to run the application in a specified window type, such as a minimized window. Exec offers no control over window style; Run offers the options listed in Table 3.9.

    Table 3.9. Integers Accepted by the Run Method for the Window Style

    Integer

    Window Style Description

    0

    Hides the window and activates another window.

    1

    Activates and displays a window. If the window is minimized or maximized, the system restores it to its original size and position. An application should specify this flag when displaying the window for the first time.

    2

    Activates the window and displays it as a minimized window.

    3

    Activates the window and displays it as a maximized window.

    4

    Displays a window in its most recent size and position. The active window remains active.

    5

    Activates the window and displays it in its current size and position.

    6

    Minimizes the specified window and activates the next top-level window in the Z order. The Z order is nothing more than the list detailing the order in which windows are to be activated. If you press ALT+TAB, you will see a graphical representation of the Z list.

    7

    Displays the window as a minimized window. The active window remains active.

    8

    Displays the window in its current state. The active window remains active.

    9

    Activates and displays the window. If the window is minimized or maximized, the system restores it to its original size and position. An application should specify this flag when restoring a minimized window.

    10

    Sets the show-state based on the state of the program that started the application.

  • You might need to run a script on computers that do not have WSH 5.6 installed. Exec is supported only on WSH 5.6.

  • You might want to wait for the application being called to finish running before the script resumes. This can be done with either Run or Exec but requires less coding with Run.

Running Programs

The Run method accepts three parameters. The first and only required parameter is the name of the program you want to run. If the program is in the same folder as the script, or if it is located within the computer path, you need enter only the name (for example, Calc.exe). Otherwise, enter the full path to the program (C:AdminMonitoringDiskSpace.exe).

The second parameter is an integer that indicates the window style with which the program should begin (assuming the program has a window). The window style determines such things as whether a window will be the active window or be maximized. Table 3.9 lists the integers that Run accepts as a second parameter as well as the corresponding the window styles.

For example, the script in Listing 3.15 starts Notepad. In line 1, the script sets the MAXIMIZE_WINDOW constant to 3, which represents an activated and maximized window style. In line 3, the script uses the WshShell Run method to start Notepad, passing it the MAXIMIZE_WINDOW constant so that the program runs in a maximized window.

Example 3.15. Running a Program Using the Run Method

1 Const MAXIMIZE_WINDOW = 3
2 Set objShell = WScript.CreateObject("WScript.Shell")
3 objShell.Run "notepad.exe", MAXIMIZE_WINDOW

Note

Note

Not all applications respond to the window style options. For example, Control Panel (Control.exe) always opens in the same way, regardless of the window style specified in the script.

The Run method also accepts a Boolean value as a third parameter that determines whether the script pauses until the called program is finished running or instead continues with the next command in the script. If this value is set to False (the default), the Run method simply issues the command to run the program but does not check to ensure that the program actually ran. If the third parameter is set to True, the script will wait for the program to finish running, return the integer exit code provided by the program, and then continue with the next line of the script.

If you set this value to False, you can run multiple programs at the same time; the script will start program A and then immediately start program B, even though program A is still running. This can enable your scripts to complete faster. However, it can also lead to possible problems: For example, what if program B cannot be run until program A has finished? If you are worried about possible “collisions” between programs, set this value to True. In that case, program B will not start until program A has concluded.

For example, this script runs Calculator and then waits until Calculator has been closed before proceeding. If Calculator is never closed, line 3 of this script will never execute:

Set objShell = WScript.CreateObject("WScript.Shell")
objShell.Run("calc.exe"),1,True
Wscript.Echo "Script completed."

Note

Note

WSH keeps track of the specific instance of a program started using Run. For example, suppose you run the preceding script and then manually start a second instance of Calculator. If you close this second instance, the script will not respond. Instead, it will continue to wait until the initial instance of Calculator, the one started using Run, has been closed.

Running Command-Line Tools

Although both Run and Exec are well suited for running GUI programs from scripts, you are unlikely to call many GUI programs from within a script. After all, the basic idea behind most system administration scripts is to carry out a task without the need for any human intervention. Because GUI applications typically require human intervention, about the best you can do within a script is open the application; you are very limited in what you can do with the application after it has been opened.

This is not true for command-line tools, however. Most command-line tools are designed to run in automated fashion; after they have been started, there is no need for any human intervention. The tools start, perform their appointed task, and then terminate.

Both the Run method and the Exec method can be used to run command-line tools, although in either case you should use a slightly different syntax from the one used to run GUI tools. When you run a command-line tool using either of these methods, you should always preface the tool name with one of the following:

  • %comspec% /k

  • %comspec% /c

The %comspec% variable is an environment variable that specifies the command-line processor. By using %comspec%, you can create scripts that run on both Windows 98 computers (where the command-line processor is Command.exe) and on Windows 2000 computers (where the command-line processor is named Cmd.exe).

The %comspec% variable is not required; however, it does provide a way for a the command window in which a tool runs to remain on the screen. (By default, a command window opens, the tool runs, and then the command window closes as soon as the tool finishes. This means that you might not have time to view the output generated by that tool.)

Including %comspec% is also the only way to run command-line commands such as dir. This script will not run the dir command. Instead, you will receive an error message stating that dir could not be found. (This is because dir is not a stand-alone tool; there is, for example, no program named dir.exe.)

Set objShell = WScript.CreateObject("WScript.Shell")
objShell.Run("dir"), 1, True

However, this script, which first starts the command interpreter, will run the dir command:

Set objShell = WScript.CreateObject("WScript.Shell")
objShell.Run("%comspec% /K dir"), 1, True

The /k and /c parameters allow you to specify whether the command window will remain open after the script completes or whether it will be closed. If you want the window to remain open so that you can view the script output, use the /k parameter. If you want the window to close (as you might with a logon script), use the /c parameter.

For example, the following script runs the Cacls.exe tool, which, in this instance, displays permission settings for the folder C:Scripts. The script leaves the command window open so that the results can be viewed:

Set objShell = WScript.CreateObject("WScript.Shell")
objShell.Run("%comspec% /K cacls.exe c:scripts"), 1, True

By contrast, this script runs the Sc.exe tool and stops the Alerter service. As soon as the script completes, the command window closes:

Set objShell = WScript.CreateObject("WScript.Shell")
objShell.Run("%comspec% /c sc.exe stop alerter"), 1, True

Using Spaces in Command-Line Parameters

If a parameter passed to a command-line tool includes a space, that parameter must be enclosed in quotation marks. For example, to use Sc.exe to determine the keyname for the Upload Manager service, you need to use the following syntax:

sc.exe getkeyname "Upload Manager"

To use this same command within a script, you must also include quotation marks around the parameter. However, this is not entirely straightforward. For example, you might try placing quotation marks around Upload Manager, like this:

Set objShell = WScript.CreateObject("WScript.Shell")
objShell.Run("%comspec% /k sc.exe getkeyname "Upload Manager""), 1, True

When you run this script, you do not get the keyname for Upload Manager. Instead, you get the error message shown in Figure 3.11.

Incorrectly Specifying the Run Parameter

Figure 3.11. Incorrectly Specifying the Run Parameter

At first, this error message might seem nonsensical; after all, it says that the script expected to see a right parenthesis, and your code has a right parenthesis. As it turns out, though, the problem lies not with the parentheses but with the quotation marks. WSH correctly sees the first set of quotation marks (the ones right before %comspec%) as marking the start of the command string being passed to the Run method. However, it sees the second set of quotation marks (the ones right before the word Upload) as marking the end of the command string. To WSH, this is the command you are trying to execute:

objShell.Run("%comspec% /k sc.exe getkeyname ""

Because the syntax is not correct (a right parenthesis is required immediately after the second set of quotation marks), the script fails.

Anytime you need to include quotation marks as part of the command string, you must use a pair of quotation marks. For example:

Set objShell = WScript.CreateObject("WScript.Shell")
objShell.Run("%comspec% /k sc.exe getkeyname ""Upload Manager"""), 1, True

In this script:

  1. A single set of quotation marks is used to begin the command string.

  2. A pair of quotation marks is used with Upload Manager. This will cause “Upload Manager” (with the surrounding quotation marks) to be included as part of a command string.

  3. A single set of quotation marks is used to end the command string. In this example, that results in three quotation marks appearing, one right after another.

Running a Program and Directly Accessing Its Output

To run a program and use its output, a script can use the WshShell Exec method. The WshShell Exec method returns a WshScriptExec object that provides access to the program’s standard output, standard input, and standard error streams. Your scripts can retrieve the output that results from running a program by accessing the program’s standard output stream (StdOut).

The script in Listing 3.16 runs the command-line tool, Ipconfig.exe, which retrieves information about the networking configuration of a computer, including the IP address currently assigned to that computer. The script captures the output of the tool and filters it line by line, displaying only lines that include the word Address.

Example 3.16. Running an Application and Using Its Output

 1 Set objShell = WScript.CreateObject("WScript.Shell")
 2 Set objExecObject = objShell.Exec("%comspec% /c ipconfig.exe")
 3
 4 Do Until objExecObject.StdOut.AtEndOfStream
 5     strLine = objExecObject.StdOut.ReadLine()
 6     strIP = Instr(strLine,"Address")
 7     If strIP <> 0 Then
 8         Wscript.Echo strLine
 9     End If
10 Loop

In line 2 of Listing 3.16, the script uses the Exec method to run the Ipconfig.exe command-line tool. The output generated by Opconfig.exe will be stored in the object reference named objExecObject.

In line 4, the script sets up a loop that will continue until the end of the program’s standard output stream is reached. The script checks for the end of the stream by using the AtEndOfStream property of the StdOut TextStream object.

In line 5, the script reads a line of the program’s output and stores it in the strLine variable.

In line 6, the script uses the VBScript Instr function to determine whether the word Address is stored in strLine. If the word Address is found, the script displays the line using the Echo method on line 8.

When you run Ipconfig.exe, you receive output similar to this:

Windows IP Configuration

Ethernet adapter Local Area Connection 2:

        Connection-specific DNS Suffix . : fabrikam.com
        IP Address. . . . . . . . . . . . : 192.168.248.248
        Subnet Mask . . . . . . . . . . . : 255.255.252.0
        Default Gateway . . . . . . . . . : 192.168.248.1

When you run the preceding script, you receive output only for those lines that contain the word Address:

        IP Address. . . . . . . . . . . . : 192.168.248.248

This enables you to run a command-line tool, check its output, and then have your script proceed accordingly.

Working with Shortcuts

Shortcuts are links to local or network programs, files, folders, computers, or Internet addresses. Each shortcut is a file with either an .lnk or a .url extension. The two extensions correspond to the two types of shortcuts: standard shortcuts and Uniform Resource Locator (URL) shortcuts. Standard shortcuts can link to local or network programs, files, folders, or computers, while URL shortcuts link only to entities that can be referenced by a URL — most commonly, Web pages.

Shortcuts are used in a number of places within the Windows shell, particularly within menus and toolbars. The Start menu, the Programs menu, the Quick Launch bar, and the SendTo menu, for example, consist of a group of shortcuts located in special folders. The shortcuts that appear on the Quick Launch bar are located in the following folder:

C:Documents and Settings{user profile name}Application DataMicrosoftInternet
ExplorerQuick Launch

Your scripts can make use of shortcuts to customize the menus and the desktops of your users; for example, you can create a script that provides different menu options to different groups of users within your organization.

The properties of the WSH Shortcut object are shown in Table 3.10.

Table 3.10. WSH Shortcut Properties

Property

Description

Arguments

Additional command-line arguments that can be used when starting the application.

Description

Description given to the shortcut.

FullName

Read-only property that returns the complete path to the target application.

HotKey

Keyboard shortcut: a combination of keys that, when held down together, will start the application. Keyboard shortcuts typically consist of one of the following keys plus a letter (a–z), number (0–9), or function key (F1–F12):

  • ALT

  • CTRL

  • SHIFT

For example, to set the keyboard shortcut to the CTRL key and the 9 key, use this value:

CTRL + 9

If the key combination you select is already in use, it will be overwritten and will be applied to the new shortcut created by your script.

IconLocation

Allows you to specify an icon and an icon index for the shortcut. If no location is specified, the default icon for the application is used.

TargetPath

Complete path to the target application. You must specify the full path, including the drive letter or UNC path.

When setting a TargetPath, WSH will accept the value entered. It will not check to ensure that the path is correct.

WindowStyle

Specifies the initial window type for the application. Valid styles are the same as those shown for the Run method and are listed in Table 3.9.

WorkingDirectory

Specifies the working directory for the application.

Creating Standard Shortcuts

Creating a standard shortcut involves three steps:

  1. Create an instance of the WshShortcut object by calling CreateShortcut(), passing as the sole parameter the path for the new shortcut file. Although shortcuts can be created anywhere within the file system, they are typically created within special folders such as AllUsersDesktop and StartMenu. Special folders are discussed later in this chapter.

  2. Set the properties of the WshShortcut object.

  3. Call the WshShortcut Save method. If you do not call the Save method, the shortcut will not actually be created.

The script in Listing 3.17 creates a shortcut to the Internet Information Services (IIS) manager on the desktop. The shortcut is visible to all users of the computer and can be opened either by double-clicking it or by using the key combination CTRL+SHIFT+I.

Example 3.17. Creating a Desktop Shortcut

1 Set objShell = WScript.CreateObject("WScript.Shell")
2 strDesktopFolder = objShell.SpecialFolders("AllUsersDesktop")
3 Set objShortCut = objShell.CreateShortcut(strDesktopFolder & _
4     "IIS Manager.lnk")
5 objShortCut.TargetPath = "%SystemRoot%System32Inetsrviis.msc"
6 objShortCut.Description = "Run the Internet Information Services Manager."
7 objShortCut.HotKey = "Ctrl+Shift+I"
8 objShortCut.Save

In line 2 of Listing 3.17, the WshShell SpecialFolders property retrieves the directory path to the Desktop special folder. This path is stored in the strDesktopFolder variable.

In lines 3–4, the CreateShortcut method creates a shortcut file named IISManager.lnk in the Desktop folder.

In lines 5–7, the script sets the TargetPath, Description, and HotKey properties of the WshShortcut object.

In line 8, the script creates the actual shortcut by calling the WshShortcut Save method.

Note

Note

An icon will be created on the desktop even if you do not set any properties, but the icon will not be a functional shortcut; if you double-click it, nothing will happen. (You will not even receive an error message.) To create a functional shortcut, you must set the TargetPath property. If the TargetPath property is not set, double-clicking the shortcut will not do anything.

Creating URL Shortcuts

Creating a URL Shortcut involves three similar steps:

  1. Create an instance of the WshUrlShortcut object by calling CreateShortcut, passing as the sole parameter the URL for the new shortcut file.

  2. Set the properties of the WshUrlShortcut object.

  3. Call the WshUrlShortcut Save method.

The script in Listing 3.18 creates a shortcut to the MSDN® Web site on the desktop. The shortcut is visible only to users who run the script. This is because the shortcut is created in the Desktop folder for the current user and not in the AllUsersDesktop folder.

Example 3.18. Creating a Desktop URL Shortcut

1 Set objShell = WScript.CreateObject("WScript.Shell")
2 strDesktopFld = objShell.SpecialFolders("Desktop")
3 Set objURLShortcut = objShell.CreateShortcut(strDesktopFld & "MSDN.url")
4 objURLShortcut.TargetPath = "http://msdn.microsoft.com"
5 objURLShortcut.Save

Adding an Item to the Quick Launch Bar

The script in Listing 3.19 uses a URL shortcut to create a Quick Launch button that opens the Microsoft® TechNet Web site. The Quick Launch button is visible only to users who run the script because the shortcut is created in the personal Quick Launch bar for the user.

Example 3.19. Creating a Quick Launch Button to Open the TechNet Online Web Site

1 Set objShell = WScript.CreateObject("WScript.Shell")
2 Set colEnvironmentVariables = objShell.Environment("Volatile")
3
4 strQLFolder = colEnvironmentVariables.Item("APPDATA") & _
5     "MicrosoftInternet ExplorerQuick Launch"
6
7 Set objURLShortcut = objShell.CreateShortcut(strQLFolder & "TechNet.url")
8 objURLShortcut.TargetPath = "http://www.microsoft.com/technet"
9 objURLShortcut.Save

Deleting Shortcuts

Shortcuts are files that can be deleted in the same way you delete any other file. For example, the following script deletes the shortcut created in Listing 3.18:

Set objShell = WScript.CreateObject("WScript.Shell")
Set colEnvironmentVariables = objShell.Environment("Volatile")
Set objFSO = CreateObject("Scripting.FileSystemObject")

strQLFolder = colEnvironmentVariables.Item("APPDATA") & _
    "MicrosoftInternet ExplorerQuick LaunchTechNet.URL"
objFSO.DeleteFile(strQLFolder)

For more information about working with files in WSH scripts, see “Script Runtime Primer” in this book.

Working with Special Folders

Special folders are folders that are — or at least potentially can be — present on all Windows computers; these include the My Documents, Fonts, and Start Menu folders. There are two types of special folders: those that map to standard directories and those that do not. The Favorites folder, for example, maps to a standard directory; the My Computer folder does not.

The WScript SpecialFolders collection contains the full path to each of the special folders that map to a standard directory. Table 3.11 lists the identifiers and the contents of each of the special folders in the SpecialFolders collection.

Table 3.11. Special Folders Identifier

Identifier

Folder Contents

AllUsersDesktop

Shortcuts that appear on the desktop for all users

AllUsersStartMenu

Shortcuts that appear on the Start menu for all users

AllUsersPrograms

Shortcuts that appear on the Programs menu for all users

AllUsersStartup

Shortcuts to programs that are run on startup for all users

Desktop

Shortcuts that appear on the desktop for the current user

Favorites

Shortcuts saved as favorites by the current user

Fonts

Fonts installed on the system

MyDocuments

Current user’s documents

NetHood

Objects that appear in Network Neighborhood

PrintHood

Printer links

Recent

Shortcuts to current user’s recently opened documents

SendTo

Shortcuts to applications that show up as possible send-to targets when a user right-clicks on a file in Windows Explorer

StartMenu

Shortcuts that appear in the current user’s start menu

Startup

Shortcuts to applications that run automatically when the current user logs on to the system

Templates

Application template files specific to the current user

To determine the location of any folder, retrieve the value of the SpecialFolders.Item property, specifying the name of one of the identifiers shown in Table 3.11.

Note

Note

The SpecialFolders collection enables your script to determine the path of any special folder that maps to a standard directory but does not enable your script to manipulate that folder or its contents. You must use a mechanism such as the FileSystemObject to actually work with the contents of the special folders. For more information about using the FileSystemObject in your scripts, see “Script Runtime Primer” in this book.

Retrieving the Location of Special Folders

The script in Listing 3.20 determines the location of the Fonts special folder. The script echoes the location of the Fonts folder.

Example 3.20. Determining the Location of the Fonts Folder

1 Set objShell = WScript.CreateObject("WScript.Shell")
2 strFontDirectoryPath = objShell.SpecialFolders.Item("Fonts")
3 Wscript.Echo "Font Directory Path: " & strFontDirectoryPath

Creating a Shortcut in a Special Folder

When you install an application, that application often associates itself with a particular file type. For example, when you install the Microsoft® FrontPage® Web site creation and management tool, FrontPage associates itself with all .htm files. If you right-click on a .htm file and click Edit, the file will open in FrontPage.

Of course, there might be times when you simply want to view the .htm file in Notepad. Recognizing this fact, Windows includes a special folder named SendTo. The SendTo option presents a menu of applications or locations to which you can send the selected file. You can add to the options available in the SendTo menu by adding shortcuts to the SendTo special folder. The next time you want to open an .htm file (or any file) in Notepad, you can simply right-click the file, select SendTo, and then click Notepad.

The script in Listing 3.21 creates a shortcut to the Notepad application in the SendTo special folder, thus adding Notepad to the SendTo menu.

Example 3.21. Adding the Notepad Application to the SendTo Menu

1 Set objShell = WScript.CreateObject("WScript.Shell")
2 strSendToFolder = objShell.SpecialFolders("SendTo")
3 strPathToNotepad = objShell.ExpandEnvironmentStrings _
4     ("%SystemRoot%/system32/notepad.exe")
5
6 Set objShortcut = objShell.CreateShortcut(strSendToFolder & _
7     "
otepad.lnk")
8 objShortcut.TargetPath = strPathToNotepad
9 objShortcut.Save

Environment Variables

Environment variables are a set of string values associated with a process. The Windows Shell process has a number of environment variables associated with it that contain useful information that you can use within your scripts, including:

  • Directories searched by the shell to locate programs (the path).

  • Number of processors, processor manufacturer, and processor architecture of the computer.

  • User profile location.

  • Temporary directory locations.

When a user logs on to Windows, the shell process starts and obtains its initial environment variables by loading both the computer-specific (system) and user-specific (user) environment variables from the registry.

In addition to the computer-specific and user-specific environment variables loaded from the registry, additional process environment variables are generated dynamically during each logon.

Table 3.12 lists a description of each type of environment variable and its location in the registry.

Table 3.12. Types of Environment Variables and Their Storage Locations

Type

Description

Registry Location

User

Applies to the user currently logged on to the computer and is saved between logoffs and restarts

HKCUEnvironment

System

Applies to all users of the computer and is saved between logoffs and restarts

HKLMSystemCurrentControlSetControlSession ManagerEnvironment

Volatile

Applies to current logon session and is not saved between logoffs and restarts

HKCUVolatileEnvironment

Process

Applies to current process and might be passed to child processes

Not stored in the registry

The Environment property of the WshShell object returns a WshEnvironment collection object that gives your scripts the ability to retrieve, create, and modify environment variables. The WshEnvironment collection provides access to all four types of environment variables: system, user, process, and volatile.

Retrieving Environment Variables

To retrieve a collection of environment variables of a specific type, your script must access the WshShell Environment property and provide a string parameter that represents the desired type of environment variable: system, user, process, or volatile. Your script can then use the resulting WshEnvironment collection to access the values of those environment variables by name.

The environment variables that can be retrieved using WSH are shown in Table 3.13.

Table 3.13. WSH Environment Variables

Name

System

User

Process

Process (Windows 98/ ME only)

NUMBER_OF_PROCESSORS

 

 

PROCESSOR_ARCHITECTURE

 

 

PROCESSOR_IDENTIFIER

 

 

PROCESSOR_LEVEL

 

 

PROCESSOR_REVISION

 

 

OS

 

 

COMSPEC

 

WSH Environment Variables

HOMEDRIVE

  

 

HOMEPATH

  

 

PATH

WSH Environment Variables

PATHEXT

 

 

PROMPT

  

 

SYSTEMDRIVE

  

 

SYSTEMROOT

  

 

WINDIR

 

WSH Environment Variables

TEMP

 

WSH Environment Variables

TMP

 

WSH Environment Variables

The environment variables shown in the preceding table are present on all Windows computers. However, you might also have additional user-specific or computer-specific environment variables, which can also be accessed through a script. If you do not know the names of these variables, you can obtain a complete list of the variables (and their values) by typing set from the command prompt.

The script in Listing 3.22 retrieves both the user-specific and computer-specific PATH environment variables.

Example 3.22. Displaying User-specific and Computer-specific PATH Environment Variables

1 Set objShell = WScript.CreateObject("WScript.Shell")
2 Set colSystemEnvVars = objShell.Environment("System")
3 Set colUserEnvVars = objShell.Environment("User")
4 Wscript.Echo "Computer-specific PATH Environment Variable"
5 Wscript.Echo colSystemEnvVars("PATH")
6 Wscript.Echo "User-specific PATH Environment Variable"
7 Wscript.Echo colUserEnvVars("PATH")

When the preceding script runs under CScript, output similar to the following appears in the command window:

Computer-specific PATH Environment Variable
%SystemRoot%system32;%SystemRoot%;%SystemRoot%system32WBEM;C:Program FilesM
icrosoft.NETFrameworkSDKBin;C:Program FilesMicrosoft Visual Studio.NETVc7
bin;C:Program FilesMicrosoft Visual Studio.NETCommon7IDE;C:WINNTMicrosof
t.NETFrameworkv1.0.2914;C:Program FilesMicrosoft Visual Studio.NETVc7in
;C:Program FilesMicrosoft Visual Studio.NETCommon7IDE;
C:MSSQL7BINN;C:Program FilesSupport Tools;C:Program Files
Resource KitC:PROGRA~1CACommonSCANEN~1;C:PROGRA~1CAeTrustANTIVI~1
User-specific PATH Environment Variable
C:Perlin;C:Perlin;C:Perlin

Creating Environment Variables

To create a new environment variable, your script must start with the same first step required for retrieving the values of environment variables: It must obtain a reference to a collection of environment variables of one of the four types (user, system, process, or volatile).

After your script has a reference to the collection corresponding to the type of environment variable being created, it can then store a new string value in the corresponding collection location.

Storing the string value creates the new environment variable. The new index becomes the name of the new environment variable, and the corresponding string becomes its initial value. For example, this line of code creates an environment variable named MyVariable, with the initial value 0:

colUsrEnvVars("MyVariable") = 0

You can write scripts that create process or volatile environment variables, but neither of these types is saved between logons and reboots. You can also create a volatile type, which is stored in the registry and lasts for a logon session; this type of environment variable can be used as a mechanism for communicating information between two scripts that both run during a single logon session. However, you will likely find that creating system or user types is more useful because these environment variables are saved between logon sessions and computer reboots.

The script in Listing 3.23 creates a user environment variable named APP_VARIABLE and sets its initial value to “Installed.” The script then retrieves the value of the new APP_VARIABLE environment variable to confirm that it was created.

Example 3.23. Creating a User-specific Environment Variable

1 Set objShell = WScript.CreateObject("WScript.Shell")
2 Set colUsrEnvVars = objShell.Environment("USER")
3 colUsrEnvVars("APP_VARIABLE") = "Installed"
4 Wscript.Echo colUsrEnvVars("APP_VARIABLE")

Modifying Environment Variables

To modify an environment variable, your script must use steps similar to those used to create a new environment variable. It must obtain a reference to a collection of environment variables of one of the four types (user, system, process, or volatile) and store that reference in a variable.

After your script has a reference to the collection corresponding to the type of environment variable being modified, it can then reference the name of the environment variable to modify and store a string value in the corresponding collection location, overwriting the string previously located in that location.

The script in Listing 3.24 modifies the value of a user-specific environment variable named APP_VARIABLE by changing the value to Upgraded.

Example 3.24. Modifying a User-specific Environment Variable

1 Set objShell = WScript.CreateObject("WScript.Shell")
2 Set colUsrEnvVars = objShell.Environment("USER")
3 strCurrentValue = colUsrEnvVars("APP_VARIABLE")
4 colUsrEnvVars("APP_VARIABLE") = "Upgraded"
5 Wscript.Echo colUsrEnvVars("APP_VARIABLE")

Expanding Environment Variables

When constructing environment variables or configuration strings to be used in the registry or elsewhere, you might want to incorporate the current value of an existing environment variable within those variables or strings. For example, if a script needs access to the temporary folder, you need to somehow indicate that, for this user on this computer, the temporary folder can be found in C:Temp.

However, you would not want to hard-code the value of that environment variable in your configuration string, as it might change in the future and your script would no longer be valid. Although the temporary folder might be C:Temp today, there is no reason why that cannot be changed to something else (for example, C:Temporary Folder) tomorrow. Because of that, it is better to use an environment variable to dynamically retrieve the location of the temporary folder rather than hard-coding in the value and hoping that it never changes.

To refer to the value of environment variables within configuration strings, you must use the WshShell ExpandEnvironmentStrings method.

This method accepts, as a parameter, a string with an embedded environment variable name enclosed in percentage symbols (%) and returns a string in which the environment variable name and percentage symbols (%) have been replaced with the value of the corresponding environment variable.

The script in Listing 3.25 shows the difference between echoing the value of an environment variable and echoing the expanded value of an environment variable.

Example 3.25. Creating and Displaying an Environment Variable That Incorporates Existing Environment Variables

1 Set objShell = WScript.CreateObject("WScript.Shell")
2 Set colEnvVars = objShell.Environment("User")
3 Wscript.Echo "Temporary folder (Unexpanded):"
4 Wscript.Echo colEnvVars("TEMP") & vbCrLf
5 Wscript.Echo "Temporary folder (Expanded)"
6 Wscript.Echo objShell.ExpandEnvironmentStrings("%TEMP%")

When run under Cscript, output similar to the following appears in the command window:

Temporary folder (Unexpanded):
%USERPROFILE%Local SettingsTemp

Temporary folder (Expanded)
C:DOCUME~1kmyerLOCALS~1Temp

Logging an Event

Troubleshooting applications and services is simplified if these applications and services log important events to event logs. Your scripts will also be easier to troubleshoot if they do the same.

The WshShell object provides the LogEvent method for logging events to the Application event log. The LogEvent method enables you to write to the event log from within your scripts.

Note

Note

If you want to read and process event log information from within your scripts, you need to use WMI. For more information about using WMI to work with event logs, see “Logs” in this book.

LogEvent has two required parameters. The first parameter of the LogEvent method is an integer that specifies the type of event you would like your script to log. Table 3.14 lists the available event types and their corresponding integer values.

Table 3.14. Event Types and Integer Values

Value

Event Type

0

SUCCESS

1

ERROR

2

WARNING

4

INFORMATION

8

AUDIT_SUCCESS

16

AUDIT_FAILURE

The second parameter your scripts need to supply to the LogEvent method is a string containing the message to log. You also have the option to supply a computer name as the third parameter, in which case the event will be logged in the Application log on that computer instead of the computer on which the script is running. (This parameter is ignored on Windows 95- and Windows 98-based computers.) Remote logging is useful for scripts designed to run on a number of different computers: Instead of being logged locally, all events generated by these scripts can be logged to a central computer.

The script in Listing 3.26 logs an event of each of the types listed in Table 3.14 along with a corresponding description.

Example 3.26. Logging Events to the Application Log

1 Set objShell = WScript.CreateObject("Wscript.Shell")
2 objShell.LogEvent 0,"Test Success Event"
3 objShell.LogEvent 1,"Test Error Event"
4 objShell.LogEvent 2,"Test Warning Event"
5 objShell.LogEvent 4, "Test Information Event"
6 objShell.LogEvent 8, "Test Success Audit Event"
7 objShell.LogEvent 16, "Test Failure Audit Event"

The LogEvent method can record events only in the Application event log. In addition, you cannot specify a unique source or a unique event code: All events are automatically given the source Windows Script Host and an event code corresponding to the event type (for example, all Success events will have an event code of 0).

Reading From and Writing to the Local Registry

As a general rule, it is best to manage the registry using system tools such as Regedit.exe; although not foolproof, these tools have built-in safeguards that help minimize the damage that can be caused by incorrectly configuring a registry entry. On the other hand, it is also true that many of these registry tools cannot be automated and are designed to work on only one computer at a time (typically the local computer). It is one thing to say that you should use Regedit.exe to manage the registry; it is quite another to have an urgent security bulletin recommending that you change a registry entry on all 1,000 of your domain controllers as quickly as possible. In situations in which system tools are not fast enough or efficient enough, the WshShell object provides methods for reading from, writing to, and deleting from the registry.

Caution

Caution

Changing the registry with a script can easily propagate errors. The scripting tools bypass safeguards, allowing settings that can damage your system, or even require you to reinstall Windows. Before scripting changes to the registry, test your script thoroughly and back up the registry on every computer on which you will make changes. For more information about scripting changes to the registry, see the Registry Reference on the Microsoft Windows 2000 Server Resource Kit companion CD or at http://www.microsoft.com/reskit.

Reading a Registry Entry

The registry is the primary configuration database for the Windows operating system; the ability of an operating system component to run, and to run correctly, often depends on the configuration of one or more settings within the registry.

As a system administrator, you spend a considerable amount of time checking values set within the registry. For example, in the event of computer problems, support personnel will often ask you to verify specific registry settings. This can be done directly, using a tool such as Regedit.exe, or it can be done programmatically, using the WshShell RegRead method.

For the most part, the RegRead method requires you to do just two things: 1) Create an instance of the WScript Shell object and 2) call the RegRead method, specifying the registry entry you wand to read. For example, the version number of the Windows operating system is stored in HKLMSoftwareMicrosoftWindows NTCurrentVersionCurrentVersion. You can retrieve this value by using the following code:

Set objShell = WScript.CreateObject("WScript.Shell")
sngVersion = objShell.RegRead _
    ("HKLMSoftwareMicrosoftWindows NTCurrentVersionCurrentVersion")
Wscript.Echo sngVersion

Registry Data Types

Each value stored in the registry has a particular data type. Table 3.15 lists the subset of registry types that WSH supports and the corresponding VBScript-compatible types into which the RegRead method translates corresponding registry values.

Table 3.15. Registry Data Types and Associated Script Data Types

Name

Data Type

Script Data Type

REG_SZ

String

Converted to String

REG_DWORD

Number

Converted to Integer

REG_BINARY

Binary Value

Converted to VBArray of Integers

REG_EXPAND_SZ

Expandable String

Converted to String

REG_MULTI_SZ

Array of Strings

Converted to VBArray of Strings

The data types listed in Table 3.15 are the ones most commonly used in the registry. If your script attempts to use the RegRead method to retrieve the value of a registry entry with an unsupported data type, the call will result in an error.

Note

Note

Unfortunately, WSH does not provide a way for you to verify the data type of a registry entry before you attempt to read it. However, you can use WMI to verify data types.

The script in Listing 3.27 uses the RegRead method to read the value of a multistring registry entry. Because this is a multistring value, the information is returned as an array, and a For Each loop is used to report each item in that array.

Example 3.27. Reading a Multistring Value from the Registry

1 Set objShell = WScript.CreateObject("WScript.Shell")
2 arrValues = objShell.RegRead _
3     ("HKLMSYSTEMCurrentControlSetServicesEventLogSecuritySources")
4 For Each strValue In arrValues
5      Wscript.Echo strValue
6 Next

When the preceding script is run under CScript, output similar to the following is displayed in the command window:

Spooler
Security Account Manager
SC Manager
NetDDE Object
LSA
DS
Security

Creating or Modifying a Registry Entry

Your scripts can use the RegWrite method to create a new registry entry or modify an existing one. The RegWrite method accepts three parameters: the registry entry to create or modify, the value to assign to the entry, and (optionally) the data type of the entry.

The script in Listing 3.28 uses the RegWrite method to create a DWORD entry (and set the value to 56) in the registry.

Example 3.28. Creating a DWORD Value in the Registry

1 Set objShell = WScript.CreateObject("WScript.Shell")
2 objShell.RegWrite "HKCUTestKeyVersion", 56, "REG_DWORD"

Note

Note

The WshShell RegWrite method does not support writing the REG_MULTI_SZ data type.

Deleting a Registry Entry

Your scripts can use the RegDelete method to delete registry subkeys or entries. The RegDelete method accepts a single parameter that specifies the subkey or entry to delete. Deleting a subkey deletes all the entries in that subkey.

The script in Listing 3.29 uses the RegDelete method to delete a DWORD value in the registry.

Example 3.29. Deleting a DWORD Value in the Registry

1 Set objShell = WScript.CreateObject("WScript.Shell")
2 objShell.RegDelete "HKCUTestKeyVersion"

Sending Keystrokes to a Program

By providing scripts with access to most COM objects, WSH enables you to automate applications that have a COM-based object model. Unfortunately, some applications, especially older ones, do not have a COM-based object model. To automate these applications, WSH provides a way to send keystrokes to these applications.

When you use the WshShell SendKeys method to send keystrokes to an application, your script mimics a human typing on the keyboard. To send a single keyboard character, you pass SendKeys the character itself as a string argument. For example, “x” to send the letter x. To send a space, send the string “ ”. This is exactly what a user would do if he or she was working with the application: to type the letter x, the user would simply press the x key on the keyboard.

When you use the SendKeys method, special keys that do not have a direct text representation (for example, CTRL or ALT) are represented by special characters. Table 3.16 lists these SendKeys representations for commonly used keys.

Table 3.16. SendKeys Representations of Common Keys

Key

SendKeys Representation

BACKSPACE

{BACKSPACE}, {BS}, or {BKSP}

BREAK

{BREAK}

CAPS LOCK

{CAPSLOCK}

DEL or DELETE

{DELETE} or {DEL}

DOWN ARROW

{DOWN}

END

{END}

ENTER

{ENTER} or ~

ESC

{ESC}

HELP

{HELP}

HOME

{HOME}

INS or INSERT

{INSERT} or {INS}

LEFT ARROW

{LEFT}

NUM LOCK

{NUMLOCK}

PAGE DOWN

{PGDN}

PAGE UP

{PGUP}

PRINT SCREEN

{PRTSC}

RIGHT ARROW

{RIGHT}

SCROLL LOCK

{SCROLLLOCK}

TAB

{TAB}

UP ARROW

{UP}

SHIFT

+

CONTROL

^

ALT

%

BACKSPACE

{BACKSPACE}, {BS}, or {BKSP}

All function keys, like F1, are represented by the button name contained within braces — for example, {F1} for the F1 button and {F2} for the F2 button.

For example, the following script starts Notepad and then types the sentence, “This is a test.”

Set objShell = WScript.CreateObject("WScript.Shell")
objShell.Run "Notepad.exe"
Do Until Success = True
    Success = objShell.AppActivate("Notepad")
    Wscript.Sleep 1000
Loop
objShell.SendKeys "This is a test."

When the script runs, Notepad will open, and the sample sentence will be typed in, as shown in Figure 3.12.

Controlling Notepad by Using SendKeys

Figure 3.12. Controlling Notepad by Using SendKeys

Note

Note

You can send repeated keystrokes by using the SendKeys method. For example, to send the letter a ten times, you send the string “{a 10}”. You must include a space between the keystroke and the number. SendKeys allows you to send only repeated single keystrokes. You cannot send multiple characters using repeated keystrokes; for example, this command will fail: {dog 10}.

You should be aware that sending keystrokes to an application is not the optimal method for automating a procedure. If you have an application in your enterprise that you need to automate and it has no COM-based object model, you might consider this technique. However, you should first examine whether other methods exist for automating that particular application.

Although SendKeys can be used effectively, there are several potential problems with this approach:

  • The script might have difficulty determining which window to send the keystrokes to.

  • Because the application runs in GUI mode, a user might close the application prematurely. Unfortunately, this will not terminate the script, and the script could end up sending keystrokes to the wrong application.

  • The script might have difficulty synchronizing with the application.

This timing issue is especially troublesome, simply because scripts tend to run much faster than GUI applications. For example, this simple script, which starts Calculator and then tries to type the number 2 into the application, is coded correctly but will likely fail when run (Calculator will start, but the number 2 will not be entered):

Set objShell = WScript.CreateObject("WScript.Shell")
objShell.Run "Calc.exe"
objShell.AppActivate "Calculator"
objShell.SendKeys "2"

The script fails not because of a syntax issue but because of a timing issue. As quickly as it can, the script issues commands to:

  1. Start Calculator.

  2. Switch the focus to Calculator (using the AppActivate method).

  3. Send the number 2 to Calculator.

Unfortunately, the script runs faster than Calculator can load. As a result, the number 2 is sent, and the script terminates, before Calculator can finish loading and start accepting keystrokes.

There are at least two ways of working around this problem. First, you might be able to estimate how long it will take an application to load and then pause the script for that amount of time. For example, in this script the Run method is called, and then the script pauses for 5 seconds, giving Calculator time to load:

Set objShell = WScript.CreateObject("WScript.Shell")
objShell.Run "Calc.exe"
Wscript.Sleep 5000
objShell.AppActivate "Calculator"
objShell.SendKeys "2"

Of course, is some cases it might be difficult to estimate how long it will take before an application is loaded and ready to accept keystrokes. In that case, you can call the AppActivate method and check the return value.

Using AppActivate

Before sending keystrokes to an application, you must first ensure that the application is running and that the focus is on the application (that is, the application is running in the active window). You can use the AppActivate method to set the focus on an application. The AppActivate method brings the specified window to the foreground so that you can then start using the WshShell SendKeys method to send keystrokes to the application.

The AppActivate method takes a single parameter that can be either a string containing the title of the application as it appears in the title bar or the process ID of the application. The AppActivate method returns a Boolean value that indicates whether the procedure call has been successful. If the value is False, AppActivate has failed, usually because it was unable to find the application (possibly because that application had not finished loading).

You can place your script in a loop, periodically calling AppActivate until the return value is True. At that point, the application is loaded and prepared to accept keystrokes.

For example, this script checks the return value for AppActivate. If this value is False, the script pauses for 1 second and then checks the value again. This continues until the return value is True, meaning that the application is loaded and ready for use. At that point, the script continues.

Set objShell = WScript.CreateObject("WScript.Shell")
objShell.Run "Calc.exe"
Do Until Success = True
    Success = objShell.AppActivate("Calculator")
    Wscript.Sleep 1000
Loop
objShell.SendKeys "2"

When the script is determining which application to activate, the given title is compared to the title of each window visible on-screen. If no exact match exists, the AppActivate method sets the focus to the first window whose title begins with the given text. If a window still cannot be found, the first window whose title string ends with the text is given the focus. The partial matching with the leading and trailing text of title bars ensures that AppActivate works with applications, such as Notepad, that display the name of the currently opened document on the title bar. (For example, when you first start Notepad, the window title is Untitled - Notepad, not Notepad.)

This means that when setting the focus to the Calculator, you can use one of the following lines of code:

objShell.AppActivate "Calculator"
objShell.AppActivate "Calc"
objShell.AppActivate "C"

Of course, this shortcut method of referring to a window can cause problems. For example, suppose you use this line of code:

objShell.AppActivate "Calc"

If you happen to be working on a Microsoft Word document named Calculations.doc, the keystrokes might be sent to the Word document instead of Calculator.

The script in Listing 3.30 demonstrates a more practical use of the SendKeys method: It starts and sets focus to the Microsoft Management Console (MMC) and then sends keystrokes that cause the Add/Remove Snap-in and Add Standalone Snap-in dialog boxes to be displayed. The script automates the first part of the common task of constructing custom MMC snap-in tools.

Example 3.30. Sending Keystrokes to a GUI Application

 1 Const iNormalFocus = 1
 2 Set objShell = WScript.CreateObject("WScript.Shell")
 3 objShell.Run "mmc.exe",iNormalFocus
 4
 5 Wscript.Sleep 300
 6
 7 objShell.AppActivate "Console1"
 8 Wscript.Sleep 100
 9 objShell.SendKeys "^m"
10 Wscript.Sleep 100
11 objShell.SendKeys "{TAB}"
12 Wscript.Sleep 100
13 objShell.SendKeys "{TAB}"
14 Wscript.Sleep 100
15 objShell.SendKeys "{ENTER}"

Retrieving and Changing a Script’s Current Working Directory

The working directory for a script is initially set to the directory from which the script is started. This is not necessarily the directory where the script file is located. If you are working in the C:Temp directory, for example, and you type c:scripts eport.vbs to run the Report.vbs script, the working directory is C:Temp, even though the actual script file, Report.vbs, is located in the C:Scripts directory.

The WshShell object provides the CurrentDirectory property to allow your scripts to determine or modify their current working directory.

There are a number of reasons why you might need to know — and possibly change — the current working directory of a script. For example:

  • You want your script to create a log file in the same folder as the script.

  • You want to determine whether the script has been run locally or from across the network. (The current working directory will start with two backslashes [\] instead of a drive letter such as C if it has been run from across the network.)

To retrieve the current directory for a script, create an instance of the WshShell object, and then echo the value of the CurrentDirectory property. To configure the working directory, simply assign a new value to this property.

The script in Listing 3.31 uses the CurrentDirectory property to both retrieve and set a script’s working directory. If the specified working directory does not exist, the script will fail.

Example 3.31. Setting and Retrieving a Script's Current Working Directory

1 Set objShell = WScript.CreateObject("WScript.Shell")
2
3 Wscript.Echo "Initial Working Directory:"
4 Wscript.Echo objShell.CurrentDirectory
5
6 objShell.CurrentDirectory = "C:"
7
8 Wscript.Echo "Working Directory After Change:"
9 Wscript.Echo objShell.CurrentDirectory

If the preceding script is started from the folder C:Temp, the following output will appear in the command window:

Initial Working Directory:
C:Temp
Working Directory After Change:
C:

Displaying Timed Message Boxes

In a perfect world, each time you ran a script the script would encounter optimal conditions and be able to quickly and easily complete its appointed task. In the real world, of course, things do not always work quite so smoothly. Sometimes you will run a script and encounter a decision point; for example, you might try to connect to a remote computer, only to find that the connection cannot be made. When that occurs, a decision must be made: Should the script try again, should it ignore the problem, or should it simply give up?

Depending on your needs, you might make the decision ahead of time and include code that instructs the script what to do: If you cannot make the connection, try again. At other times, you might prefer to be notified that a problem occurred and then make the decision yourself. If so, you need a way to be notified that a decision needs to be made, as well as a way to make that decision.

Your script can use the WshShell Popup method to display a message box with a variety of buttons and return a value that indicates which of the buttons the user clicked. For example, you can display a message box with Yes and No buttons, and your script can take the appropriate action based on the button clicked by the user: Try to make the connection if the user clicks Yes; terminate the script if the user clicks No.

In addition to providing users with multiple-choice options, you can also supply the Popup method with a parameter that forces it to time out after a given number of seconds as well as change the icon and title of the message box. By contrast, Wscript.Echo message boxes do not have an icon and always have the title Windows Script Host.

A sample message box created using the Popup method is shown in Figure 3.13.

Message Box Created by the Popup Method

Figure 3.13. Message Box Created by the Popup Method

Comparing the Echo and Popup Methods

If you run your script under Wscript, you can use Wscript.Echo instead of the WshShellPopup method to display messages in a message box. However, with Wscript.Echo, users can only click the OK button or do nothing. Wscript.Echo does not enable you to present users with multiple choices.

In addition, your script is paused while the Wscript.Echo message is displayed. Only after the user clicks the OK button does the script proceed. In cases where no user acknowledges the message, Wscript.Echo pauses your script indefinitely or until it times out (if a time-out has been set). If you want your script to display messages in GUI message boxes as it progresses, but continue regardless of whether a user clicks the OK button, you cannot use Wscript.Echo.

As shown in Figure 3.14, the message box displayed by Wscript.Echo has a single OK button. The user can either do nothing and keep the script waiting or click the OK button, allowing the script to continue.

Message Box Produced by Using Wscript.Echo Under WScript

Figure 3.14. Message Box Produced by Using Wscript.Echo Under WScript

Note

Note

Unlike Wscript.Echo, the Popup method always displays a message box, regardless of whether a script is running under CScript or WScript.

Creating a Message Box That Times Out

The script in Listing 3.32 uses the WScript Popup method to create three messages in message boxes, each of which has a single OK button. Each message box will be displayed on the screen for a maximum of 5 seconds. If no one has clicked the OK button after 5 seconds, the message box will automatically dismiss itself from the screen.

Example 3.32. Displaying Timed Progress Message Boxes

1 Const TIMEOUT = 5
2 Set objShell = WScript.CreateObject("WScript.Shell")
3
4 objShell.Popup "Disk Report Complete", TIMEOUT
5 objShell.Popup "Memory Report Complete", TIMEOUT
6 objShell.Popup "CPU Report Complete", TIMEOUT

Timed message boxes are useful in at least two instances. For one, they enable you to provide a sort of graphical progress indicator without interrupting the flow of the script. For example, the script shown in Listing 3.32 can be incorporated within a script that actually generates a disk report, a memory report, and a CPU report.

As each report is completed, you might want to display a message box notifying users of the current status. If this message box were displayed using the Echo method, the message box would remain on the screen, and the script would remain paused until someone clicked OK. With the Popup method, the message remains on the screen either until someone clicks OK or until the time-out period expires. In the preceding script, the message appears — and the script pauses — for no more than 5 seconds before continuing.

Along similar lines, you might want to give users the opportunity to make a decision but, if no decision is immediately forthcoming, allow the script to follow a default course of action. For example, you might have a script that carries out a number of activities and then copies a set of files across the network. Because this copying procedure might take considerable time and bandwidth, you can display a pop-up message box asking the user whether he or she wants to proceed with the copying. The message can could be displayed for a minute or so; if there is no response, the script can automatically begin copying files.

Choosing Icons and Buttons

The WshShell Popup method enables you to create message boxes with various sets of buttons and icons. For example, you can create a message box with Yes and No buttons or a message box with the button set Abort, Retry, Ignore. In addition, you can determine which button a user clicked and then take appropriate action based on the user choice. This helps differentiate the Popup method from the Echo method; message boxes displayed using Echo have only an OK button.

You specify both the button set and the icon by providing the Popup method with a fourth parameter. This parameter accepts a combination of predefined constants that specify the button set and the icon the message box should use.

Table 3.17 lists the icons available to use with the Popup method along with their corresponding constants.

Table 3.17. Constants for Icons

Icon

Constant Name

Constant Value

STOP

vbCritical

16

QUESTION MARK

vbQuestion

32

EXCLAMATION MARK

vbExclamation

48

INFORMATION

vbInformation

64

Table 3.18 lists the button sets available to use with the Popup method along with their corresponding constants.

Table 3.18. Constants for Button Sets

Button Set

Constant Name

Constant Value

OK

vbOKOnly

0

OK and CANCEL

vbOKCancel

1

ABORT, RETRY and IGNORE

vbAbortRetryIgnore

2

YES, NO and CANCEL

vbYesNoCancel

3

YES and NO

vbYesNo

4

RETRY and CANCEL

vbRetryCancel

5

Although you can use either the constant names or the constant values within a script, using the constant names makes it much easier to understand the code. For example, it is relatively easy to see that the following line of code creates a pop-up message box with Yes and No buttons. (This message box also has a time-out value of 10 seconds and the title Popup Example.)

objShell.Popup "Stop Icon / Abort, Retry and Ignore Buttons", _
     10, "Popup Example", vbYesNo

To display both an icon and a button set, use two constants (joined by a plus sign) in the code:

objShell.Popup "Stop Icon / Abort, Retry and Ignore Buttons", _
     10, "Popup Example", vbCritical + vbYesNo

The script in Listing 3.33 displays a series of message boxes, each of which has a different icon and button set. If you click any button on a message box, the next message box will be displayed; otherwise, the script displays each message box for five seconds.

Example 3.33. Displaying Combinations of Icons and Button Sets

 1 Const TIMEOUT = 5
 2 Const POPUP_TITLE = "Icons and Buttons"
 3 Set objShell = WScript.CreateObject("WScript.Shell")
 4 objShell.Popup "Stop Icon / Abort, Retry and Ignore Buttons", _
 5      TIMEOUT,POPUP_TITLE,vbCritical+vbAbortRetryIgnore
 6
 7 objShell.Popup "Question Mark Icon / Yes, No and Cancel Buttons", _
 8      TIMEOUT,POPUP_TITLE,vbQuestion+vbYesNoCancel
 9
10 objShell.Popup "Exclamation Mark Icon / Yes and No Buttons", _
11      TIMEOUT,POPUP_TITLE,vbExclamation+vbYesNo
12
13 objShell.Popup "Information Icon / Retry and Cancel Buttons", _
14      TIMEOUT,POPUP_TITLE,vbInformation+vbRetryCancel

In lines 4–14, the WshShell Popup method is called four times in succession to display pop-up message boxes with various icons and button sets. The third parameter passed to Popup is always the POPUP_TITLE constant and results in each pop-up message box having Icons and Buttons as its title. The fourth parameter is passed various constants representing both the icon and the button set to be used. Note that the constants are combined by using the plus (+) operator.

Choosing the Default Button

The WshShell Popup method lets you specify the default button when you create a message box. The default button is the button that has focus and will be chosen if the user presses ENTER.

It is not unusual for users to reflexively press ENTER any time a message box is displayed. Because of that, some care should be taken when choosing the default button. For example, you might choose the button that, 9 times out of 10, users will select anyway. Or you might choose the button that is the “safest” should a user press ENTER without realizing what they are doing. For example, your message box might prompt the user, “Are you sure you want to delete all the files on this hard drive?” In a case such as this, you might want to configure No as the default button. That way, no damage will be done if a user accidentally presses ENTER.

You can specify which button to make the default by adding another constant to the fourth parameter of the Popup method. Table 3.19 lists the constants you can use to set the default button in a message box. If you try to set the second or third button as the default when the message box does not have a second or third button, the default button constant you specify will be ignored and the left button will be set as the default.

Table 3.19. Constants for Default Button Locations

Default Button

Constant Name

Constant Value

LEFT

vbDefaultButton1

0

MIDDLE

vbDefaultButton2

256

RIGHT

vbDefaultButton3

512

The script in Listing 3.34 displays two message boxes in succession. Both message boxes use the Abort, Retry, Ignore button set. The first message box does not specify the default button, so the leftmost button, Abort, is automatically selected as the default. The second message box uses the vbDefaultButton2 constant to make Retry the default button.

Example 3.34. Setting Retry as the Default Button

1 Const TIMEOUT = 5
2 Set objShell = WScript.CreateObject("WScript.Shell")
3
4 objShell.Popup "Abort, Retry, Ignore. No Default Specified." _
5      ,TIMEOUT,,vbAbortRetryIgnore
6
7 objShell.Popup "Abort, Retry, Ignore. Retry Set as Default." _
8      ,TIMEOUT,,vbAbortRetryIgnore+vbDefaultButton2

Retrieving User Input

One of the advantages of using the Popup method rather than the Echo method is that you can give users the chance to make a choice: Yes, I want to try again; no, I would rather just quit. This means that the script needs to determine which button a user has clicked. The Popup method returns an integer that you can compare with a set of constants to determine which button was clicked. If a message box times out, the Popup method returns –1.

Table 3.20 lists the values you can use to identify which button a user has clicked. If the return value of the Popup method is equal to one of these constants, the user has clicked the associated button. Within a script, you can check either for the value of the constant or for the constant itself. For example, these two lines of code both check to see whether the OK button was clicked:

If intClicked = 1
If intClicked = vbOK

Table 3.20. Button Constants

Value

Constant

Button Clicked

1

VbOK

OK

2

VbCancel

Cancel

3

VbAbort

Abort

4

VbRetry

Retry

5

VbIgnore

Ignore

6

VbYes

Yes

7

VbNo

No

The script in Listing 3.35 displays a message box that uses the Yes, No button set to determine whether the user would like more detailed information.

The script uses the FileSystemObject to present the user with information about the host the script is running under. The script determines and displays the version of the script host file and uses the Popup method to allow the user to decide whether or not they would like to see more details about the file.

Example 3.35. Retrieving User Input from a Message Box

 1 Const TIMEOUT = 7
 2 Set objShell = WScript.CreateObject("WScript.Shell")
 3 Set objFS = WScript.CreateObject("Scripting.FileSystemObject")
 4
 5 strPath = Wscript.FullName
 6 strFileVersion = objFS.GetFileVersion(strPath)
 7
 8 iRetVal = objShell.Popup(Wscript.FullName & vbCrLf & _
 9      "File Version: " & _
10      strFileVersion & vbCrLf & _
11      "Would you like more details?" _
12      ,TIMEOUT,"More Info?",vbYesNo + vbQuestion)
13
14 Select Case iRetVal
15     Case vbYes
16         Set objFile = objFS.GetFile(strPath)
17         objShell.Popup WScript.FullName & vbCrLf & vbCrLf & _
18          "File Version: " & strFileVersion & vbCrLf & _
19          "File Size: " & Round((objFile.Size/1024),2) & _
20              " KB" & vbCrLf & _
21              "Date Created: " & objFile.DateCreated & vbCrLf & _
22              "Date Last Modified: " & objFile.DateLastModified & _
23              vbCrLf,TIMEOUT
24         Wscript.Quit
25    Case vbNo
26         Wscript.Quit
27    Case -1
28         WScript.StdOut.WriteLine "Popup timed out."
29         Wscript.Quit
30 End Select

WshNetwork Object

The WshNetwork object provides your scripts with the ability to work with network drives and printers. It also provides your scripts with access to the name of the computer they are currently running on, as well as the domain and user names of the account under which they are running. This makes WshNetwork extremely useful in scripts that take actions based on such information as the name of the user logging on or the domain in which the user account resides.

The WshNetwork object can be used to accomplish some of the same tasks as the net.exe command-line tool. For example, the net name command returns the name of the user and the computer; the net use command is used to map and unmap network drives. These similarities make WshNetwork useful for converting legacy batch files and logon scripts that use net.exe to a WSH-based solution.

Table 3.21 lists the capabilities provided by the WshNetwork object, along with the methods and properties that your scripts can use to access this functionality.

Table 3.21. Capabilities Provided by the WshNetwork Object

Category

Method or Property

Working with network drives

MapNetworkDrive

EnumNetworkDrives

RemoveNetworkDrive

Working with network printers

AddPrinterConnection

AddWindowsPrinterConnection

EnumPrinterConnections

SetDefaultPrinter

RemovePrinterConnection

Obtaining information about the currently logged-on user

ComputerName

UserDomain

UserName

WshNetwork is part of the Windows Script Host Object Model, wshom.ocx. The object model for the WshNetwork object is shown in Figure 3.15. In addition to the methods and properties provided by the WshNetwork object, WshNetwork also exposes a WshCollection object. The WshCollection object is not created using CreateObject but is automatically created and returned by the WshNetwork object’s EnumNetworkDrives and EnumPrinterConnections methods.

WshNetwork Object Model

Figure 3.15. WshNetwork Object Model

Figure 3.15 shows the properties and methods of the WshNetwork object.

Accessing the WshNetwork Object

The WshNetwork object is a COM object, and an instance of the object can be created by the following code statement:

Set objNetwork = WScript.CreateObject("WScript.Network")

Managing Network Drives

Network drives remain an important part of the computing infrastructure. Users prefer mapped network drives to Universal Naming Convention (UNC) path names; it is easier to remember that financial records are stored on drive X than to remember that financial records are stored on \atlfs- 01departmentsaccountingadminfinancial_records2002_archive. Administrators prefer mapped network drives as well; if financial records need to be moved to a new server, it is far easier to remap drive X than to expect users to memorize the new location.

Your scripts can use the methods in this section to manage network drive connections as part of a logon script or in any WSH script that has a need to connect to or disconnect from a network share. In fact, this represents one area where WSH has system administration capability not found in WMI. While both WSH and WMI let you enumerate mapped network drives (WMI by using the Win32_MappedLogicalDisk class), only WSH enables you to create and delete drive mappings.

WshNetwork provides three methods to work with network drive connections: MapNetworkDrive, RemoveNetworkDrive, and EnumNetworkDrives. You can use the three methods to manage network connections as part of a user’s logon script or in any WSH script that needs to connect to or disconnect from a network share.

Mapping a Network Drive

You map a local drive letter to a shared folder using the WshNetwork object’s MapNetworkDrive method. You can use MapNetworkDrive to connect directly to a shared folder or any child folder beneath a shared folder. MapNetworkDrive has two mandatory and three optional arguments, defined in Table 3.22.

Table 3.22. MapNetworkDrive Arguments

Argument

Type

Required

Default

Description

LocalName

String

None

The drive letter, followed by a colon, assigned to the mapped network drive, e.g., “H:”.

RemoteName

String

None

The shared folder’s UNC name, e.g., “\ServerNameShareName” or “\ServerNameShareNameFolderName”.

UpdateProfile

Boolean

 

False

Boolean value indicating whether the mapping information is stored in the current user’s profile. The value True updates the current user’s profile; False does not.

UserName

String

 

None

Maps the network drive using the credentials of someone other than the current user.

Password

String

 

None

Password for the user identified by the UserName argument.

The following example demonstrates how MapNetworkDrive can be used in a user’s logon script to connect to two network shares.

Set objNetwork = Wscript.CreateObject("WScript.Network")
objNetwork.MapNetworkDrive "G:", "\atl-fs-01Sales"
objNetwork.MapNetworkDrive "H:", "\atl-fs-01Users$lewjudy"

Drive G is mapped to the Sales share on the file server named atl-fs-01. Drive H is mapped to the user’s home directory located directly beneath the hidden share named Users$ on atl-fs-01.

By default, MapNetworkDrive uses the access token of the current user to validate permissions prior to making the connection; you cannot map a drive to a folder unless you have permission to access that folder. In administrative scripts, it might be necessary to supply an alternate or elevated set of credentials to successfully connect to a share. You can use the optional UserName and Password arguments to supply a set of credentials that are used in lieu of the current user’s access token. After the connection is established, the supplied credentials govern access to the network drive for the duration of the connection.

Two common conditions can cause MapNetworkDrive to fail:

  • The user running the script might not have sufficient permissions to connect to the target share.

  • The local drive letter might already be in use.

Left unchecked, both conditions can result in run-time errors. To avoid permissions-related or password-related issues, your best defense is to trap and handle the error using VBScript’s On Error Resume Next statement. To handle a local drive letter that is already in use, you can forcibly disconnect the mapped drive or locate and use a different drive letter.

Unmapping a Network Drive

Your scripts can use the RemoveNetworkDrive method to unmap a network drive. If the network drive has a mapping between a local drive letter and the remote UNC path, the method requires the local drive letter as its single parameter.

If the network drive does not have a mapping between a local drive letter and the remote UNC path, the method requires the remote UNC path as its single parameter.

The script in Listing 3.36 unmaps a drive G.

Example 3.36. Unmapping a Network Drive

1 Set objNetwork = WScript.CreateObject("Wscript.Network")
2 objNetwork.RemoveNetworkDrive "G:"

In addition to the mandatory parameter that specifies the mapped drive to be removed, the MapNetworkDrive method accepts two additional, optional, parameters:

  • A Boolean value that, if set to True, specifies that the method should unmap the drive regardless of whether it is currently in use. If the value is set to True, the user will no longer be able to save data to that drive, even if he or she has already opened a document from there. Instead, the user will have to save the document to an alternate location.

  • A Boolean value that, if set to True, specifies that the method should remove the drive mapping from the profile of the user.

Listing Current Network Drives

Your scripts can use the EnumNetworkDrives method to retrieve a list of the current mapped network drives on a computer.

The EnumNetworkDrives method returns a collection that holds pairs of items: network drive local names and their associated UNC names. The collection is zero-indexed; the even-numbered items in the collection are the local drive names, and the odd-numbered items are the associated UNC paths.

A sample network drive collection is shown in Table 3.23. In this sample collection, drive D (index number 0) maps to \atl-fs-01userskmyer (index number 1).

Table 3.23. Sample Network Drive Collection

Index Number

Value

0

D:

1

\atl-fs-01userskmyer

2

E:

3

\atl-fs-02accounting

4

F:

5

\atl-fs-03public

A script can retrieve both pieces of information about each mapped network drive by iterating through the collection returned by EnumNetworkDrives and retrieving two items from the collection during each iteration. An example of this is shown in Listing 3.37.

Example 3.37. Listing Current Network Drives

1 Set objNetwork = WScript.CreateObject("WScript.Network")
2 Set colDrives = objNetwork.EnumNetworkDrives
3 For i = 0 to colDrives.Count-1 Step 2
4    Wscript.Echo colDrives.Item(i) & vbTab & colDrives.Item (i + 1)
5 Next

Managing Network Printers

Managing printer connections on user computers is an important part of system administration. When a new printer comes online, you do not have to send instructions on how to connect to this device; instead, you can simply include code in a logon script that automatically makes this connection for a user. Likewise, when a printer is removed from the network, you can remove the printer connection, preventing the problems likely to arise when users try to print to a printer that no longer exists.

The WshNetwork object provides methods that enable your scripts to add or remove printer connections, to set the default printer, and to list the current printer connections on a computer.

Adding Printer Connections

Two WshNetwork methods enable your scripts to add printer connections: AddWindowsPrinterConnection and AddPrinterConnection.

The AddWindowsPrinterConnection method enables a script to add a Windows-based printer connection (like using the Control Panel Add Printer Wizard), while the AddPrinterConnection method enables a script to add an MS-DOS®-based printer connection.

Adding Windows-based Printer Connections

When used to add a Windows-based printer connection to a Windows NT–based operating system such as Windows 2000, Windows XP, or Windows NT, the AddWindowsPrinterConnection method accepts, as its only required parameter, the UNC path of the printer.

The script in Listing 3.38 adds a connection to the network printer with the UNC path \HRServer01Printer1.

Example 3.38. Adding a Windows-based printer connection (Windows NT–based operating system)

1 Set objNetwork = Wscript.CreateObject("WScript.Network")
2 objNetwork.AddWindowsPrinterConnection "\HRServer01Printer1"

When used to add a Windows-based printer connection to Windows 95, Windows 98, or Windows Me, the AddWindowsPrinterConnection method requires two parameters: the UNC path of the printer and the name of the printer driver (which must already be installed on the computer). In addition, when used with these operating systems, the method accepts a third, optional, parameter that specifies the local port through which the printer should be made available.

Adding MS-DOS-based printer connections

The AddPrinterConnection method enables a script to add an MS-DOS-based printer connection. The method takes two required parameters: the local port through which the printer will be available and the UNC path of the network printer.

The AddPrinterConnection method also lets you add the mapping to the user profile. If you want to do this, set the updateProfile Boolean argument to True. The user name and the password parameters let you connect to the specified printer by using another user’s credentials.

Unlike the AddWindowsPrinterConnection method, the AddPrinterConnection method requires you to specify a port name.

Removing a Printer Connection

To remove a printer connection, use the RemovePrinterConnection method.

WshNetwork.RemovePrinterConnection(printerName, [forced], [updateProfile])

The first argument identifies the shared printer. The other two optional arguments let you specify:

  • Whether the disconnection should be forced, even if the printer is in use.

  • Whether the user profile should be updated to reflect the disconnection.

The RemovePrinterConnection method removes both Windows and MS-DOS-based printer connections. If the printer was connected using AddPrinterConnection, the printerName argument must be the same as the printer’s local port. If the printer was set up using the AddWindowsPrinterConnection method or was added manually through the Add Printer wizard, the printerName argument must be the printer’s UNC name.

For example, this command removes the connection to the printer \atl-ps-01colorprinter.

Set objNetwork = WScript.CreateObject("WScript.Network")
objNetwork.RemovePrinterConnection "\atl-ps-01colorprinter"

Enumerating the Available Printers

To obtain a list of the printers set up on a computer, you use code similar to that used to list the mapped network drives.

You use the EnumPrinterConnections method to obtain a WshCollection object where each network printer is made up of two elements in the collection. The even-positioned item contains the printer’s local name or port. The odd-positioned item contains the UNC name. As shown in Listing 3.39, you can use a For Next loop with the step value 2 to collect all the information you need.

Example 3.39. Enumerating Available Printers

1 Set objNetwork = WScript.CreateObject("WScript.Network")
2 Set colPrinters = objNetwork.EnumPrinterConnections
3 For i = 0 to colPrinters.Count -1 Step 2
4    Wscript.Echo colPrinters.Item(i) & vbTab & colPrinters.Item (i + 1)
5 Next

When run under CScript, output similar to this is displayed in the command window:

LPT1: Art Department Printer
XRX00034716DD75 \atl-prn-xrxplotter
XRX0000AA622E89 \atl-prn-xrxcolorprinter

Setting the Default Printer

Many users print documents by clicking the printer icon within their application; in most cases that means that documents are automatically sent to the user’s default printer. By assigning different users different default printers, you can help divide the print load among your print devices and ensure that documents are printed quickly and efficiently.

The SetDefaultPrinter method lets you assign a specific printer the role of the default printer. SetDefaultPrinter uses the following syntax:

WshNetwork.SetDefaultPrinter(printerName)

The printerName parameter is the UNC name of the printer. (If the printer is a local printer, you can also use a port name such as LPT1.) This script sets the printer \atl-ps-01colorprinter as the default:

Set objNetwork = WScript.CreateObject("WScript.Network")
objNetwork.SetDefaultPrinter("\atl-ps-01colorprinter")

The SetDefaultPrinter method cannot retrieve the current default printer.

Obtaining User and Computer Information

The WshNetwork object has three read-only properties that your scripts can use to obtain information about the computer they are running on and the user account under which they are running. These properties are ComputerName, UserDomain, and UserName.

Depending on your needs, you should consider using ADSI to return this same information. WSH is able to return only the SAM account name of a user or computer (for example, kenmyer). ADSI, however, is able to return the distinguished name for these same objects. (For example, cn=ken myer,ou=Human Resources,dc=fabrikam,dc=com.) Using the distinguished name, the script can then directly bind to the object in Active Directory and perform such tasks as enumerate the groups that the user belongs to. With the SAM account name, the script would need to search Active Directory, determine the distinguished name, and then bind to the object.

There might be times, however, when simply knowing the user name, computer name, or domain name is sufficient information for your script to perform its tasks. In those instances, the WshNetwork object will likely suffice. In addition, WSH is installed by default on Windows 98 and Windows NT 4.0 computers, while ADSI is not. Because of this, WSH might be your only option when supporting computers such as those running Windows NT 4.0.

The WshNetwork object is often used in logon scripts that map different network drives, depending on the domain of the user running the script. The script in Listing 3.40 maps drive N to \fileserver01accounting if the user’s domain is ACT or maps drive N to \fileserver01development if the user’s domain is DEV.

Example 3.40. Mapping a Network Drive According to User Domain

 1 Set objNetwork = WScript.CreateObject("WScript.Network")
 2 strUserDomain = objNetwork.UserDomain
 3
 4 If strUserDomain = "ACCOUNTING" Then
 5    objNetwork.MapNetworkDrive "N:", "\fileserver01accounting", True
 6 ElseIf strUserDomain = "DEVELOPMENT" Then
 7    objNetwork.MapNetworkDrive "N:", "\fileserver01development", True
 8 Else
 9    Wscript.Echo "User " & objNetwork.UserName & _
10        "not in ACCOUNTING or DEVELOPMENT. N: not mapped."
11 End If

WshController Object

One limitation of WSH has always been the fact that scripts could only be run locally; they could not be run against remote computers. For instance, suppose you want to use a script to add printer connections to a number of computers. You would have to run that script on each of those computers; historically, you could not run a script on Computer A and get it to add a printer connection to Computer B. Because of that, WSH has primarily been relegated to use in logon scripts.

Clearly, it would be useful to automate the running of a script on remote computers and on multiple computers. The WshController object, introduced in WSH 5.6, provides that capability.

The WshController object allows you to create a controller script that can run worker scripts against remote computers. The controller script initiates, monitors, and, if necessary, terminates the worker script. The worker script, meanwhile, is simply the script that carries out the administrative task — for example, adding a printer connection or mapping a network drive.

The worker scripts do not have to be located on the same computer as the controller script, although their location must be specified relative to the computer on which the controller script runs. For instance, you can create a controller script that accesses a worker script in a shared folder on another computer and runs that worker script on yet another computer.

The WshController object provides communication among all the local and remote computers involved. The script that runs on the remote computer is never saved to disk on the remote computer. Instead, the WshController object starts the script within a WSH process in the memory of the remote computer.

Figure 3.16 shows a list of all the objects, events, methods, and properties your scripts can make use of when using the WshController object to run scripts on remote computers.

WshController Object Model

Figure 3.16. WshController Object Model

Accessing the WshController Object

The WshController object is a COM object, and an instance of the object can be created by the following code statement:

Set objController = WScript.CreateObject("WshController")

Note

Note

The ProgID of the WshController object does not follow the same naming convention as the WshNetwork (WScript.Network) and WshShell (WScript.Shell) objects. The ProgID of the WshController object has no dot (.) and does not begin with WScript. It is simply the name of the object: WshController.

Setup Required to Run Scripts Remotely

Before you can use the WshController object to run scripts on remote computers, you must first ensure that your environment is configured as follows:

  • Both the local and target remote computers must be running WSH version 5.6.

  • You must add a string-valued entry (REG_SZ) named Remote to the registry subkey HKEY_LOCAL_ MACHINESOFTWAREMicrosoftWindows Script HostSettings and set its value to 1 on all target remote computers. You do not need to add this entry to the registry of the local computer from which you run the controller script.

Caution

Caution

Changing the registry with a script can easily propagate errors. The scripting tools bypass safeguards, allowing settings that can damage your system, or even require you to reinstall Windows. Before scripting changes to the registry, test your script thoroughly and back up the registry on every computer on which you will make changes. For more information about scripting changes to the registry, see the Registry Reference on the Windows 2000 Server Resource Kit companion CD or at http://www.microsoft.com/reskit.

The script in Listing 3.41 uses the WMI Registry Provider to add the Remote entry and set its value to 1 on the computer specified in the strComputer variable.

Example 3.41. Adding a Registry Entry to Enable Running Scripts Remotely

1 Const HKEY_LOCAL_MACHINE = &H80000002
2 strComputer = "RemoteComputerName"
3
4 Set objRegProv = GetObject("winmgmts:{impersonationLevel=Impersonate}" & _
5                  "!\" & strComputer & "
ootdefault:StdRegProv")
6
7 strKeyPath = "SOFTWAREMicrosoftWindows Script HostSettings"
8 objRegProv.SetStringValue HKEY_LOCAL_MACHINE,strKeyPath,"Remote","1"

The user running the controlling script on the local computer must be a member of the local administrators group of each target remote computer.

WshController Capabilities

The WshController object enables you to run scripts on remote computers, monitor the status of remotely running scripts, and examine errors produced by these scripts. Table 3.24 lists these categories along with the methods, properties, and events of the WshController object, the WshRemote object, and the WshRemoteError object that your scripts can use to access this functionality.

Table 3.24. Capabilities Provided by the WshController Object

Task Category

Method, Property, or Event

Running Scripts on Remote Computers

CreateScript, Execute, Terminate

Monitoring Status of Remotely Running Scripts

Status, Start, End, Error (event)

Examining Errors Produced by Remotely Running Scripts

Error (event), Error (property), Character, Description, Line, Number, Source, SourceText

Running Scripts on Remote Computers

The WshController object provides you with the ability to run scripts on remote computers. The WshController object has a single method, CreateScript, which returns a WshRemote object.

In turn, the WshRemote object can then be used to run the worker script. The worker script is not run as a result of the call to CreateScript. Instead, the WshRemote object Execute method is used to actually run the script on the remote computer.

The script in Listing 3.42 runs the hypothetical worker script MapNetworkDrive.vbs on the remote computer, RASServer01. Although the location of the worker script can be specified using either a local file path or a UNC path, because no path is specified in Listing 3.42, this means the worker script must be located in the same directory as the controller script.

Example 3.42. Running a Local Script on a Remote Computer

 1 strRemoteComputer = "RASServer01"
 2 strWorkerScript = "MapNetworkDrive.vbs"
 3 Set objWshController = WScript.CreateObject("WshController")
 4 Set objRemoteScript = _
 5 objWshController.CreateScript(strWorkerScript, strRemoteComputer)
 6 objRemoteScript.Execute
 7
 8 Do While Not objRemoteScript.Status = 2
 9      Wscript.Sleep(100)
10      Wscript.Echo "Remote script not yet complete."
11 Loop

Note

Note

Remote access is not an interactive process, which means that remotely run scripts cannot display any GUI elements on the remote computer. If you run a remote script that displays a GUI element, the script might fail or produce indeterminate results. For example, suppose your script displays a message box that the user must respond to. Because the script runs in a hidden window, the user will never be able to see, and thus never be able to respond to, that message box. As a result, the script will hang, waiting for user input that will never be provided.

Monitoring Status of Remotely Running Scripts

Running scripts on remote computers is most useful if you can be sure that the scripts actually ran successfully. You can do so by using the WshRemote object and monitoring events that are generated when the remote script runs.

The WshRemote object (created when you call the CreateScript method) has a property named Status that monitors the status of the worker script associated with the object. The Status property can take on one of three values:

  • 0 indicates that the remote script has not yet started to run.

  • 1 indicates that the remote script is currently running.

  • 2 indicates that the remote script has finished running.

In addition to the WshRemote object Status property, three events are triggered when certain things happen with the remote script. Having your script handle these events allows you to monitor the running of the remote script. The three events are:

  • Start. This event is triggered when the remote script starts executing.

  • End. This event is triggered when the remote script is finished executing.

  • Error. This event is triggered only if the remote script encounters a problem. If the Error event is triggered, you can access the WshRemoteError object as a property of the WshRemote object and retrieve information about the error that occurred.

The script in Listing 3.43 has three subroutines (Remote_Start, Remote_Error, and Remote_End) that respond to the three events Start, Error, and End. The subroutines are called if and when each of the events takes place. In turn, each subroutine simply displays a message indicating that the corresponding event has taken place.

The call to ConnectObject in line 7 of Listing 3.43 causes the script to handle the events. Note that the string “Remote_”, passed as the second parameter to the ConnectObject method, determines the first part of the names of the event-handling subroutines; the last part of the names is determined by the particular event the subroutine handles.

Example 3.43. Monitoring a Remotely Running Script

1  strRemoteComputer = "RASServer01"
2  strWorkerScript = "CreateTextFilMapNetworkDrive.vbs"
3
4  Set objWshController = WScript.CreateObject("WshController")
5  Set objRemoteScript =_
6     objWshController.CreateScript(strWorkerScript, strRemoteComputer)
7  Wscript.ConnectObject objRemoteScript, "Remote_"
8  objRemoteScript.Execute
9
10 Do While Not objRemoteScript.Status = 2
11    Wscript.Sleep(100)
12 Loop
13
14 Sub Remote_Start
15    Wscript.Echo "Started Running Remote Script."
16 End Sub
17
18 Sub Remote_Error
19    Wscript.Echo "Error Running Remote Script."
20    objRemoteScript.Terminate
21    Wscript.Quit
22 End Sub
23
24 Sub Remote_End
25    Wscript.Echo "Finished Running Remote Script."
26 End Sub

If everything works as expected, only the Start and End events will be triggered when you run the script in Listing 3.43. When the script runs, you will receive messages indicating that the remote script started and ended. If an error occurs, however, you will receive a simple message stating that an error occurred. For information about retrieving more details about errors, see “Examining Errors Produced by Remotely Running Scripts”, which follows.

Examining Errors Produced by Remotely Running Scripts

Knowing that a remotely executing script has encountered an error is important. However, once you know the error has occurred, you next need to determine the cause and correct the problem. If an error occurs in a remotely executing script, the WshRemoteError object can be accessed as a property of the WshRemote object. The WshRemoteError object includes a number of properties that describe the error that occurred and can help you troubleshoot the problem. These properties are shown in Table 3.25.

Table 3.25. Properties of the WshRemoteError Object

Property

Description

Character

Returns the character position in the line where the error occurred. For example, in this simple script, the semicolon is an invalid character. Because the semicolon is the 14th character in the line, the Character property is 14:

Wscript.Echo ;

Description

Brief description of the error.

Line

Line number of the script in which the error occurred.

Number

Error code associated with the error.

Source

Identifies the COM object that reported the error.

SourceText

Contains the line of code that generated the error. The SourceText cannot always be retrieved; in that case, an empty string will be returned.

The script in Listing 3.44 includes a subroutine that handles the Error event. Unlike the script in Listing 3.43, which simply displayed a message indicating that an error occurred, this script uses the WshRemote object Error property to retrieve an instance of the WshRemoteError object. The script then displays all of the properties of the WshRemoteError object, providing you with a great deal of information that is useful for troubleshooting the problem.

Example 3.44. Examining Errors Produced by a Script Running Remotely

 1 strRemoteComputer = "RASServer01"
 2 strWorkerScript = "CreateTestFile.vbs"
 3
 4 Set objWshController = WScript.CreateObject("WshController")
 5 Set objRemoteScript =_
 6    objWshController.CreateScript(strWorkerScript, strRemoteComputer)
 7 Wscript.ConnectObject objRemoteScript, "Remote_"
 8 objRemoteScript.Execute
 9
10 Do While Not objRemoteScript.Status = 2
11    Wscript.Sleep(100)
12 Loop
13
14
15 Sub Remote_Error
16    Wscript.Echo "Error Running Remote Script."
17    Set objError = objRemoteScript.Error
18    Wscript.Echo "Character   :" & objError.Character
19    Wscript.Echo "Description :" & objError.Description
20    Wscript.Echo "Line        :" & objError.Line
21    Wscript.Echo "Number      :" & objError.Number
22    Wscript.Echo "Source      :" & objError.Source
23    Wscript.Echo "Source Text :" & objError.SourceText
24    objRemoteScript.Terminate
25    Wscript.Quit
26 End Sub

Limitations of Remote WSH

Remote WSH has two important limitations. First, there is no easy way to retrieve the output from a remotely run script. Second, remotely run scripts cannot access shared folders using the credentials of the user who ran the controller script.

To work around the first problem, you can create a text file that holds the results of your worker script. You might create the file on a shared folder on each of the remote computers you are running the worker scripts on and then retrieve the files and store them in a central location. To learn about creating text files and accessing them by using UNC paths, see “Script Runtime Primer” in this book.

However, there is no way around the second problem, at least not without creating potential security vulnerabilities.

Securing Scripts

Security is always a primary concern for system administrators; this is as true for scripts and scripting as it is for anything else. After all, no one wants a repeat of the ILOVEYOU virus, a script that, largely without warning, managed to wreak havoc worldwide.

WSH 5.6 includes a number of measures designed to guard against problems such as this. The ILOVEYOU virus succeeded not so much by exploiting a flaw in Windows Script Host as it did by exploiting a flaw in human nature: people are innately curious about anything that is given to them. Faced with the decision “Do you want to run this script?” and with no other information to go on, many people opted to run the script.

WSH 5.6 can help users make more intelligent choices. For example, when a user tries to run a script, WSH can be configured to display a dialog box that says, in effect, “We do not know who wrote this script, and we have no guarantee that it is safe to run. Are you sure you want to proceed?” Alternatively, system administrators can relieve users of the need to make choices at all. Instead, WSH can be configured so that users can only run scripts that have been pre-approved and digitally-signed.

This section of the chapter examines several techniques that can be used to enhance script security, including:

  • Signing scripts with digital signatures.

  • Restricting the ability of a user to run a script.

  • Disabling Windows Script Host.

Important

Important

Security obviously applies to items other than scripts. It is true that hackers often use scripts to ply their trade, simply because these plain-text files are easy to write and easy to distribute. However, scripts are not the only security threat faced by system administrators; after all, executable files and batch files have been misused as well. The techniques discussed in this chapter can be a useful part of any security plan, but they are by no means an entire security plan in and of themselves.

Digitally Signing Scripts

Digital signatures (introduced with WSH 5.6) provide a way for you to verify who authored a script, as well as a way to guarantee that a script has not been altered since it was written and signed. This does not necessarily mean that the script is “safe;” after all, virus writers can obtain digital signatures, too. However, digital signatures do provide two measures of protection:

  • You can specify which script authors are to be trusted and which ones are not. For example, you can specify that only scripts signed using a certificate issued by trusted authorities can be run in your organization. If you list Verisign as the only trusted authority, then only scripts signed with a certificate issued by Verisign will be considered safe and will be able to run. (Of course, if a hacker has obtained a certificate from Verisign, then any scripts he or she writes will be considered safe, whether they actually are or not.)

  • You can be assured that the script has not been changed since the time it was written and signed. If you know that the script ThirdPartyScript.vbs is safe to use within your organization, you can distribute that script with the knowledge that no one can modify the script without violating the digital signature. When a script is signed, a “hash” is derived and used to verify the signature. If the script has been modified in any way, the hash will be invalid, and the script will be reported as having been altered in some way.

When a script is signed, the digital signature is appended to the end of the file as a set of comments. Adding the signature as commented lines ensures that the script can still be run using previous versions of WSH (these versions will see the additional lines as comments rather than as a digital signature). A signature might look like this:

'' SIG '' Begin signature block
'' SIG '' MIIC8AYJKoZIhvcNAQcCoIIC4TCCAt0CAQExDjAMBggq
'' SIG '' hkiG9w0CBQUAMGYGCisGAQQBgjcCAQSgWDBWMDIGCisG
'' SIG '' AQQBgjcCAR4wJAIBAQQQTvApFpkntU2P5azhDxfrqwIB
'' SIG '' AAIBAAIBAAIBAAIBADAgMAwGCCqGSIb3DQIFBQAEEPC2
'' SIG '' QdSn0Xnjl7nT/Xwadl2gggF6MIIBdjCCASCgAwIBAgIQ
'' SIG '' NeMgQmXo1o1F8M6hs6TX1jANBgkqhkiG9w0BAQQFADAW
'' SIG '' MRQwEgYDVQQDEwtSb290IEFnZW5jeTAeFw0wMDEyMjEy
'' SIG '' MzUxMTJaFw0zOTEyMzEyMzU5NTlaMBUxEzARBgNVBAMT
'' SIG '' Ck15IENvbXBhbnkwXDANBgkqhkiG9w0BAQEFAANLADBI
'' SIG '' AkEAx/bBOOqOzdHk2EfxXloUaGo9PtI/HSJ9LQSXkhF7
'' SIG '' neEf4Qy+oyA7NImnOacI+1HDCOAPeKgGJIvaFcZs0BuM
'' SIG '' iQIDAQABo0swSTBHBgNVHQEEQDA+gBAS5AktBh0dTwCN
'' SIG '' YSHcFmRjoRgwFjEUMBIGA1UEAxMLUm9vdCBBZ2VuY3mC
'' SIG '' EAY3bACqAGSKEc+41KpcNfQwDQYJKoZIhvcNAQEEBQAD
'' SIG '' QQA6/fIIDKycSp2DdBT/A3iUSxoiu2BqmEEpVoGKE5yY
'' SIG '' CA3MDWuI29RRvgNJ2oQasb8rZiD5dEexGK3rWEQGV6r+
'' SIG '' MYHhMIHeAgEBMCowFjEUMBIGA1UEAxMLUm9vdCBBZ2Vu
'' SIG '' Y3kCEDXjIEJl6NaNRfDOobOk19YwDAYIKoZIhvcNAgUF
'' SIG '' AKBOMBAGCisGAQQBgjcCAQwxAjAAMBkGCSqGSIb3DQEJ
'' SIG '' AzEMBgorBgEEAYI3AgEEMB8GCSqGSIb3DQEJBDESBBCV
'' SIG '' t6owbn7YLnkAnCqiDdINMA0GCSqGSIb3DQEBAQUABECe
'' SIG '' xmfNlmrIls2kFkyhXOWKicnpOk5iW4twTRNAc4LAkO8M
'' SIG '' uk0ZBCBgR5XC8F7slEMfWCG9R7129EUF4vFhZToK
'' SIG '' End signature block

Enforcing the Use of Signed Scripts

To configure security settings for signed and unsigned scripts, you need to modify the registry key HKEY_LOCAL_MACHINESoftwareMicrosoftWindows Script HostSettingsTrustPolicy. Valid values for this key are:

  • 0. All scripts run without any sort of warning. This is the default setting.

  • 1. Before a script is run, a Security Warning dialog box is displayed showing the security status of the script (signed and verified, signed but not verified, unsigned). The user has the option of running any of these scripts, regardless of their security status. The user can also click a button to view the details of the certificate used to sign the script.

  • 2. Before a script is run, the signature is verified, and a check is made to ensure that the script is coming from a trusted author (someone known to the certification authority). After this verification, the script runs automatically, without giving the user the option to view that signature. If the script is unsigned or the signature cannot be verified, the script will not run, and the user will not be given the option of running the script at their own risk. Instead, the user will receive the following message:

    Execution of the Windows Script Host Failed. (No signature was present in
    the subject.)
    

Programmatically Signing a Script

WSH 5.6 includes the Scripting.Signer object that allows you to digitally sign a script using another script. To do this you need to:

  • Create an instance of the Scripting.Signer object.

  • Use the SignFile method, specifying both the file name of the script to be signed and the name of the digital certificate to be used to sign the script.

For example, this script uses the IT Department certificate to sign the script C:ScriptsCreateUsers.vbs.

set objSigner = WScript.CreateObject("Scripting.Signer")
objSigner.SignFile "C:ScriptsCreateUsers.vbs", "IT Department"

You can also digitally sign a number of scripts at the same time. This script loops through and signs all the files found in the C:Scripts folder. (The assumption is that the only scripts are stored in C:Scripts.)

set objSigner = WScript.CreateObject("Scripting.Signer")
Set objFSO = WScript.CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder("c:scripts")
Set colListOfFiles = objFolder.Files
For each objFile in colListOfFiles
    objSigner.SignFile objFile.Name, "IT Department"
Next

Programmatically Verifying a Signed Script

The Scripting.Signer object can also be used to programmatically verify the digital signature on a script. To do this, you need to use the VerifyFile method, along with two arguments:

  • The file name of the script whose signature is to be verified.

  • A Boolean value indicating whether or not you want the Security Warning dialog box to appear in case the signature cannot be verified. If this value is set to False, the dialog box will not appear. If the value is set to True, however, a dialog box will be displayed, warning you that the script signature could not be verified, and asking you whether or not you still want to run the script.

The following script verifies the signature on the file C:ScriptsCreateUsers.vbs, and suppresses the Security Warning dialog box. The script will return one of two values: True means that the digital signature has been verified, False means either that the script has not been signed or the signature could not be verified.

set objSigner = WScript.CreateObject("Scripting.Signer")
blnShowGUI = False

blnIsSigned = objSigner.VerifyFile("C:ScriptsCreateUsers.vbs", blnShowGUI)
    If blnIsSigned then
        WScript.Echo objFile.Name & " has been signed."
    Else
        WScript.Echo objFile.Name & " has not been signed."
    End If
End If

Alternatively, you might want to use the VerifyFile method to verify the digital signatures on a number of scripts. For example, this script verifies the digital signatures on all the scripts found in the C:Scripts folder.

set objSigner = WScript.CreateObject("Scripting.Signer")
blnShowGUI = False

Set objFSO = CreateObject("Scripting.FileSystemObject")
Set objFolder = objFSO.GetFolder("C:Scripts")
Set colListOfFiles = objFolder.Files
For each objFile in colListOfFiles
    blnIsSigned = objSigner.VerifyFile(objFile.Name, blnShowGUI)
    If blnIsSigned then
        WScript.Echo objFile.Name & " has been signed."
    Else
        WScript.Echo objFile.Name & " has not been signed."
    End If
Next

Restricting the Ability to Run Scripts

By default, double-clicking a .VBS file will immediately run the script. If you modify the registry, however, you can prevent the script from running immediately; instead, a warning box (complete with a customized message) will be displayed.

This does not prevent the user from running the script; he or she can click Open With, locate Wscript.exe or Cscript.exe, and then run the script. Alternatively, the script can be started from the command line by specifying the script host:

wscript.exe DeleteFiles.vbs

cscript.exe DeleteFiles.vbs

However, this approach does provide an extra layer of protection by giving users the option to cancel a script before it runs. Without this option, a script that is double-clicked will run without warning.

The following batch file can be used to modify the registry and force a warning box to appear whenever a user double-clicks a .VBS file. This batch file:

  1. Uses Reg.exe (found in the Windows 2000 Support Tools) to copy the registry tree HKEY_CLASSES_ROOTVBSFileShell to HKEY_CLASSES_ROOTVBSFilekupShell. Backing up this key rather than deleting it enables you to easily restore standard functionality if necessary.

  2. Deletes HKEY_CLASSES_ROOTVBSFileShell.

  3. Adds a new entry (NoOpen) to HKEY_CLASSES_ROOTVBSFile, and sets the value of the entry to the desired warning message. In this sample script, the message — which is limited to 140 characters — is “Do not run this file unless it has been approved by Information Services.”

reg copy HKCRVBSFileShell HKCRVBSFilekupShell /s /f
reg delete HKCRVBSFileShell /f
reg add HKCRVBSfile /v NoOpen /t REG_SZ /d "Do not run this file unless it has
been approved by Information Services."

To restore standard functionality, use this batch file:

reg copy HKCRVBSFilekupShell HKCRVBSFileShell /s /f
reg delete HKCRVBSFilekupShell /f
reg delete HKCRVBSFile /v NoOpen /f

Similar steps can be undertaken to restrict the use of other scripting file types, including .VBE, .JS, .JSE, and .WSF files.

Important

Important

Configuring scripts to display a message box when started could create problems with logon or logoff scripts. Instead of running each time a user logs on, your logon script will display the message box instead. To work around this problem, call the logon script from a batch file instead.

Disabling Windows Script Host

In more desperate circumstances, you can disable Windows Script Host; this will prevent users from running any scripts (including VBScript and JScript scripts) that rely on WSJ.

To disable Windows Script Host, create one of the following two registry entries (REG_DWORD) and set the value to 0 (you need to create the entry, because it does not exist by default). To disable WSH for a particular user, create this entry:

HKEY_CURRENT_USERSoftwareMicrosoftWindows Script HostSettingsEnabled

To disable WSH for all users of a particular computer, create this entry:

HKEY_CURRENT_USERSoftwareMicrosoftWindows Script HostSettingsEnabled

When enforced, the following message will be displayed any time a user attempts to run a WSH script:

Windows Script Host access is disabled on this machine. Contact your
administrator for details.

This message box appear even if the user attempts to start the script from a batch file or using a designated script host (for example, by typing cscript.exe c:scriptsmyscript.vbs at the command prompt).

..................Content has been hidden....................

You can't read the all page of ebook, please click here login for view all page.
Reset