From the May 2002 issue of MSDN Magazine

MSDN Magazine

Scripting

Windows Script Host 5.6 Boasts Windows XP Integration, Security, New Object Model

Dino Esposito
This article assumes you're familiar with Visual Basic .NET or C#
Level of Difficulty     1   2   3 
Download the code for this article: WSH.exe (39KB)
SUMMARY Windows Script Host (WSH) 5.6, a major upgrade for the WSH environment, provides some significant improvements over previous versions. A brand new security model that is tightly integrated with security in Windows XP allows administrators to place fine-grained restrictions on scripts reducing the risk from malicious code. In addition, local scripts can now run on remote machines, and enhancements to the object model reduce the amount of boilerplate code needed when writing professional code. This overview of WSH 5.6 explains these changes and how .NET and scripting work together.

Windows® Script Host (WSH) version 5.6 is the latest release of the script host for 32-bit Windows systems. As a native component of Microsoft® Windows XP and the upcoming Windows Server 2003, WSH 5.6 provides the environment for creating and hosting scripts that can perform both local and remote system administration tasks. WSH 5.6 also runs on Windows 2000, Windows Me, Windows 98, and Windows NT® 4.0. If you're using one of these older systems, an add-on version of WSH 5.6 can be downloaded from the scripting section of the MSDN Web site at https://msdn.microsoft.com/scripting.
      Although WSH comes with a rather compact object model to facilitate typical Windows administrative tasks, it provides nearly complete access to any imaginable task in Windows through script. WSH allows you to run scripts that use Windows Management Instrumentation (WMI) and Active Directory® Services Interface (ADSI) functions. In addition, it provides programmable objects representing the system shell, the system registry, and the file system, in addition to an infrastructure to control remote scripts and secure your local system against malicious programs. WSH is definitely evolving to integrate with .NET, but full integration is still a little way off.
      In this article I'll describe the new features in WSH 5.6. You'll also see how .NET and scripting work together. In particular, I'll explore some of the techniques you need to set up WSH scripts that call into components based on the .NET Framework. Finally, the sidebar "The Road to Scripting in .NET" illustrates a component that compiles and executes C# code on the fly in much the same way as WSH does with VBScript and JScript® sources.

What's New in WSH 5.6


      Since the version number for WSH jumped from 2.0 (the previous version released in the summer of 1999) to version 5.6, you might think you missed a lot of major releases in the past couple of years, but you didn't. The jump simply corrects a numbering issue.
      The new features in WSH 5.6 reinforce security through administrative privileges, code signing, and execution control. You might think that executing scripts is relatively safe because scripts aren't compiled modules and cannot access the whole spectrum of Win32® SDK functions. As recent e-mail viruses prove, however, a malicious script can be as harmful as a more conventional Win32-based executable deliberately designed to break systems. WSH 5.6 gives system administrators the right tools to control the execution flow that a WSH script may trigger.
      In WSH 5.6, you will also find some significant improvements for developers, such as new collections to manage script arguments. These new features are meant primarily for simplifying system administration and increasing the level of security that can be exercised on running scripts.
      The new features of WSH 5.6 can be summarized as follows:

  • A new security model that also addresses code signing and verification
  • The ability to execute scripts remotely
  • The ability to determine the status of spawned processes and access their standard I/O streams
  • A brand new mechanism to improve argument handling

After taking a look at the new XML file format and tags, I'll discuss these new features.

New File Format and Tags


      In addition to plain .vbs and .js files, WSH now supports an XML-based file format that enables you to package your scripts in jobs, which I'll explain later. This XML format is called the Windows Script File (WSF) and has an extension of .wsf. This file format greatly simplifies writing complex and articulated code. Basically, the XML schema allows you to embed extra information within the same file that is useful when running the code, although it is not strictly part of the VBScript or JScript syntax. The script source code becomes the contents of a particular XML node that the WSH runtime extracts and executes. Other nodes in the XML file let you insert directives for the runtime as well as import external code.
      All the information that logically pertains to a given script is called a job and is wrapped by a <job> tag. The same physical WSF file can include more jobs, each identified by a unique ID and all wrapped by an outer <package> tag.

  <package>
  
