'; window.popUpWin.document.write(zhtm); window.popUpWin.document.close(); // Johnny Jackson 4/28/98 } //--> Windows 98 Professional Reference -- Ch 33 -- Windows Scripting with Windows 98 Scripting Host


Windows 98 Professional Reference

Previous chapterNext chapterContents


- 33 -

Windows Scripting with Windows 98 Scripting Host


This chapter shows you how to use VBScript and the Windows Scripting Host (WSH) to automate administrative and production tasks. Administrative tasks are the little, frequently repeated, but critical configuration or maintenance tasks that are necessary to keep your system healthy. These include tasks such as the following:

Production tasks are those operations that are always a part of completing one's work product. These are often trivial, but are repeated so often that they become frustrating time sinks. Alternatively, they can be error-prone, complex sequences of operations. Production tasks include tasks such as the following:

This chapter shows how you can use WSH and VBScript to automate both administrative and production tasks. I'll introduce WSH scripting through a series of example scripts. Each of these scripts is chosen to illustrate an important scripting technique or an important WSH object. Before attacking the examples, though, I'll give you some background information about VBScript and COM and describe the WSH object model. The background information will help you understand the context in which WSH fits. The WSH object model is the technical foundation you'll need to understand the examples. To help you develop your own scripts, I'll also introduce you to the VBScript Debugger and other tools that make WSH scripting easier. Throughout, though, I'll assume you have some familiarity with VBA or some other dialect of VB. To teach programming (or even Visual Basic) from scratch is far beyond the scope of this chapter.

Background

In part because of the emphasis on user-driven interfaces, prior editions of Windows have not been well suited for production work. Until now, Windows has supplied only one user-oriented tool for automating tasks--the DOS command language. Unfortunately, few applications provide a command-line interface rich enough to allow them to be well controlled from a DOS batch script. While some applications supply a macro language that can support external automation using only the command interface, that solution imposes a heavy learning curve for the average administrator or power user. Stooping to a macro-based implementation for every automation task forces the administrator or user to become expert in every application's user interface and macro syntax. In any but the simplest production environments, that's just not realistic.

Recently Microsoft has pushed two technologies that, combined, address this need. First is the Common Object Model (COM, formerly known as OLE and sometimes as OLE Automation or just Automation). The COM standard specifies how programs can expose their internal objects to other programs. The second technology is Visual Basic; both standard Visual Basic (VB) and Visual Basic for Applications (VBA). VB is a simple-to-learn language that can manipulate COM objects as if they were an intrinsic part of the language. During the last few years, Microsoft has revised their core applications to expose virtually all their functionality through some COM object and to use VBA as the application's internal macro language. Thus, a programmer can now incorporate an Excel spreadsheet, a Word document, or an Access database and all its functionality directly into a new application, just by referencing the appropriate COM object. Obviously, this is very attractive technology--so attractive that there are now hundreds of thousands of programmers conversant in Visual Basic and the use of various COM objects.

While VB and COM are powerful enough to "script" production and administrative tasks, for several reasons, VB isn't quite the right tool for the job. First, VB programs can't be modified outside the VB development environment. That makes VB impractical for scripting because administrative scripts often need to be modified ad hoc" on the station on which they are being used. It's just not cost effective to put a complete VB development environment on every workstation just so the administrator can make a quick adjustment to some script. For the same reasons, VB is not the right tool for production scripting. Production scripts are most effective when users can make changes. Users should be able to adjust scripts for small changes (like different directory names) without calling on the administrator. Finally, VB isn't the right language because it's too big. VB is geared for producing visual user interfaces. That means the runtime library is laden with large, complex visual objects. It's a waste of overhead to load such a library just to connect a network printer or execute a Word mail merge.

Enter VBScript, a VB dialect originally developed for use in web pages. Programs in VBScript are simple text files, so they can be written and modified by anyone with a text editor. Unfortunately, until recently, VBScript could only be executed as part of a web document. Because nearly everyone has a web browser, that wouldn't be a great limitation, but the security needs of a browser and the system-manipulation needs of a scripting language are directly at odds. For a browser to be secure, it should prevent a web page from modifying the file system or launching arbitrary tasks. For a scripting language to be useful, it must allow the script to do just those things and more.

Microsoft has now addressed these VBScript limitations by creating WSH, a stand-alone host that can execute VBScript programs directly. Because WSH occupies a minimal footprint and is being distributed free, it is ideal for production and administrative automation. Sites can easily afford to place a copy of WSH on every machine. (WSH is included as part of Windows 98.) Scripting in VBScript is a natural for Windows administrators because most are already familiar with the language features in VBScript, either from writing application macros in VBA or through using VBScript to create web pages. (The following sidebar, "The VBScript Language Model," explains how VBScript differs from VBA.)

The VBScript hosted by WSH is the same language as that used to control the server in Active Server Pages and to control the browser in active web pages. (In fact, WSH, Internet Explorer, and Internet Information Server all use the same language engine--implemented as a separate OLE component--to interpret VBScript programs.) Even so, because WSH supplies a completely different execution environment (a different object model), using VBScript with WSH is substantially different than using it in web pages. Web scripts must manipulate the browser, various document components, and various server-side services. Automation scripts, on the other hand, must be able to manipulate file systems, execute programs, and configure system parameters. The following sidebar introduces the WSH Object Model.


The VBScript Language Model

VBScript is mostly a proper subset of VBA. (VBScript does include a suite of string manipulation and formatting functions that are not in VBA.) VBScript omits those VBA features that involve significant translation overhead (such as strong type checking facilities) or that comprise a security risk (such as file I/O). VBScript does not support the following:

