This article may contain URLs that were valid when originally published, but now link to sites or pages that no longer exist. To maintain the flow of the article, we've left these URLs in the text, but disabled the links.


MIND



Cutting Edge
cutting@microsoft.com        Download the code (3KB)
Dino Esposito

Windows Scripting Host

I
t's been several years since Windows® evolved from a comprehensive graphical environment to a real operating system. But until recently, Windows has not offered a scripting capability like that provided by the now ancient MS-DOS® batch language. The Windows environment is much richer than the MS-DOS environment was, and a good Windows scripting language must be able to access these resources. Windows provides a shell with many objects and pseudo-objects you can manipulate programmatically. There are folders and shortcuts, network connections, printers, dial-up networking, and the Recycle Bin to name a few. You can also manipulate the Windows registry programmatically.
      In this column, I'll take a look at the Windows Scripting Host (WSH), an integrated module that allows you to use VBScript, JScript™, or any other scripting language to automate operations throughout the Windows desktop. With it, you can glue together and drive the various objects of the shell or any other available ActiveX®-compatible server. The source code I've provided shows how to manipulate shortcuts and the registry with JScript and VBScript code.

What is the Windows Scripting Host?
      Suppose you want to create a shortcut on the desktop and you want to do it repeatedly in a batch-mode, non-interactive way. You could write a utility to automate this task, but that would require knowledge of the low-level shell's object model and registry details, not to mention familiarity with COM programming. Writing the utility would take a while to complete and might not be worth the effort.
      With WSH, a parser engine for script languages is embedded in the Windows desktop, as well as in Microsoft Internet Explorer and Internet Information Server. Functionally speaking, the parsing modules found in each host are nearly the same. What changes is the surrounding object model: Dynamic HTML for Internet Explorer 4.0 and the Windows Scripting Host model for the desktop. Moreover, in the Windows desktop a script is identified through its extension (.vbs for VBScript or .js for JScript). This lets you associate shell facilities like additional property sheet pages and context menu items with scripts, as shown in Figure 1. It also means you can double-click a .vbs or .js file in Windows Explorer (or single-click, depending on your Active Desktop™ settings) to run the specified script. In other words, you can consider .vbs and .js new executable extensions, just like the old .bat files.

Figure 1: Script Properties

Figure 1: Script Properties
      The WSH script interpreter hosted in the 32-bit Windows shell provides this solution. It is based on the ActiveX Scripting interfaces introduced with Internet Explorer 3.0, making it language-independent and able to work with any 32- bit Windows platform. There are plans to make WSH a standard part of the Windows 98 and Windows NT® 5.0 operating systems. The engine is already available from http://www.microsoft.com/scripting/, and it integrates well with the current Windows 95 and Windows NT 4.0 platforms. All of the samples discussed here have been tested under Windows 95 and Windows NT 4.0.
      WSH consists of two files, wscript.exe and cscript.exe, which are the Windows-based host and the command-based host, respectively. Both can execute scripts written in any language that supports the ActiveX Scripting interfaces. Microsoft provides an engine for VBScript and JScript. Third-party parsers are available for other well-known script languages such as Perl, TCL, and Rexx.