<job id="job1">
</job>
<job id="job2">
</job>
•••
</package>

 

      In particular, the tags in the WSF schema let you include external files and import constants from type libraries. The XML format is also the key to running different languages in the same script. WSH, in fact, has the ability to host multiple scripting languages and also supports calls from one language to functions written in another language.
      WSH 5.6 expands the types of metadata you can associate with a script. New tags have been introduced to create usage information and to define command-line argu- ments. One consequence of this improvement is that WSF scripts running under the control of WSH 5.6 don't need to use any kind of boilerplate code to detect the /? switch on the command line for the correct way to execute the script. The WSH 5.6 object model has a new method called ShowUsage, which can be called programmatically to show a pop-up window describing the usage of the script. This method is automatically invoked by the WSH runtime when the script has the /? switch. All the rest happens automatically, thanks to the WSH 5.6 runtime. I'll discuss ShowUsage and other benefits of metadata later.

Refreshing the WSH Object Model


      The WSH object model did not undergo a significant restyling in the transition from version 2.0 to 5.6. Aside from the necessary changes brought about by the introduction of remote scripting and script controllers, the set of methods and properties for the main objects remained nearly identical. (Don't confuse WSH remote scripting with client-side remote scripting you can use to call ASP function from within HTML pages.)
      Figure 1 shows the properties and Figure 2 the methods of the WScript object—the root object of the WSH object model. From this object, you can create three new objects: WScript.Shell, WScript.Network, and WScript.WshController. The Shell object provides access to the native shell for Windows and some of its core objects, such as the registry, shortcuts, special folders, and the memory environment. The Network object gives you access to the shared resources on the network to which the computer is connected. Finally, WshController—new to WSH 5.6—creates and controls a remote script process, a script that you run on another computer system but spawn and control from your own.
      A fourth object that depends on WScript is WshArguments. Unlike the other three, you cannot create WshArguments directly through CreateObject and a unique ProgID. Instead, a script must obtain a reference to an existing instance of this object. When a script references the WScript.Arguments collection for the first time, a WshArguments object is implicitly created, initialized, and returned to the caller. WshArguments provide access to the entire collection of command-line parameters in the order in which they were originally specified.
      The WScript object has three distinct properties to manage the three most important I/O consoles available: StdIn, StdOut, and StdErr. The StdIn property represents the read-only input buffer. The StdOut property represents the write-only output stream, and the property StdErr is the write-only stream where error messages will be output. These properties only work when the script is run with CScript.exe. If the script is run with WScript.exe, any attempt to access any of the streams results in an invalid handle error.
      These three properties are all implemented through a TextStream object. This object was originally introduced with the scripting runtime to facilitate sequential access to a file's content. It exposes read/write properties, but these are filtered by the WSH runtime to ensure that you can't write to the StdIn stream or read from the StdOut stream.
      The following example shows how to ask for text and to display what has been entered.

  Set inp = WScript.StdIn
  
WScript.Echo.Write("Enter some text and press [Enter]: ")
text = inp.ReadLine()
WScript.Echo vbCrLf & "You entered: " & vbCrLf & text

 

ReadLine returns all the text entered until it encounters a carriage return. Read expects an integer argument that denotes the maximum length of the returned text.
      The StdErr output can be redirected to a file by specifying the handle for StdErr (2), followed by > and the name of the file.

  cscript.exe script.vbs 2 > error.txt
  

 

      The Arguments property returns the collection of the command-line arguments for the script. Such a collection is rendered by the WshArguments object. You already know about the number of arguments through the Count property and you know how to retrieve individual arguments using a zero-based index.

  Set args = WScript.Arguments
  
For i = 0 to args.Count - 1
WScript.Echo args(i)
Next

 

      WSH prepares and fills the Arguments property only on demand. Arguments returns an empty, but non-null object if no argument has been specified. The WshArguments object has two new properties in WSH 5.6, Named and Unnamed, which contain only the script arguments that have been given (or not given) a name in the <runtime> section of the script. I'll discuss these properties in more detail later in this article.
      The WshShell object has a new property called CurrentDirectory that gets and sets the current working directory for the active script host—wscript.exe or cscript.exe. This property defaults to the root of the C drive, not the path of the current script. Figure 3 summarizes the methods that the WshShell provides in WSH 5.6.
      The WSH programming interface for the registry provides only basic functionality—reading, writing, and deleting—and has limited support for types. The registry methods lack the ability to enumerate the subkeys or the entries found under a certain node. Therefore, for more advanced registry operations, you must use an ad hoc third-party component or the WMI Standard Registry Provider (StdRegprov).
      Aside from supporting the enumeration of network shares and printers, the WshNetwork object also provides properties that return the local computer, domain, and user names. This information is not particularly useful if you want to bind to the user or the computer object in an Active Directory scenario. In this case, you are better off using the ADSI properties User-name and Computername through the ADSystemInfo object.
      More information on the WSH object model and its most recent features, plus many practical examples, can be found in the Microsoft Windows XP Professional Resource Kit. Next, I'll review in more detail the new features of WSH 5.6, starting with the enhancements that have been made to system and code security.

Safe Scripting Administration


      WSH was originally designed to be a tool for system administrators to programmatically access programs, the file system, e-mail, FTP sites, and so forth. Scripts are not encrypted nor signed with certificates, and can come from any Internet source, including untrustworthy ones. Once WSH is properly installed on a given machine, the system treats files with a .vbs or a .js extension just like any other binary executable. (Remember, scripts have become the preferred vehicle for transmission of viruses, such as ILOVEYOU and AnnaKournikova.)
      WSH 5.6 has several features that provide for secure script execution. These features include the ability for administrators to enforce strict policies that determine which users have privileges to run scripts locally or remotely. Administrators can also configure WSH to authenticate a script before running it.
      A script is considered authentic and guaranteed when it comes with a digital certificate issued by a trusted authority. If a script has a certificate, WSH can immediately detect unauthorized modifications and, if found, will return an error at run time. WSH 5.6 scripts can come with a digital certificate provided by one of several certifying authorities. WSH checks the embedded certificate against your machine-specific list of trusted sources, and runs the script only if a match is found.
      There are two main security mechanisms in WSH 5.6 that you can use to design a security model that is tailor-made to your organization. One is based extensively on Windows XP security features, and the other is backward compatible with previous versions of Windows. I'll explain these two models next.
      Windows XP provides a suite of settings to control which software can run on your machine and how it can run. These settings are known globally as Software Restriction Policies (SRP) and apply to all executable code installed on, and running from, Windows XP. So SRP is not something specific to script, but is a more general mechanism built directly into the operating system machinery. Administrators and programmers can control the settings of SRP through the Local and Group Policy editor (see Figure 4).
      SRP is based on a set of rules that apply to the software available from a given machine in the network. A typical example of these security rules might be a statement that says no EXE file can run from a given folder or Web site. A common SRP setting may force users to run only scripts signed with a particular certificate. You could even disable WSH completely in this way. The dialog box in Figure 4 shows that an additional rule has been defined to disallow all .vbs files from running on the specific machine under Windows XP. If you attempt to violate one of the SRP path rules, you'll get an error (see Figure 5).
      A path rule prevents all scripts from running whether they come from a trusted source or not. To allow certified scripts to run normally, you must set up a certificate rule, as shown in Figure 6.

Figure 6 Certificate Rule
Figure 6 Certificate Rule

      You create a certificate rule by right-clicking in the right panel of the SRP snap-in and selecting the proper menu item. You select the certificate that must be used to sign the scripts you accept and set the security level to unrestricted. The certificate must be registered in the system and issued by a recognized authority (see Figure 7). For testing purposes, you can get a test certificate from the Windows 2000 Certificate Server or from tools like selfcert.exe (which comes with Office 2000). When running on older versions of Windows, WSH 5.6 uses a registry entry to store information about restriction policies. The registry key is:

  HKEY_CURRENT_USER
  
\Software
\Microsoft
\Windows Script Host
\Settings

 

      The TrustPolicy entry can take any one of the three integer values that are listed in Figure 8. Of course, administrative privileges are required to modify the TrustPolicy key; the average user won't be able to make these changes.

Script Authentication and Signing


      Verifying a script is the process that determines whether the script that you are about to run is coming from a trusted source. Prior to WSH 5.6, you used the signcode.exe tool to encrypt script files. This tool is not strictly part of any version of the Microsoft Windows operating system. Rather it is part of the Microsoft Windows 2000 Platform SDK and is a separate download. The WSH 5.6 object model ships with a new object called Scripting.Signer that does the same thing. The following simple script signs a file using the specified certificate and store:

  Set args = WScript.Arguments
  
Set Signer = CreateObject("Scripting.Signer")
File = args(0)
Cert = args(1)
Store = args(2)
Signer.SignFile File, Cert, Store

 

      Script verification can also be accomplished programmatically through the methods of the Scripting.Signer object. You use the VerifyFile method to verify the validity of the embedded signature and check that it belongs to a person whom you trust, listed in the Trusted Publishers list. In addition, the VerifyFile method makes sure that the script has not been tampered with since it was signed. Although the VerifyFile method programmatically confirms the digital signature, bear in mind that security is about mutual trust. Therefore you should periodically verify that you really trust the sources listed in the Trusted Publishers list.
      The Scripting.Signer object enables a developer to sign a script with a digital signature. To work properly and sign the given script file, the object requires a valid certificate. Figure 9 lists the methods of the Scripting.Signer object. SignFile needs to process the certificate by name and assumes that all certificates are in a store called "my." This is the default store for certificates that contain private keys, and they are the certificates you must use for code signing. However, you can indicate any valid store according to the certificate's settings:

  Set Signer = CreateObject("Scripting.Signer")
  
Signer.SignFile ScriptFile, CertificateName, CertificateStore

 

      In addition to signing a script file, you can also sign code stored in an in-memory string by using the Sign method. You must indicate the file extension of the script as a way to help the operating system detect the type of the script file being verified. Use .vbs if you are signing VBScript; use .js for JScript code. Once a script file has been signed, its source code looks like the following:

  ' all the original code goes here
  
'' SIG '' Begin signature block
'' SIG '' MIIEMAYJKoZIhvcNAQcCoIIEITC
'' SIG '' ......
'' SIG '' vYTJ6K+uGyDHOKPLHjGFSnvgiMw=
'' SIG '' End signature block

 

In this code snippet the whole signature block appears in a commented section. The signature block is commented and placed at the end of .vbs and .js files and into a <signature> node in .wsf files. This has been done to ensure that previous versions of WSH are still able to run signed files, since both commented sections and the unknown <signature> element are ignored in the versions prior to WSH 5.6.
      To programmatically verify the authenticity of a script file, call the VerifyFile method and pass it the script file name plus a boolean flag indicating whether or not you want user interface elements to be displayed:

  Set signer = CreateObject("Scripting.Signer")
  
Signer.VerifyFile("MyFile.vbs", True)

 

      The method denotes the outcome of the operation by returning a boolean value that equals False if the script cannot be trusted, and True otherwise. If the method can successfully read the private key information and the certificate authority that issued the certificate, it first verifies that the code has not been tampered with and then returns True.

Remote Script Execution


      The WshController object is the WSH element that governs the implementation of remote tasks. It does this by instantiating script programs directly on remote computers. To date, the communication technology running behind this flavor of remote scripting, also referred to as Remote WSH, is DCOM. It will probably evolve sometime in the future to also address HTTP and SOAP. As mentioned earlier, Remote WSH has nothing to do with remote scripting. Remote WSH is the ability to run local WSH scripts on a remote machine. At the highest level of abstraction, Remote WSH can be seen as DCOM for scripts. Remote scripting, in contrast, works over HTTP and represents a portable way to call into public methods of custom ASP pages.
      You instantiate the WshController object as follows:

  Set controller = CreateObject("WshController")
  

 

The WshController object has no properties and exposes just one method, CreateScript. By invoking CreateScript, you obtain a reference to a WshRemote object:

  Set remScript = Controller.CreateScript(scriptName, serverName)
  
remScript.Execute

 

The scriptName argument indicates the script's path and switches as they would be typed at the command prompt. The path to the script must be relative to the caller machine. It can be either a local or a Universal Naming Convention (UNC) path name. The serverName parameter is optional and indicates the UNC name of the remote machine where you want the script to run. If omitted, the script runs locally.
      The WshRemote object represents an instance of a local WSH script which is ready to run across process, computer, and network boundaries, as defined by the serverName argument. You can execute, control, and terminate the script remotely by setting the properties of the WshRemote object.
      When you generate a remote WSH script instance, the controller loads the local script file from the directory path you have specified and sends its text to the server process over the network. The DCOM marshaler takes care of all this. The script file and all of its embedded resources (such as included files) are never persisted to a server destination path, not even temporarily. Instead, they are copied directly into the server process's memory.
      To enable Remote WSH functionality, you must first set up the remote machine with the proper security settings. For one thing, both the remote and local machines must be running Windows NT 4.0 SP3 or greater and both must have WSH 5.6 installed. The user who will run scripts remotely must be a member of the Local Administrators group on the target machine.
      Finally, Remote WSH must be enabled on the remote server machine. An administrator who wants to enable Remote WSH could do so through the administrative template that the Windows XP Resource Kit provides or simply by creating a Remote String entry in the registry at HKCU\Software\Microsoft\Windows Script Host\Settings\Remote String and setting it to 1.
      Remote WSH is not an interactive process like login, so remotely instantiated scripts cannot display any kind of user interface on the remote computer, including pop-up messages. If you try to instantiate a process that displays a user interface, the script may fail or have unreliable results.

Controlling the Remote Script


      Once the WSH automation server is successfully started, it returns an instance of the WshRemote object, which allows the caller to execute the script remotely and access information about the remote script instance. When you create a WshRemote object, execution of the WSH script does not occur immediately because the method only creates the script instance. The server-side script begins execution only when you call the Execute method. This is useful if you want to coordinate a specific time when scripts run on multiple computers, triggered by some condition.
      When launched, remote scripts always run through wscript.exe and in a security context that does not allow creation of user interface elements such as message boxes. This is because an error notification that pops up on the remote machine might not be seen in a timely manner. Worse yet, a WScript.Echo command that was accidentally left in could prompt a message box on a thousand machines. By default, WScript.Echo and user interface commands are disabled. However unadvisable it might be, you can reconfigure your DCOM settings through the DCOMCNFG utility to allow Remote WSH to run in a context that allows user interface elements. Figure 10 shows the programming interface (methods, properties, and events) of the WshRemote object.
      The remote script can be any valid WSH command line, not including the executable name—wscript.exe in this case. The WshController object does not permit the direct instantiation of executable files (.exe or .bat) on the remote machine. You can do this indirectly by placing the executables in a script file that spawns them with the WshShell's Exec method. For this method to work properly, the executables that run remotely must reside on the target remote computer in the directory path specified by the Exec command line. Also, keep in mind that if you use this method, you cannot expect to return data from these executables since the WshRemote object only returns status and error information.
      WSH does not recognize whether one of the command-line parameters used in a remote WSH invocation is itself a local file, since arguments are treated like strings. Running a script via Remote WSH is like running that script on the remote machine. If you need files copied over to a remote machine for further use, then you'll have to write code to copy them yourself.
      The status of a WSH remote script can be represented by any of the values listed in Figure 11. Compared to connecting to the events of the WshRemote object, the Status property gives you an inexpensive way to get status information. Connecting to events uses more computer resources and can sometimes be tricky. This code shows how to run a remote script and monitor its progress:

  Set controller = CreateObject("WshController")
  
Set remScript = controller.CreateScript(scriptName, srvName)
remScript.Execute()
bDone = False
While Not bDone
WScript.Sleep(100)
Wscript.Echo remScript.Status & vbCrLf
Wend
WScript.Quit

 

Notice that the Status property of the WshRemote object is only updated when it is effectively queried, minimizing the traffic between calling and called scripts. An alternative that uses events is shown in Figure 12.
      The error information you can access for a remote WSH script is similar to the information returned by the VBScript Err object. If a remotely instantiated script terminates because of an error, the WshRemoteError object is filled with error properties such as Number, Description, Character, SourceText, and Source. You access this object through the WshRemote Error property, typically from within an event handler that catches the Error event.

Spawned Processes


      In WSH 5.6, the WshShell object has a new method called Exec. It runs an application in a child command shell and provides access to streams of the spawned process like StdIn, StdOut, and StdErr. The Exec method takes a string value indicating the command line used to run the script. The command line should appear exactly as you would have typed it at the command prompt.
      The Exec method returns a WshScriptExec object, which is the object that actually provides status and error information about the script being run. Through WshScriptExec you also access the standard I/O streams. You cannot use the Exec method to execute anything other than command-line console applications. Likewise, the Exec method cannot be used to run remote scripts and should not be confused with the Execute method of the WshRemote object.
      The WshScriptExec object solved an issue for script developers who needed to spawn console or command prompt applications. A simple, yet effective approach is the following:

  shell.Run "%comspec% /k dir"
  

 

The %comspec% environment variable points to the application that manages the command prompt on the current platform. The /k switch keeps the command prompt window open after the execution of the command, while the /c switch closes it as soon as the command terminates. There is nothing wrong with this approach as long as it really provides what you need. The Run method ceases to be the ideal approach if you need to access the input, output, and error streams of the spawned application. In this case, you are better off using the Exec method.

Script Arguments


      The WshArguments object provides access to the entire collection of command-line parameters in the order they have been specified. WshArgument is a collection that can be accessed according to the typical programming interface of COM collections. In WSH 5.6, the WshArguments object features new child collections called Named and Unnamed, which can be accessed through the WshNamed and WshUnnamed objects. Named arguments are command-line arguments that have been associated with a name. All arguments not associated with a name fall in the category of unnamed arguments. The contents of the Arguments collection are always the sum of the contents of Named and Unnamed arguments.
      In general, a command-line argument includes two distinct types of information: the role and the value. For most applications, the role of each parameter is implied by the position it occupies on the command line. The program assumes that the first parameter means one thing, the second another, and the third something else. The role of a named argument is not implied by position but comes as a part of the argument itself. Consider this command:

  program.exe /file:input.txt
  

 

The /file switch indicates the role of the parameter, and the actual content of the parameter follows the colon. Scripts that define their command-line arguments according to this schema have a way to identify their input values by name. Named arguments are far easier to manipulate because they can be placed anywhere on the command line and are self-explanatory. WSH 5.6 supports named arguments but it does so according to a number of assumptions.

Named and Unnamed Arguments


      In WSH 5.6, a named argument is always prefixed by a slash and followed by the colon (:) character. Any other combination of characters simply won't work. The value of the parameter is what comes after the colon until a blank character is found. If you want to assign it a string that may contain blanks, you must wrap it in quotes, like this:

  thescript.vbs /ErrorMsg:"This is an error message"
  

 

      When a named argument is stored in the Named collection, the wrapping quotes, if any, are removed.
      All the named arguments are extracted from the command line, parsed, and made accessible through the Named collection. The following code returns the number of named arguments for the current script:

  MsgBox WScript.Arguments.Named.Count
  

 

      The Named collection provides the typical programming interface for collections. This includes properties like Item, Count, and Length. In addition, Named has an extra method, Exists. The Exists method checks whether the collection contains an item whose name matches the specified text:

  If WScript.Arguments.Named.Exists("File")
  

MsgBox WScript.Arguments.Named("File")
End If

 

      If you don't specify arguments using the /Name:Value convention, the Named property will return an empty collection. A named argument is always placed in the Arguments collection in its unparsed form which is, for example, /File:Input.txt.
      All the arguments on the command line that don't have a name are considered unnamed and grouped in the Unnamed collection. You retrieve any unnamed argument as you would retrieve a generic item from Arguments.

The Runtime Element


      Script applications implemented through the WSH schema can include metadata, which is information used to request special services from the WSH runtime. In particular, all the information defined within the runtime element serves as self-documentation. It lets you specify the expected arguments, a description of the script's behavior, and usage information.
      The runtime element is the parent element within which you define all run-time arguments and usage information. The runtime element is normally placed immediately following the job element. It is a child of a given job, and all of its settings apply only to that particular job:

  <job>
  
<runtime>
<named name="File"
helpstring="Indicates the file to process"
type="string"
required="true"
/>
</runtime>
•••
</job>

 

      The named element indicates one of the named arguments that a job supports. It recognizes four attributes: Name, Helpstring, Required, and Type. The Name attribute, as you might guess, allows you to define an argument name. The Helpstring attribute specifies the description of the argument. The Required attribute is used to set an argument as required or optional.
      Type is an optional attribute that describes the type of the argument. It determines how the argument will be parsed from the command line. Allowed values are "simple," "string," and "boolean," with "simple" being the default value. A simple argument takes no additional value and is passed as just the name: /Drive. A boolean argument is passed to the script with a trailing + or - sign to turn it on or off: /Verbose+.
      Figure 13 shows the elements you can use under the runtime element that influence the output of ShowUsage.
      The WshArguments object has a new method called ShowUsage, which collects the information in the <runtime> element and formats it for display. The code in Figure 14 generates the message box shown in Figure 15.

Figure 15 WSH Options
Figure 15 WSH Options

      The run-time information for each parameter is used only for self-documentation and to format the data displayed by the ShowUsage method of the WshArguments collection. The runtime elements do not enforce the values set for the arguments it contains. For example, if an argument is marked as required but is actually missing from the command line, no error will be raised. The ShowUsage method is also automatically invoked when the script is run with the /? switch.

WSH Scripts on Windows XP


      The list of enhancements in WSH 5.6 ends here. But before closing, let me mention a couple of tips that will help you when scripting for Windows XP. First is a visual issue. Windows XP has Visual Styles that can be turned on programmatically. In "New Graphical Interface: Enhance Your Programs with New Windows XP Shell Features" (MSDN® Magazine, November 2001), I explained that the look of any Win32 application can be adapted to the current theme without writing any code. All that's needed is an XML manifest file with the same name as the executable, with a .manifest extension. Amazingly, the same XML manifest file can work unchanged with any executable on that machine, but you might want to customize the product and copyright information.
      In WSH scripts you don't have many GUI elements to make Windows XP themes really cool. However, turning themes on for all WSH applications is easy. Just copy the XML manifest file I discussed in my November 2001 article into the System32 folder on Windows XP, then rename the file wscript.exe.manifest.
      In doing so, you'll notice that the System32 folder already contains a few other manifest files. Once the manifest file is in the System32 folder, try running a script that uses the VBScript InputBox function. The effect, with and without the manifest, is shown in Figure 16.

Figure 16 With and Without Manifest
Figure 16 With and Without Manifest

      Windows XP comes with a new system COM component, ScriptPW.Password, implemented in the scriptpw.dll file. Although this is not officially part of the WSH 5.6 object model, you can certainly use it for your own purposes. (Incidentally, the component also works under Windows 2000 once you move the DLL and properly register it as a COM component.) The ScriptPW object works as a password reader and allows you to type in sensitive data without displaying it. The GetPassword method collects and returns the typed text. You use the component as follows:

  Set oPW = CreateObject("ScriptPW.Password")
  
pswd = oPW.GetPassword()

 

      ScriptPW.Password is certainly useful because it addresses functionality that was previously missing. Unfortunately, though, it is not perfect. For one thing, it doesn't work in a GUI environment and is only available to console scripts running under the control of cscript.exe. Second, it captures any text you type but it never provides any feedback to the user. In particular, it displays only a blinking cursor as you enter text; no special character replaces the individual characters.

.NET Languages


      The WSH core (wscript.exe) is a relatively simple Win32 application that hosts the Windows Script engines and acts a kind of super proxy between the .vbs or .js user file and the script engine. In the past, a lot of people applied the same model to their applications making them customizable and extensible. The question you may be asking now is what's going to change in this area with the advent of .NET?
      Scripting has very little to do with .NET for two good reasons. First and foremost, WSH relies completely on COM. Second, the advantages you get from .NET languages over scripting languages are substantial in terms of performance and programming ease. Whenever you can use a .NET language instead, there's no question about what's better.
      The .NET Framework comes with a set of language engines that applications can host in much the same way as the VBScript and the JScript engines. These new engines are freely redistributable along with the rest of the framework. At this time, the two script engines that the .NET Framework provides run Visual Basic .NET and JScript .NET. In addition, a third engine is in the works for the .NET intermediate language (IL). This engine would be able to load precompiled IL code but not compile from sources.
      This new Script for the .NET Framework infrastructure has a number of advantages over the Windows Script engines. First, you have full access to the entire .NET platform. In other words, you are no longer forced to build a tailor-made object model in your applications just to allow for scripting. In .NET, the application is inherently programmable in the sense that any constituent public class is accessible without intermediate proxies. However, you can control what objects are effectively reachable via script and you can make the scripts run in a kind of secured sandbox.
      The languages you use to script .NET applications are the same first-class languages you use for writing them. They provide advanced features such as strong typing, early binding, and, of course, compiled code. In addition, Script for the .NET Framework can rely on the debugging and editing features of Visual Studio® for Applications (VSA).
      To start using a language engine, you first create an instance of it, add some code, and then add the object model that the script is based upon. Objects are not attached to the engine through living instances as they were with the Windows Script engine. Instead, you need to specify the type and name in order to script the object. Only when the script executes and an instance of that object is needed will the engine call you back to obtain a usable instance for that kind of object. The interface involved with the setup of the engine is IVsaEngine. The interface for callbacks is IVsaSite.
      The language engines also support the ability to add references to .NET classes by creating an item in the engine with the full name of the involved assembly. In this way, all the types in the assembly are available to the script.
      When WSH is updated for .NET it is expected to be an application based on the Script for the .NET Framework engine and the new object model is heavily based on the VSA engine. What you can expect in the future of WSH, far beyond today's upgrade, is a shell-level environment that knows how to work with any .NET language from JScript .NET to Visual Basic .NET, and from J# to C#. Any of these languages will become just another "script" language supported by WSH.
      Why then would you use WSH instead of regular Windows Forms applications? Windows Forms are, and will be, full-fledged applications distributed through compiled code. It is expected that WSH applications for .NET will be script-like applications distributed through source code that will be compiled on the fly. Each of them will have its own use and gain its own space.
      For more information about Visual Studio for Applications see https://msdn.microsoft.com/vstudio/vsip/vsa. Information about Script for the .NET Framework can be found at https://msdn.microsoft.com/scripting. Particularly helpful for understanding the big picture and getting started with some sample code are the articles in the Scripting Clinic column on MSDN Online.

Conclusion


      WSH 5.6 is definitely a major upgrade. It provides structural improvements such as a new security model and the ability to run scripts remotely. The programming power for administering Windows is significantly increased and writing script code is easier due to long-awaited, valuable features such as named arguments, the runtime information, and script controllers. Is WSH 5.6 ready for .NET? Not yet, but a number of interesting things are in the works as you read this.

For related articles see:
Andrew Clinick's Scripting Clinic columns on MSDN Online
For background information see:
Visual Studio for Applications
Information about scripting for the .NET Framework can be found at https://msdn.microsoft.com/scripting
Dino Esposito is an instructor and consultant based in Rome, Italy. Author of Building Web Solutions with ASP.NET and ADO.NET (Microsoft Press, 2002), he now spends most of his time teaching classes on ASP.NET and ADO.NET for Wintellect (https://www.wintellect.com). Get in touch with Dino at dinoe@wintellect.com.