While there are several other small differences (for example, you can't base arrays), these four categories encompass the most significant differences. As you can see from the following discussion, a lot is left out of VBScript; but then, it needs to be small and quick to fit its intended role.

Types and Type Checking

VBScript is almost type ignorant. While you can still declare variables using Dim, you can specify only one type: variant. Everything--variables, object references, arguments--is represented as a variant. VBScript, however, is not a typeless language. Although everything is represented as a variant, VBS is aware of the underlying type and still warns you if a parameter doesn't match the type requirements of a COM interface. Unfortunately, that's about the only time you'll get warned about type mismatches. Oddly enough, even though everything is represented as a variant, type conversions are not automatic. You must still convert strings to integers before you can perform arithmetic on them.
Besides outlawing the intrinsic types, VBScript also outlaws user-defined types. This is not a language for creating complex data structures! In fact, you can't even use TypeOf to determine the type of existing objects. With only one all-purpose data type, it might seem reasonable to construct all data structures from an all-purpose aggregation, such as a collection. Don't count on it. VBScript also omits virtually all the collection manipulation features of VBA. Add, Count, Item, Remove, and ! are all missing. The only operations supported on collections are For Each .... Next, ordinal selection, and selection using a key. (The keys themselves, though, are inaccessible.)

Although it's easy to say that all the type mechanism is gone, it's hard to get used to the implications. If you've been writing large, object-oriented programs, you'll be pretty uncomfortable when you realize that along with Type ... End Type you've also lost New, ParamArray, Property Get, Property Let, and Property Set.

Control Statements

Several VBA control structures have been omitted, but most won't be missed. The most inconvenient changes are the omission of With ... End With and the restriction of Select Case to single-valued cases. Other omissions include Do Events, GoSub ... Return, GoTo, On Error GoTo, On Error ... Resume, Resume, Resume Next, On ... GoSub, On ... GoTo, line numbers, and line labels.

Although this list includes many error-handling structures, the basic error handler, On Error Resume Next, is still present. The Error Method is gone, but the Err object and all of its properties are still available. Thus, you can still write capable error handlers, but you just have to work a little harder to craft the right expression.

Basic File I/O

What can I say? It's gone. All gone. So is the Debug object. Remember, VBScript was designed to use in web documents. Web documents aren't supposed to do file I/O.

A new runtime library for WSH does, however, include objects that allow you to do file I/O. I'll discuss these in more detail in the section on the WSH Object Model.

Conditional Compilation

Again, VBScript just doesn't have any--but then again, why would an interpreted language have support for conditional compilation? All the same, it would be nice to have a manifest constant and a file include capability.

Other Differences

The other differences are relatively few and innocuous (at least for most scripting users). For example, all financial functions have been omitted; as have the Lset and Rset string functions. You can get a complete language specification by downloading vbsdoc.exe from

www.microsoft.com/scripting/vbscript/download/vbsdown.htm.

Despite these omissions, VBScript is still a powerful, highly usable language. If you're wondering what's left, just scan the lists of keywords and functions in the language specification referenced previously. Even with all the omissions, VBScript is still a rich and versatile language.


The WSH Object Model

The Windows Scripting Host supplies objects to support parameterized execution, system configuration, network configuration, simple user interactions, and file system operations. The objects that supply these services, however, aren't all independent COM components. Many classes are only accessible through the methods or properties of some larger class. Table 33.1 lists the objects and details their access, methods, and properties.

Table 33.1 WSH/VBScript Objects

Class Name Access Methods Properties

Supplied by Wscript.exe or Cscript.Exe

Err Err Description Clear
    HelpContext Raise
    HelpFile  
    Number  
    Source  
Wscript Wscript CreateObject Application
    Disconnect Object Arguments
    Echo FullName
    GetObject Name
    Quit Path
      ScriptFullName
      ScriptName
      Version
WshArguments Wscript.Arguments   Item,
      Count,
      Length

Supplied by WSHom.Ocx

WshShell Wscript. Environment CreateShortcut
  CreateObject SpecialFolders Expand-
    ("WshShell")  
Environment-      
Strings      
      Popup
      RegDelete
      RegRead
      RegWrite
      Run
WshNetwork Wscript. ComputerName AddPrinter-
  CreateObject UserDomain Connection
  ("Wscript.Network") UserName EnumNetwork-
      Drives
      EnumPrinter-
      Connections
      MapNetwork-
      Drive
      RemoveNet-
      workDrive
      RemovePrinter-
      Connection
      SetDefault-
      Printer
WshCollection Returned from Item  
  methods WshNetwork. Count  
  EnumNetworkDrives Length  
  or WshNetwork.    
  EnumPrinter    
  Connections    
WshEnvironment Returned as property Item Remove
  WshShell.Environment Count  
    Length  
WshShortcut Returned from method Arguments Save
  WshShell.CreateShortcut Description  
    Hotkey  
    IconLocation  
    TargetPath  
    WindowStyle  
    WorkingDirectory  
WshSpecialFolders Returned as property Item  
  WshShell.SpecialFolders Count  
    Length  
WshURLShortcut Returned from method FullName Save
  WshShell.CreateShortcut TargetPath  
Dictionary Wscript.CreateObject CompareMode Add
  ("Scripting.Dictionary") Count Exists
    Item Items
    Key Keys
      Remove
      RemoveAll
Drive Select from Drives AvailableSpace  
  collection or as return DriveLetter  
  from method DriveType  
  FileSystemObject. FileSystem  
  GetDrive(drivespec) FreeSpace  
    IsReady  
    Path  
    RootFolder  
    SerialNumber  
    ShareName  
    TotalSize  
    VolumeName  
Drives Collection, available Count  
  as property Item  
  FillesystemObject.Drives    
File Select from Files Attributes Copy
  collection DateCreated Delete
  or returned from method DateLastAccessed Move
  FileSystemObject. DateLastModified OpenAs-
    Drive TextStream
GetFile-      
    Name (filespec)
    ParentFolder  
    Path  
    ShortName  
    ShortPath  
    Size  
    Type  
Files Collection, returned Count  
  from method Item  
  FileSystemObject.    
  GetFolder(folderspec)    
FileSystemObject Wscript.CreateObject Drives BuildPath
  ("Scripting.FileSystem   CopyFile
  Object")   CopyFolder
      CreateFolder
      CreateTextFile
      DeleteFile
      DeleteFolder
      DriveExists
      FileExists
      FolderExists
      GetAbsolute-
       
PathName      
      GetBaseName
      GetDrive
      GetDriveName
      GetExtension-
      Name
      GetFile
      GetFileName
      GetFolder
      GetParent-
      FolderName
      GetSpecial-
      Folder
      GetTempName
      MoveFile
      MoveFolder
      OpenTextFile
Folder   Attributes Copy
    DateCreated Delete
    DateLastAccessed Move
    DateLastModified CreateTextFile
    Drive  
    IsRootFolder  
    Name  
    ParentFolder  
    Path  
    ShortName  
    ShortPath  
    Size  
    SubFolders  
Folders   Count AddFolders
    Item  
TextStream   AtEndOfLine Close
    AtEndOfStream Read
    Column ReadAll
    Line ReadLine
      Skip
      SkipLine
      Write
      WriteLine
      WriteBlank-
      Lines

The services provided by WSH objects are partitioned into six top-level objects: Wscript, WshShell, WshNetwork, Err, Scripting.Dictionary, and Scripting.FileSystemObject.

Wscript is the executive module. It supplies the methods to create and release other objects (including all COM components), to identify the running script and access its arguments, and to generate output messages (the Echo method).

WshShell provides primitive user interface capabilities and access to the local system. Through WshShell, you can manipulate environment variables, the desktop, and the Registry. You can get limited input from the user through the WshShell.Popup method.

WshNetwork supplies access to network resources and configuration. Through this object you can manage network drives and printers.

Err is the same object as available in VBA. By testing Err and querying its properties, you can collect information about program errors.

The Dictionary object implements an associative array. By using CreateObject, you can create as many dictionary instances as you might need. In most scripting applications, the flexibility and convenience of this object more than compensates for the loss of language support for user-defined types.

Similarly, the FileSystemObject compensates for the absence of intrinsic I/O functions. This object provides a rich set of methods and properties to support file and folder manipulation. One of its methods will create a TextStream object--a text file that acts much like traditional DOS or UNIX streams.

The Execution Environment

Microsoft actually supplies two implementations of the scripting host, Wscript and Cscript. Wscript is the native windows version of the host. All of the examples in this chapter were developed and tested under Wscript. To launch a script by using Wscript (assuming it has been selected as the default application for .vbs files) you merely double-click on some visual representation of the script file.

Cscript is a command-line-oriented implementation of the scripting host. You could think of Cscript as the native DOS version of the host. To launch a script with Cscript, you must either use Run on the Start menu or execute a command line in a DOS window.

When scripts execute under Cscript, all output generated by Wscript.Echo is redirected to the DOS standard output channel. Under Cscript, using the OpenTextFile method of the file system object, you can even open the console device (CON: ) and read from standard input.

Thus, in addition to the WSH object model, a script executing under Cscript has limited access to standard input and standard output. Scripts executing under Wscript do not.

Another minor difference between the two hosts is the ease with which you can specify command-line arguments. Under Cscript, the arguments are simply part of the invoking command. If the script is to execute under Wscript, you have three alternatives:

The .wsh file is a control file that specifies the execution parameters using an .ini-like format. Using a .wsh file enables you to override (on a script-by-script basis) the default options for the scripting host. The .wsh file also allows you to specify the command line and arguments for the script.

These minor differences make Cscript a more natural fit for scripts that are to communicate with other DOS applications.

Writing Scripts

This section introduces you to some of the basics of scripting in the WSH environment. It isn't meant to be an introduction to Visual Basic. In fact, this section will be most meaningful to those who have already done some VB programming. This section isn't even meant to be a complete reference to the WSH environment, though in some instances I do give the syntax for specific WSH methods.

The goal in the following is to present a sampler--a collection of scripts and script fragments that will give you a feel for what can be accomplished in WSH/VBScript and what's involved in writing working scripts. To make the material accessible to as many as possible, I begin with some very basic topics, like generating output, and proceed to more complex topics. Except for the last program, I have attempted to keep the listings short, straightforward, and limited to a subset of the language--again, to help those who are not experts in Visual Basic get a feel for what's involved.

For full details about the language syntax and semantics and about the public interfaces to the WSH objects, you should see the reference materials listed in the "Scripting Resources" section near the end of this chapter.

Basic I/O

The simplest WSH script is probably the following one-liner:

MsgBox "Hello World"

This script generates the vbOK-style dialog box in Figure 33.1. As the Visual Basic title bar suggests this program doesn't really use anything special from WSH. In particular, it doesn't use any WSH objects. But, it's important to remember that most of the VBA commands are still available in VBScript.

Figure 33.1 This dialog box is generated by the MsgBox Command. Note the contents of the title bar.

Although MsgBox is still available, administrative scripts should probably generate their output using the WSH Echo method. An Echo-based version of the Hello World program looks like this:

Wscript.Echo "Hello World"

This version uses the Echo method of the WScript object to generate the dialog box shown in Figure 33.2. Note that the Wscript object doesn't need to be instantiated--it's always available to a WSH script.

Figure 33.2 The Echo-based Hello-World script produces this dialog box. Note that the title bar says Windows Scripting Host instead of Visual Basic.

The Echo method also generates a simple vbOK-style dialog box, but the box's title bar reads Windows Scripting Host instead of Visual Basic.

Wscript's Echo method takes any number of arguments, but doesn't really support any variations; you always get a vanilla vbOK-style dialog box that contains a concatenated list of the arguments. Even so, Echo does have advantages over MsgBox. The Echo method aims to be the Windows equivalent of the DOS ECHO command. Microsoft supplies the WSH executive in two different forms: Cscript, the command-line oriented version; and Wscript, the windows version. All output generated by the Echo method is directed to DOS's standard output channel when run under Cscript and to a vbOK dialog box when run under Wscript. Thus, using Wscript.Echo for simple output allows you to interactively test a script in the Windows environment, but then use the production version of the script in a traditional batch environment.

For greater control over the dialog box and for simple input you can use WshShell's Popup method. The Popup method understands most of the standard Visual Basic dialog box styles and returns a value indicating which button the user clicked to close the box. The calling syntax is as follows:

WshShell.Popup strText, [natSecondsToWait], [strTitle],[natType])

Popup returns an integer indicating which button the user clicked.

Because Popup is a member of the Shell object, programs must instantiate the Shell object by calling Wscript.CreateObject before calling Popup. The following program creates an instance of WshShell and then uses PopUp to collect some information about the user:

Option Explicit Dim objWsh Dim natIsOld
Set objWsh = Wscript.CreateObject("Wscript.Shell") natIsMale = objWsh.Popup( "Are you Male?",, _                        "User Information",vbYesNo)

Note the use of Set to save a reference to the object created by the call to CreateObject. If you omit the Set keyword, the following line will generate an Object Doesn't Support this Property or Method error.

The call to Popup creates a dialog box complete with custom title bar (see Figure 33.3). The Popup function then returns either vbYES or vbNO, much like any similar call to VB's MsgBox function.

Figure 33.3 This Popup-generated dialog box has a custom title bar and returns the user's selection as an integer.

Enumerating the Environment

In addition to Basic I/O, the shell object supplies information about the script and its environment. The program in Listing 33.1 uses the Environment property of the Shell to generate a listing of all current environment strings. The report shown in Figure 33.4 illustrates one of the fundamental differences between Windows 98 and Windows NT. Windows NT partitions its variable space--Windows 98 does not. If this script were run on an NT machine, each section would include some output. This WSH Script reports the contents of all current environment variables.

Listing 33.1  Envex.vbs

Option Explicit
Dim strMsg
Dim CRLF
Dim TAB
Dim wshShell
Dim strVar
CRLF = Chr(13) & Chr(10)
TAB = Chr(09)
`Uncomment the following line before debugging 
`with the script debugger
`WScript.Echo "Envex waiting for Debugger"
Set wshShell = WScript.CreateObject("WScript.Shell")
StrMsg = "Current Environment Variables (Default):"
For Each strVar in wshShell.Environment
      strMsg = strMsg & CRLF & TAB & strVar
Next
StrMsg = StrMsg & CRLF & CRLF & "Current Environment Variables (System):"
For Each strVar in wshShell.Environment("System")
      strMsg = strMsg & CRLF & TAB & strVar
Next
StrMsg = StrMsg & CRLF & CRLF & "Current Environment Variables (User):"
For Each strVar in wshShell.Environment("User")
      strMsg = strMsg & CRLF & TAB & strVar
Next
StrMsg = StrMsg & CRLF & CRLF & "Current Environment Variables (Volatile):"
For Each strVar in wshShell.Environment("Volatile")
      strMsg = strMsg & CRLF & TAB & strVar
Next
StrMsg = StrMsg & CRLF & CRLF & "Current Environment Variables (Process):"
For Each strVar in wshShell.Environment("Process")
      strMsg = strMsg & CRLF & TAB & strVar
Next
WScript.Echo strMsg
WScript.DisconnectObject(wshShell)
WScript.Quit

Figure 33.4 The script in Listing 33.1 will always generate a report similar to this when run under Windows 98

The following loop iterates over each of the strings in the Environment collection--available only as a property of the WSH Shell object:

For Each strVar in wshShell.Environment       strMsg = strMsg & CRLF & TAB & strVar Next

Listing 33.1 was written to report on the environment variables of an NT host, which are partitioned into four separate access spaces. Figure 33.4, however, was generated by running the script on Windows 98, in which the environment variables are all contained in a single name space.

You can use similar code and the Wscript.Arguments collection to generate a list of the command-line arguments passed to a script. Scripts can report singular environment characteristics just by echoing the property. For example, the following will report the filename of the script in which it appears:

Wscript.Echo Wscript.ScriptName

The following are the last two lines in Listing 33.1, which release the Script's instance of the WSH shell object, and then quit the WSH executive:

WScript.DisconnectObject(wshShell) WScript.Quit

These lines aren't required. WSH automatically reduces the reference count to the Shell object and exits after it reaches the end of the script. Windows then frees the resources associated with the Shell object. I've included these lines because I believe it's good programming practice to clean up after yourself.

Using the Network Object

The network object, class WshNetwork, includes properties and methods that allow you to identify and manage network resources. Scripts can acquire their host's network name, the current user's name, and the current domain name by retrieving the appropriate properties from the network object.

Enumerating Resources

The network object also includes methods that return a collection of strings describing current network file and printer resources. These collections are of class WshCollection (an all-purpose container class) and are only accessible by walking the members of the collection.

The following two lines create a network object and use it to instantiate a collection of strings describing the network drives:

Set wshNet = WScript.CreateObject("WScript.Network") set drives = wshNet.EnumNetworkDrives

The resulting drives collection is a set of paired strings. The first string in a pair describes the local mapping for the network resource. The second string in a pair is the network path to the same resource. When a network resource has no local mapping, the first string in the pair is a zero-length string. The same convention is used to represent printer resources.

To process these pairs properly, a script must walk the strings in order, keeping track of whether the current string is a drive designator or a path. Listing 33.2 shows how to access these collections to produce a report (see Figure 33.5) of currently connected network file and printer resources. Inside of the For Each loop, a Boolean (blnPath) is used to keep track of whether the current string represents a local mapping or a network path. This WSH Script reports all currently connected network resources.

Listing 33.2  Netex.vbs

Option Explicit
Dim strMsg
Dim CRLF
Dim TAB
Dim wshNet
CRLF = Chr(13) & Chr(10)
TAB = Chr(09)
Set wshNet = WScript.CreateObject("WScript.Network")
strMsg = "Network Properties:"
strMsg = strMsg & CRLF & "Computer Name " & TAB & wshNet.ComputerName
strMsg = strMsg & CRLF & "User Domain "   & TAB & wshNet.UserDomain
strMsg = strMsg & CRLF & "User Name"      & TAB & wshNet.UserName
strMsg = strMsg & CRLF & "Network Drives"
Dim drives
Dim blnPath
set drives = wshNet.EnumNetworkDrives
blnPath = False
For each id in drives
   if blnPath then
       strMsg = strMsg & id
   else
       strMSG = strMsg & CRLF & TAB &  id & " = " 
   end if
   blnPath = NOT blnPath
next
Dim printers, id
set printers = wshNet.EnumPrinterConnections
strMsg = strMsg & CRLF & "Network Printers"
blnPath = False
For each id in printers
   if blnPath then
       strMsg = strMsg & id
   else
       strMSG = strMsg & CRLF & TAB &  id & " = " 
   end if
   blnPath = NOT blnPath
next
WScript.Echo strMsg
WScript.DisconnectObject wshNet
WScript.Quit

Figure 33.5 The script in Listing 33.2 produces this report of network resources.

While it's useful to know what network resources are connected, it's even more useful to be able to modify those from a script. The next two examples show how to remove and connect network resources from a script.

Disconnecting Resources

Listing 33.3 shows how to use the RemoveNetworkDrive and RemovePrinterConnection methods to drop all network connections. The script produces a dialog box that lists the disconnected resources (see Figure 33.6). The Remove methods have this syntax:

WshNetwork.RemoveNetworkDrive strName, [bForce], [bUpdateProfile]
WshNetwork.RemovePrinterConnection strName, [bForce],                                       [bUpdateProfile]

Setting bForce to True will cause the resource to be disconnected even if it is currently in use by someone else. Setting bUpdateProfile to True will cause this mapping to be saved in the user's profile.

Listing 33.3

This WSH Script disconnects all network resources.

Option Explicit
Dim strMsg
Dim CRLF
Dim TAB
Dim wshNet
CRLF = Chr(13) & Chr(10)
TAB = Chr(09)
Set wshNet = WScript.CreateObject("WScript.Network")
Dim drives
Dim count, i
set drives = wshNet.EnumNetworkDrives
count = drives.count
strMsg = "Removing " & count/2 & " Drive Connections"
do while drives.count > 0 
   if drives.item(i) <> "" then 
      strMsg = strMsg & CRLF & TAB & drives.item(i) & " = " & drives.item(i+1)
      wshNet.RemoveNetworkDrive drives.item(i),1
   else 
      if drives.item(i+1) <> "" then 
         strMsg = strMsg & CRLF & TAB & " = " & drives.item(i+1)
         wshNet.RemoveNetworkDrive drives.item(i+1),1
      end if
   end if 
   set drives = wshNet.EnumNetworkDrives
loop
Dim printers
set printers = wshNet.EnumPrinterConnections
count = printers.count
StrMsg = StrMsg & CRLF & CRLF & "Removing " & count/2 & " Printer Connections"
do while printers.count > 0 
   if printers.item(i) <> "" then 
      strMsg = strMsg & CRLF & TAB & printers.item(i) & " = " & printers.item(i+1)
      wshNet.RemovePrinterConnection printers.item(i),1
   else
      if printers.item(i+1) <> "" then
         strMsg = strMsg & CRLF & TAB & " = " & printers.item(i+1)
         wshNet.RemovePrinterConnection printers.item(i+1)
      end if
   end if
   set printers = wshNet.EnumPrinterConnections
loop
WScript.Echo strMsg
WScript.DisconnectObject wshNet
WScript.Quit

Figure 33.6 The script in Listing 33.3 produces this report before disconnecting all network resources.

The Remove methods behave differently depending on whether they are called with a local resource designator or with a network path. When called with a local resource identifier, the Remove method will remove the entire resource. That is, the result of the call will be the removal of both local identifier and network path strings from the WshCollection. On the other hand, if called with a network path, both strings will be removed only if there is no local mapping for the resource.

Thus the loop in Listing 33.3 invokes the Remove method on the local designator, if there is one, and invokes the Remove method on the network path otherwise. After removing a resource, the loop reinstantiates the network object. I found that if I didn't rebuild the network object, subsequent Removes wouldn't properly index the target string.

Connecting Resources

As promised, Listing 33.4 shows how to connect network resources from a script. This example assumes that the network resources are supplied by two different hosts that each require a different password from the user at the beginning of each session. Some users find such situations very confusing. This script simplifies the logon process by asking the user for each password and then using those passwords to connect to the appropriate resources.

Listing 33.4 This WSH Script prompts the user for two passwords and then connects to resources on two different UNIX hosts.

Option Explicit
Dim strMsg
Dim CRLF, TAB
Dim wshNet
Dim strPword, strPword2
CRLF = Chr(13) & Chr(10)
TAB = Chr(09)
Set wshNet = WScript.CreateObject("WScript.Network")
strPword = inputbox("Enter your Department password.", "Department Login")
strPword2 = inputbox("Enter your WebMaster password.",  "Web Master Login")
call wshNet.MapNetworkDrive ("M:", "\\DeptHost\Deptdir", ,,strPword)
call wshNet.MapNetworkDrive ("N:", "\\Gateway\WebRoot", ,,strPword2)
call wshNet.AddPrinterConnection ( "LPT2" ,"\\DeptHost\color 4200",,,strPword)
WScript.DisconnectObject wshNet
WScript.Quit

This example uses the InputBox function from Visual Basic to retrieve the two passwords (see Figure 33.7). Although InputBox is a convenient tool for getting textual input, it's not ideal for a password application because it echoes the password to the screen where others can see it. In a production environment, you would use a COM object for this task.

Figure 33.7 The script in Listing 33.4 generates two password dialog boxes similar to this one.

After the password is available, the script calls AddPrinterConnection and MapNetworkDrive to connect the resources. The syntax for these methods is as follows:

WshNetwork.AddPrinterConnection strLocalName, ___ __      strRemoteName, [bUpdateProfile], [strUser], [strPassword]
WshNetwork.MapNetworkDrive strLocalName, _       strRemoteName, [bUpdateProfile], [strUser], [strPassword]

When the bUpdateProfile flag is True, the mapping will be stored in the user's profile.

Sequencing Jobs

Scripting for production tasks is primarily a matter of sequencing tasks or jobs. To handle this task well in the Windows environment, the scripting language must be able to manipulate not only traditional command-line-oriented applications, but also modern, complex COM objects. VBScript is well suited to this challenge.

Invoking Scripts or Programs

Listing 33.5 shows how to use the Shell's Run method to sequence three other scripts. This example uses the network scripts developed earlier to report on currently connected resources, disconnect all resources, and collect reconnect to specific resources. An administrator might use a script such as this to configure someone's workstation at logon. Alternatively, a power user might create a similar script to quickly reconfigure her network resources as she shifts from one task to another.

Listing 33.5 This WSH Script invokes the scripts in Listings 33.2, 33.3, and 33.4 to reconfigure a user's network connections.

Option Explicit
Dim strDTopPath
Dim wshShell
Set wshShell = WScript.CreateObject("WScript.Shell")
strDTopPath = wshShell.SpecialFolders("Desktop")
Call wshShell.Run(strDTopPath & "\netex2.vbs",,True)
Call wshShell.Run(strDTopPath & "\netex3b.vbs",,True)
Call wshShell.Run(strDTopPath & "\netex.vbs",,True)
WScript.DisconnectObject(wshShell)
WScript.Quit

This example assumes that the three network scripts reside directly on the desktop. The network scripts can be invoked directly (just as if someone had double-clicked their icons), because the WSH executive can exploit the file associations known to Windows.

The syntax for the Run method is as follows:

WshShell.Run strCommand, [intWindowStyle], [bWaitOnReturn]

By default, the Run method will spawn the task and return immediately to the script. In this example bWaitOnReturn is set to 1 to force the script to wait for each command to complete before launching the next command.

Manipulating COM Objects

The script in Listing 33.6 illustrates how easily VBScript can manipulate COM objects. This example uses Word to open a file, perform a mail merge, and print the result. You can do the same by using VB or VBA, but with WSH installed, any user can create or modify such a script without needing a full development environment.

Listing 33.6 This Script opens an instance of Word, opens a file, performs a mail merge, and then prints the result.

Option Explicit
Dim objWrd
Dim wrdDoc
Dim objMerge
Set objWrd = WScript.CreateObject("Word.Application")
'objWrd.Visible = True
Set wrdDoc = objWrd.Documents.Open ("d:\My Documents\labels.doc")
set objMerge = objWrd.ActiveDocument.MailMerge
objMerge.Execute
wrdDoc.PrintOut 
objWrd.Quit 0
WScript.DisconnectObject objWrd
WScript.Quit

As you can see, VBScript makes the connection to the COM object nearly invisible. The challenging part becomes identifying the object, method, or property to perform the desired task. Microsoft's Office components help by recording their macros in VBA. If you need to automate some task involving an Office application, follow this procedure:

1. Open the Microsoft Office application.


2. Choose Macro from the Tools menu and start the macro recorder.


3. Using only keystrokes, perform the operation.


4. Stop the macro recorder.


5. Open the new macro for editing.

When you open the macro for editing, you'll be viewing the VBA code necessary to perform the task without the intervention of a user. This code seldom works directly--it depends too much on decisions made by the user as they position the cursor. The macro code does, however, show you what objects, methods, and properties you need to understand to write the script. Note also, that the method and property references in the macro are all relative to the unnamed Office application (for example, the Word.Application object if the macro was recorded in Word). In the WSH/VBScript environment, you must create an explicit reference to the application object and then invoke the appropriate methods as members of that reference. Thus a Word macro might include only the following (or worse, only MailMerge, since the ActiveDocument is the default document):

ActiveDocument.MailMerge

The VBScript version, however, should look like this:

objWrd.ActiveDocument.MailMerge

After you have identified the key objects and methods by reviewing the macro, you can find the appropriate syntax in the online Visual Basic reference for that Office component. Word Help is not very friendly here--it doesn't index any of the VBA documentation.

If you have Visual Basic available (which sort of defeats the purpose of using VBScript), you can use VB's object browser to get similar information. Open a project (even an empty new project will do), select the References item from the Project menu, and check the Office component you plan to use. For example, checking the Microsoft Word entry makes the type library for Word.Application available. When the appropriate type library is available you can search quickly and conveniently for all the syntax information about any object, method, property, or intrinsic constant.

An Extended Example

As a final example, I've created a script that swaps different sets of shortcuts to and from the user's desktop. With this utility, when I switch from working on graphics projects to working on financial analyses, I can quickly remove all the graphics-related shortcuts from my desktop and replace them with shortcuts appropriate to financial work. When I'm ready to do some programming, I can remove all the financial clutter and replace it with programming shortcuts.

(Note that the Toolbars in Windows 98 implement virtually the same functionality--the shortcuts are just displayed as buttons or icons in the toolbars rather than as icons on the desktop.)

In addition to being a useful tool, this example demonstrates several WSH features. The script uses the Filesystem object to manipulate folders and files and various WshShell methods to create shortcuts. It reads arguments from its command line and manipulates Excel through the application's COM interface. Finally, unlike the other examples in this chapter, it is large enough to be structured as a collection of subroutines.

Overview

This utility uses an Excel Workbook as a database. Each distinct set of shortcuts (I call these sets "worksets") is stored on a separate sheet of the Excel Workbook. Each worksheet lists the information needed to create the related shortcuts with columns for the shortcut name, the program directory, the program name, and any command-line arguments (see Figure 33.8). The utility uses a special worksheet named XCLEANUPX to keep track of which shortcuts it last wrote to the desktop so that the script can reliably remove only its own postings each time it posts a new set.

Figure 33.8 This screen capture shows one of the worksheets in the Excel shortcut database. Notice that each sheet tab has been renamed to reflect the workset's name.

The script expects to be called either with a single argument corresponding to the name of a worksheet in the Excel workbook, or with no arguments.

If called with no arguments, the script self-installs by creating a workset folder in the Start menu, creating submenu items for each of the labeled tabs in the Excel workbook, and adding desktop shortcuts for each of the shortcuts listed in the first worksheet (see Figure 33.9). Before creating the new folder, the script checks for an existing folder. It then creates one shortcut in that folder for each of the labeled worksheets in the Excel workbook. Finally, it finds the first worksheet and creates shortcuts (on the desktop) for each line in the worksheet.

Figure 33.9 The desktop utility installs itself in the Start menu. Note that the shortcuts corresponding to the Notebook worksheet have also been added to the desktop.

Each of these shortcuts invokes the desktop script, passing an argument that specifies which workset is to be installed. (A special argument XCLEANUPX causes Desktop to delete all the shortcuts on the XCLEANUPX page without posting any new shortcuts.)

When invoked with an argument corresponding to the name of a regular worksheet, the Desktop script does the following:

Figure 33.10 shows the results of calling Desktop with the argument wordpad. Note that the three notepad shortcuts present in Figure 33.9 have been removed and replaced by a single wordpad shortcut.

Figure 33.10 This screen capture shows how the desktop looks after the wordpad workset is installed.

The Code

This example makes extensive use of the WshShortcut, the filesystem, and the Excell.Application objects. I'll limit my comments here to the two WSH specific objects.

Creating a Shortcut

The MakeLink subroutine (at the end of Listing 33.7) shows all of the steps necessary to create a shortcut. First, the code creates a shortcut object using the WshShell.CreateShortcut method. Because this method returns an object reference, its return value must be assigned using the Set keyword.

Listing 33.7 This is the code for the Desktop utility.

Option Explicit
Dim strScriptName 
Dim strScriptDir
Dim strXlsName
Dim strRemoveName
strScriptName = "desktop.vbs"
strScriptDir = "d:\windows\desktop"
strXlsName = "desktops.xls"
strRemoveName = "OldLinks"
Dim wshShell
' Initialize shell and retreive path to desktop
Set wshShell = WScript.CreateObject("WScript.Shell")
'wshShell.Popup "Wait for debug"
Dim DesktopPath
DesktopPath=WshShell.SpecialFolders("Desktop")
' Start an invisible instance of EXCEL
' and open the shortcuts file
Dim objXL
Set objXl = WScript.CreateObject("EXCEL.application")
objXL.Workbooks.Open DesktopPath & "\Desktops"
'objXL.Visible = True
'Initialize an instance of the file system object
Dim fs
Set fs= WScript.CreateObject("Scripting.FileSystemObject")
'If called with no arguments, re-install the virtual desktop
'links in the start menu
'Otherwise, install the set requested by the input argument
Dim wshArgs
Set wshArgs=wscript.arguments
If wshArgs.Count = 0 Then
   Call InstallStartLinks(objXL)
   ' and install the first sheet links as the default
   Call InstallNewLinks(objXL, objXl.Worksheets(1).Name)
Else 
   If wshArgs.item(0) = strRemoveName Then
     Call RemoveOldLinks(objXL)
   Else
      If blnArgInSheet(wshArgs.item(0)) Then
        ' this is driven by the OldLinks sheet
        Call RemoveOldLinks(objXL)
        ' this writes a new OldLinks sheet
        Call InstallNewLinks(objXL, wshArgs.item(0))
      End If
   End If
End If
'Done, either because work is done, or because args are wrong
' either way, clean up and quit
objXL.activeWorkbook.Save
objXL.application.quit
' InstallStartLinks creates the Desktops folder, if necessary
' and inserts appropriate shortcuts to this script
Sub InstallStartLinks(objXL)
   'check for WorkSets folder in Startup Directory
   Dim strStartPath, strWorkPath
   strStartPath = wshShell.SpecialFolders("StartMenu")
   strWorkPath = strStartPath & "\WorkSets"
   if NOT(fs.FolderExists(strWorkPath)) Then
      fs.CreateFolder(strWorkPath)
   end if
   Dim objWs
   Dim strSheetName, strFilePath
   For each objWs in objXL.Worksheets
      objWs.activate
      strSheetName = objWs.Name
      strFilePath=strWorkPath & "\" & strSheetname & ".vbs"
      if NOT (fs.FileExists(strFilePath)) then
         ' build an appropriately named shortcut to this script
         Call MakeLink(strSheetName, strWorkPath, strScriptDir, strScriptName, strSheetName) 
      end if
    Next
End Sub
' blnArgInSheet makes certain that a sheet exits with
' a name that matches the requested argument ... just
' in case the script gets called with a bad argument
Function blnArgInSheet(strArgument)
   Dim objWs
   Dim strSheetName
   For each objWs in objXL.Worksheets
      objWs.activate
      strSheetName = objWs.Name
      if strSheetName = strArgument then
         blnArgInSheet = True
         exit function
      end if
   Next
   blnArgInSheet = False
End Function
Sub InstallNewLinks(objXL, strSheetName)
   ' Activate the sheet with the requested set of links
   objXL.Worksheets(strSheetName).Activate
   ' Select the first cell
   objXL.ActiveSheet.Range("A1").Activate
   ' Now loop through the rows until you find a blank
   Dim strShortCutName
   Dim strDirectory
   Dim strShortCutPgm
   Dim strShortCutArgs
   Dim NumRows
   NumRows = 0
   Do While objXL.ActiveCell.Value <> ""
      strShortCutName = objXL.ActiveCell.Value
      'reference the next column
      strDirectory =objXL.ActiveCell.Offset(0,1).Value
      'and the next
      strShortCutPgm =objXL.ActiveCell.Offset(0,2).Value
      'and now get arguments if any
      strShortCutArgs = objXL.ActiveCell.Offset(0,3).Value
      Call MakeLink(strShortCutName, DeskTopPath, strDirectory, strShortCutPgm, strShortCutArgs)
      'now step to the next row
      objXL.ActiveCell.Offset(1,0).Activate
      NumRows = NumRows + 1
   Loop
   'Now copy the range to the OldLinks Sheet
   objXL.ActiveSheet.Range("A1:A10").Copy
   objXL.Worksheets(strRemoveName).Activate
   objXL.ActiveSheet.Range("A1").Select
   objXL.ActiveSheet.Paste
End Sub
' RemoveOldLinks deletes the shortcuts most recently 
' installed by this script and clears the sheet
' named OldLinks as a side-effect
Sub RemoveOldLinks(objXL)
   ' Activate the sheet with the requested set of links
   objXL.Worksheets(strRemoveName).Activate
   ' Select the first cell
   objXL.ActiveSheet.Range("A1").Activate
   ' Now loop through the rows until you find a blank
   Dim strShortCutName
   Dim strFullPath
   Do While objXL.ActiveCell.Value <> ""
      strShortCutName = objXL.ActiveCell.Value
      objXL.ActiveCell.Value = ""
      strFullPath =  DesktopPath & "\" & strShortCutName & ".lnk"
      fs.DeleteFile(strFullPath)
      'now step to the next row
      objXL.ActiveCell.Offset(1,0).Activate
   Loop
End Sub
Sub MakeLink(strFriendlyName, strLinkPath, strDirectory, strProgram, strArgs)
  Dim Shortcut
  ' Create the link file on the desktop
  Set Shortcut = WSHShell.CreateShortcut(strLinkPath & "\" & strFriendlyName & ".lnk")
  ' Set shortcut object properties 
  Shortcut.TargetPath = strDirectory & "\" & strProgram
  Shortcut.WorkingDirectory = strDirectory 
  Shortcut.WindowStyle = 4
  Shortcut.IconLocation = strDirectory & "\" & strProgram
  If strArgs <> "" Then
      ShortCut.Arguments=strArgs
  End If
  ' Save it
  Shortcut.Save
End Sub

After it is instantiated, the shortcut object is initialized by a series of assignments to its various properties. In this instance, the code assumes that the target path and working directory are always the same. In other applications, those paths might be different.

Finally, after the object is initialized, it is saved. The .lnk file is not created until the object is saved.

Using the Filesystem Object

The filesystem object is used to remove shortcut (.lnk) files and to find and create folders. A new filesystem object is instantiated with the following:

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

The same filesystem object is used for all references to the file system, so it only needs to be instantiated one time.

The InstallStartLinks subroutine uses the FolderExists method of the file system to check whether a Worksets folder exists in the Start menu. If not, the subroutine immediately creates the Worksets folder using the filesystem object's CreateFolder method. Later in the same routine, the script uses the FileExists method to determine if new links should be installed in the Worksets directory.

The RemoveOldLinks subroutine uses the filesystem object's DeleteFile method to remove shortcut files.

The syntax for these four methods is as follows:

ObjFs.FolderExists( folderspec )
ObjFs.CreateFolder( foldername )
ObjFs.FileExists( filespec )
ObjFs.DeleteFile filespec [, force]

As you saw from inspecting Table 33.1, these four methods are only a fraction of the methods supported by the filesystem object. This object also supplies all the methods you need to read and write from text files (represented as TextStream objects). For more information about the filesystem object and specifically about using the TextStream object, see the Microsoft Visual Basic Scripting Edition Language Reference, available online at http://www.microsoft.com/scripting/vbscript/download/vbsdown.htm.

Using the Script Debugger

You can get Microsoft's Script Debugger v. 1.0 free from the Microsoft Scripting site, http://www.microsoft.com/scripting. This simple debugger was originally designed to support breakpoint tracing of web-hosted scripts--whether executing in the browser or on the server. It can also be used to trace execution of scripts executing in the WSH environment, although starting a session isn't quite as easy as in the web environment. (More on that later.)

The Script Debugger is very functional despite its simplicity. After it is attached to your running script, the debugger allows you to control execution using the familiar step, step over, and step into commands. An evaluation window (called the command window) allows you to probe variables and interactively experiment with troublesome expressions.

If you are accustomed to working with a type-smart debugger and browser such as the one in Visual Studio, you may be a little disappointed with Script Debugger. The Script Debugger is most useful when applied to the logic of your own script. It doesn't have enough type information or formatting flexibility to help in diagnosing interface problems between your script and a COM object, for example. Don't expect to examine pointer values or other low-level structures in the command window. This debugger doesn't understand much beyond VB strings and integers.

Starting a Session

Unlike the debuggers in an integrated development environment, the Script Debugger doesn't automatically have access to the executing source; you must manually attach the debugging process to the script process you want to examine. To attach the debugger you must perform these steps:

1. Modify the script so that it will pause and wait for user input as soon as it opens.


2. Launch the script.


3. Launch the debugger.


4. If the Running Documents window isn't visible, open it using the View menu.


5. Open the various branches of the Running Documents tree view until you find your script.


6. Double-click on your script. The debugger should load the script file into the source window at this point.


7. From the Debug menu select Break at Next Statement.


8. Click on the dialog (or do whatever else it takes) to make your script resume execution.

The debugger should now take control and halt execution on the first line following the line where your script was waiting.

I like to use the following line to pause the script while I attach the debugger:

WScript.Echo "Envex waiting for Debugger"

You can comment this line out (as in Listing 33.1) when you aren't debugging and then remove the comment apostrophe when you need to start a debugging session. Figures 33.11 and 33.12 show the debugger being attached to a debug version of Listing 33.1. Figure 33.13 shows the easiest way to examine a variable.

Figure 33.11 In this screen capture, the script under test is waiting for user input, and the debugger is active and configured to break at the next statement.

Figure 33.12 At this point, the debugger has full control. The initial dialog box has been dismissed and the debugger has halted the script on the line following the initial call to Echo.

Figure 33.13 Here I have used the debugger to step through the first iteration of the top loop and then printed the current contents of strMsg in the command window.

An Open Architecture

Earlier I mentioned that the VBScript engine was separate from the Windows Scripting Host. This reflects a design decision to separate knowledge about the scripting language from knowledge about how to manipulate the environment. When VBScript is used for client-side web scripting, the environment consists of the resources available in the browser, and the browser is connected directly to the scripting engine (the browser "hosts" the script). When VBScript is used in server-side web scripting, the http server functions as the scripting host.

It is this separation that allows the same language engine to be used in different environments. WSH is just another "container" that knows how to interface with the language engine and that provides certain general services.

One consequence of this design decision is that different language engines can be used with each of the scripting hosts. Microsoft supplies both a Jscript (Java) and VBScript engine with their browsers and with WSH. You can expect to see other language engines available in the future because Microsoft has made public the full specification of the language engine/scripting host interface.

A second consequence is that developers are free to use the language engine and components of the scripting host in their own applications. Thus if you've written an editor, you could add VBScript as your editor's scripting (macro) language by simply embedding Microsoft's scripting control in the application. You must license this technology if you choose to use it, but as of this writing the license "fee" is simply an acknowledgment added to your application's About box.

For more information about this technology, see the ActiveX scripting links on the "Related Links" page at Microsoft's scripting site, described in the next section.

Scripting Resources

The main source for information about VBScript is Microsoft's scripting site at http://www.microsoft.com/scripting. Although most of the information at this site assumes you are doing web scripting, it does have links to WSH resources. In particular, you can download the following executable components from this site:

You can also view and download the latest version of these documents:

There are several discussion groups devoted to scripting:

Conclusion

The Windows Scripting Host opens a whole new realm of applications to Visual Basic programmers and plugs a significant hole in the Windows environment. Now administrators have a lightweight tool that is powerful enough to address the many configuration and monitoring issues present at any large site. Similarly, users and programmers have a convenient Windows-based tool for creating the little "productivity" scripts that make such a huge difference in the efficiency of any computing-intense work activity.

Because Microsoft has made it easy and inexpensive for other developers to add VBScript to their own applications, one can reasonably expect to see growing support for VBScript as a standard application scripting language.

While this chapter is certainly not the definitive reference for either VBScript or WSH, I hope it has convinced you that WSH/VBScript offers a well-chosen alternative for administrative and production scripting. With these examples and the resources named previously, you should have all you need to begin writing scripts for your own use.


Previous chapterNext chapterContents

© Copyright, Macmillan Computer Publishing. All rights reserved.