Running Scripted Batches
      "Scripted batch" is not a standard expression. However, I think it's a good way to think of WSH. The scripting host exposes an object model that renders a portion of the Windows desktop. The script language is a flexible and powerful tool to leverage such objects, giving you the same simple approach provided by MS-DOS batch files.
      There are two ways to run such script files. The first is by invoking the command-based scripting host (cscript.exe). The syntax is:

 cscript filename [//options] [/arguments]
The options refer to features of WSH such as the working mode (interactive or batch) and whether to display the banner at execution time. They are the arguments of the cscript.exe engine, and are all prefixed by double slashes.
 cscript myScript.vbs //I //logo
The cscript command-line options are listed in Figure 2. (wscript.exe offers the same options with one difference: it doesn't suppport the logo and nologo options.) After the scripting host options, you provide the arguments you want the script itself to receive through the command line. Each script argument is prefixed with a single slash.

Figure 3: Running shortcut.vbs from the console
Figure 3: Running shortcut.vbs from the console


      There are very few differences between cscript and wscript. Cscript is a console-based application and runs inside an MS-DOS box (see Figure 3). The first two lines displayed are the banner, a feature you can turn on and off using the logo and nologo options discussed earlier. The example shown in Figure 3, shortcut.vbs, is provided with the WSH package. It creates a shortcut to Notepad on your desktop. As you can see, this script is interactive and prompts the user for confirmation. If you try to run it this way

 cscript shortcut.vbs //B
it won't work because the //B argument suppresses the interactive operations.
Figure 4 shows an alternative way to run scripts from the shell. In this case, the VBScript files are registered to be executed by wscript.exe, and you won't see the MS-DOS box in the background. To see how this works, take a look at the registry under the key HKEY_CLASSES_ROOT\VBSFile\ Shell\Open\Command\.

Figure 4: Using wscript.exe
Figure 4: Using wscript.exe


      You can stop any script that is running inside the WSH after a specified number of seconds. This is to prevent poorly designed or overly greedy scripts from consuming too many machine cycles. The ActiveX Scripting engine actually kills the script, and this operation is generally safe. The InterruptScriptThread function, which is a method exposed by the IActiveScript COM interface, accomplishes the program interruption.

The Role of WSH Files
      I mentioned earlier that .vbs and .js files have an additional property page associated with them. If you modify the settings presented in the page and apply the changes, the system automatically creates a file with the same name as the script but with a .wsh extension. A .wsh file is an ASCII file that follows .ini file conventions. Here's a typical example:

 [ScriptFile]
 Path=C:\SHORTCUT.VBS
 
 [Options]
 Timeout=10
 DisplayLogo=1
 BatchMode=0
The file stores user preferences such as interactive or batch mode, and gives system administrators a way to let different users run the same script with different settings.
      The .wsh file actually provides a shortcut to the .vbs or .js file. This means that you can run the script through its .wsh file only if the actual script file is still present and accessible. Both cscript and wscript read the [ScriptFile] section to get the name of the actual script file, then read the environmental arguments from the [Options] section that follows it.

The WSH Object Model
      WSH is a small application that hosts an ActiveX Scripting engine, just as Internet Explorer does. In his Extreme C++ column (MIND, August 97), Steve Zimmerman demonstrated how applications could host such engines and support VBScript or JScript-based programs. Microsoft recently released the Script Control for this purpose. WSH works the same way; cscript and wscript simply interpret script files and pass them to the various IactiveScriptXXX interfaces. WSH identifies the proper scripting engine to load by looking at the file extension. The scripting engine integrated into Internet Explorer relies on the LANGUAGE attribute of the <SCRIPT> tag to get the same information.
      WSH helps you manipulate any automation object installed on your machine. It also defines its own object model, which is somewhat similar to the Windows desktop. There are three basic tasks you can perform with WSH: handling WSH objects and ActiveX objects, manipulating the registry, and accessing the file system through FileSystemObject. Under Windows NT 5.0, you can also use WSH to access the Windows NT Active Directory service.
      There are three main objects in the WSH object model: WScript, WshShell, and WshNetwork (see Figure 5). WScript renders the entire scripting host. WshShell and WshNetwork are helper objects that map some functions of the Windows shell and the network. For example, they let you map and unmap printers and remote drives and manipulate the registry. These three objects must be created via their ProgIDs or CLSIDs, just like any other COM object.
      The WScript object exposes properties that tell you the path of the running scripting host (wscript.exe or cscript.exe), its arguments, and the working mode (interactive or batch). The WScript object also provides methods to create and read objects.
      The shell object is a bit more interesting. While it doesn't expose all the properties and methods you could imagine or desire, WshShell does allow you to start a new process and create shortcuts. It also provides the Environment collection to handle environmental variables such as WINDIR, PATH, or PROMPT.

 Set shell = WScript.CreateObject( "WScript.Shell" )
 WScript.Echo shell.Environment.Item("WINDIR") 
 WScript.Echo shell.Environment.Item("PATH") 
 WScript.Echo shell.Environment.Item("PROMPT")
 WScript.Echo shell.ExpandEnvironmentStrings("%WINDIR%") 
      Note that some older documentation speaks of methods such as GetEnvironmentVariable or DeleteEnvironmentVariable. They no longer exist. Instead, the Environment property object points to a collection of variables. This collection has an Item property through which you can attain the state of the system environment. The Environment object exposes the usual collection methods and properties such as Item, Count, and Length, plus a Remove method for removing an environmental variable:

 shell.Environment.Remove( "PATH" )
Count and Length both return the number of elements included in the specified collection. WSH supports both for compatibility purposes.
      The ExpandEnvironmentStrings method can format strings that include environmental variables if they are bracketed by % characters:

 Wscript.Echo shell.ExpandEnvironmentStrings( _
 "The Windows directory is: %WINDIR%" ) 
      The Windows shell contains a number of special system folders such as Desktop, Start Menu, and Send To. The actual paths to these folders are returned by the SpecialFolders method (I'll discuss this more later). The Run method has an interesting feature. The syntax is:

 WshShell.Run strCommand [,iWindowsType] [,bWaitOnreturn] 
The strCommand parameter is the command line for the process to be created, while iWindowsType defines the status of the window. bWaitOnReturn is a pleasant surprise; you can control a script's synchronization by setting it to FALSE or TRUE. If you want your script to wait for the spawned process to terminate, then set that argument to TRUE. By default, the new process starts while the script continues its execution.
      The WshNetwork object maps the network, making it easy to connect and disconnect remote drives and printers. In addition, it provides easy-to-use properties for obtaining information about the current user and profile. The following snippet will echo the current computer name and user name:

 Dim objNet 
 Set objNet = WScript.CreateObject( "WScript.Network" )
 Wscript.Echo objNet.ComputerName & " - " & objNet.UserName
      Underneath the objects I've discussed, there are a few other objects that are not exposed to programmers directly via a ProgID. A list of these objects is shown in Figure 6. Among them you can find a WshShortcut object that presents all the features of a desktop shortcut, including the working directory, the target path, the hotkey, and the default icon. A shortcut is returned by the shell's CreateShortcut method. Other minor objects include the WshArguments collection, which holds the list of arguments for the script, and WshEnvironment, which contains environmental variables.
      Except for the WScript object, which is always running, the exposed objects must be created each time you need them. You can use familiar names to access the shell and network classes instead of memorizing ProgIDs:

 objShell = WScript.CreateObject( "WScript.Shell" )
 objNetwork = WScript.CreateObject( "WScript.Network" )
The WScript object is contained in both wscript.exe and cscript.exe (which is why it's always running). All other WSH objects are implemented in a COM automation component, wshOM.ocx, in the Windows system directory.
      When creating a new object you have two choices. You can use WScript.CreateObject and rely on the WSH root object to create your object, or you can use the VBScript CreateObject function or the JScript built-in ActiveXObject. Which is the faster method? My tests suggest that the CreateObject/ActiveXObject method is slightly faster and requires less memory. This is to be expected; since the scripting engine is already running, asking it to create a new object shouldn't introduce any additional overhead.

Creating Shortcuts
      Now let's look at some programming details with regard to the WSH object model. The samples you can download from the Web cover most of the circumstances in which you might want to use WSH. One of these samples, AddUsers, doesn't work properly unless Windows NT 5.0 Server is installed. AddUsers adds a list of new users to the Directory Service via the Active Directory Services Interface (ADSI). The script reads user account information from a Microsoft® Excel spreadsheet. For more information about the new Windows NT 5.0 Active Directory Service, refer to the 1997 Microsoft Professional Developers Conference proceedings, found on the latest MSDN™ CDs.
       Figure 7 presents a sample WSH script from the Microsoft Scripting Web site. It demonstrates how to create a shortcut quickly. First, the code instantiates a shell object using the standard VBScript CreateObject function. Next, it calls the CreateShortcut method and creates a new .lnk file in the current desktop directory. This directory is returned by the SpecialFolders method, which is a wrapper for the SHGetSpecialFolderLocation API function. A shortcut is rendered through an IShellLink COM interface. Many of the functions exposed by this interface are mapped to the WSHShortcut object's methods and properties.
      With JScript, the same shortcut script can be written as:


 Dim objShortcut
 objShortcut = WSHShell.CreateShortcut("notepad.lnk");
 objShortcut.TargetPath = "notepad.exe";
 objShortcut.WorkingDir = "c:\";
 objShortcut.WindowStyle = 4;
 objShortcut.IconLocation = "notepad.exe, 0";
 objShortcut.Save();
This will give you a .lnk file that contains all the information needed to point to Notepad.exe. This operation is performed in a snap and without any user intervention.
      Using the same technique, you could also add new items to the Start menu. Here's a VBScript sample that adds the Paint program (mspaint.exe):

 Dim objShell, strPath, objShortcut
 Set objShell = WScript.CreateObject( _ 
     "WScript.Shell" )
 strPath = objShell.SpecialFolders( _ 
     "StartMenu" )
 Set objShortcut = objShell.CreateShortcut( _ 
     strPath & "\Paint.lnk" )
 strAcc = "C:\Program Files\Accessories\"
 objShortcut.TargetPath = strAcc & _ 
     "MSPAINT.EXE"
 objShortcut.WindowStyle = 4
 objShortcut.Save

Manipulating the Registry
      The shell object provides a direct way to access the registry. This is both a good thing and bad thing since the registry is such a central and crucial database in all 32-bit Windows operating systems. Uninformed modification of the registry can cause dire results. The Win32® API defines many functions to read and write to the registry. However, they have been designed at a low level of abstraction, so you need at least three operations to read just a single value. The WSH object model provides methods that simplify and automate registry operations. System administrators can use this to create a simple, easy-to-use tool for giving rights to users.
      Administrators can also use the WSH object model to prevent users from making changes to the registry. To manipulate the registry you need an executable program or interactive tools like the Registry Editor, and this access can be disabled with a script. You can write a .vbs file that disables the Registry Editor, then store this script in the Startup directory so it's executed at each logon. The code in Figure 8 shows a VBScript file that does just this. The script also displays a popup message at each logon. The following code shows how to disable the Registry Editor:


 s1 = "HKEY_USERS\.Default\Software\Microsoft\Windows\"
 s2 = "CurrentVersion\Policies\System\DisableRegistryTools"
 objShell.RegWrite s1+s2, 1, "REG_DWORD"
This code writes a DWORD value into a new or existing value called DisableRegistryTools, which is located on the registry path HKEY_USERS\Default\Software\Microsoft\Windows\ CurrentVersion\Policies\System\.
      RegWrite supports only a few types: REG_DWORD, REG_ SZ, REG_BINARY, and REG_EXPAND_SZ. Any necessary conversions are performed by the shell object method because neither VBScript nor JScript recognize data types. To support more types, you might want to write your own component to edit the registry. You can access a remote registry using the Win32 RegConnectRegistry API function. The listing shown in Figure 8 uses the RegWrite method to define a popup message that will be displayed during the logon process.
      The RegWrite method exposed by the WshShell object lets you access any key within the system registry and write any of the most common types of data. You can create new keys, too. The WSH objects let you, as a system administrator, use script code to quickly customize the appearance and function of a connected workstation. You could also use a .reg file for the registry manipulation, but the scripting method offers a more readable syntax, along with a flexible and powerful language.

Accessing Generic COM Servers
      WSH doesn't end there. Any COM Server object that is installed on your machine is eligible for use by the scripting host. The object model is not comprehensive, but you can access everything that exposes a COM-compliant interface. Here are a couple of brief examples.


 Dim objWord
 Set objWord = WScript.CreateObject("Word.Application")
 WScript.Echo objWord.RecentFiles.Item(1)
      This code shows the first document in the Microsoft Word recent file list. The samples available on the Microsoft Scripting site also include a demonstration of driving Microsoft Excel documents from within WSH. Another interesting example is the following:

 Dim objShell
 Set objShell = WScript.CreateObject("Shell.Application")
 objShell.FindFiles
This runs the standard Find Files system dialog you see when you select the Find | Files or Folders item from the Start menu.
      Through WSH you can easily access the local file system through FileSystemObject. This lets you create directories, obtain information about available disk space, and even create and edit files. To perform basic operations such as creating a new directory, you can take a couple of approaches. One approach is to ask the shell object to run command.com:

 set o = CreateObject( "WScript.Shell" )
 o.Run( "command.com /c mkdir " + "c:\NewDir")
If you do this, be certain to specify the full path for the new directory. Otherwise, it will be created under the working directory for the MS-DOS prompt, which is usually the Favorites folder.
      A better solution is to use FileSystemObject:

 set o = CreateObject( "Scripting.FileSystemObject" )
 o.CreateFolder "c:\NewDir" 
If you save these two lines in a .vbs file and run it, a new directory will be created on the C: drive. This is the starting point for a more interesting utility. Figure 9 shows how simple VBScript code could help you create a folder anywhere. Just define a shortcut, have it point to folder.vbs, and associate it with a hotkey. You can create a new directory without walking all of the Windows submenus. Figure 10 shows it in action (I used Ctrl+Alt+P as the hotkey).
Figure 10: Creating a new directory
Figure 10: Creating a new directory

Registering a New Script Engine
      Throughout this article, I've discussed VBScript or JScript as the scripting engine. What if you want the same kind of shell support for Perl scripts? You can do this with the RegistryEditor.
      First, create a new key with the name of the extension you want (for example, .ps) under HKEY_ CLASSES_ROOT. Assign it a descriptive string as its default value, such as Psfile. Create a new key called Psfile under HKEY_CLASSES_ROOT. Add a few entries under the Psfile key such as Edit, Open, and Print, and link them to their respective executables. For example, designate Notepad.exe to edit or print a file and wscript.exe to run it. Now add a ScriptEngine key whose default value will identify the engine (for example, PerlScript). Finally, under the PerlScript key, register the CLSID of the COM module that is compliant with the ActiveX Scripting specifications. This module will provide a parser for the Perl scripting language. You need to create a PerlScript key under HKEY_CLASSES_ROOT with a subkey named CLSID that points to the actual CLSID of the module. To get it all working well, you must have correctly registered such a server.

Summary
      Originally, WSH was intended to be a feature exclusive to Windows 98 and Windows NT 5.0. It was not included in the Active Desktop shell update, though a document describing the underlying object model has been around for a while.
      If you're running Windows 95 or Windows NT 4.0, you can download WSH from the Microsoft Scripting site. Once you've installed it, you can start treating VBScript and JScript files like a new kind of executable. The information stored in the registry associates .vbs and .js files with the WSH modules, so you can treat them like programs and even place them in the Startup directory. Script support is common in both development tools and applications. Now it finally exists at the system level as well.


From the June 1998 issue of Microsoft Interactive Developer.