An essential feature of Windows Script Host (WSH) is the Windows Script File (WSF) format, which Microsoft introduced in 1999 as part of WSH 2.0. Since then, WSF has proved to be helpful and powerful, but it's not a widely used feature, primarily because of the cumbersome XML syntax that surrounds the code. The beauty of WSH is its immediateness and intuitive code, which requires only a text editor and a console. XML adds an extra layer of code and requires an additional step when you write a script. At first sight, the WSF format seems more of a complication than a real help for script developers—which is probably true if you only write and use simple scripts comprising a few calls. However, the more complex your scripts get, the more you need sophisticated tools, such as importable libraries of script functions, customizable resources, and predefined constants. The WSF format supports many additional features without touching the languages' syntax.
In this article, I use some quick and effective sample scripts to introduce you to the advantages of the WSF format. I also show you a handy tool that automatically translates existing VBScript and JScript code into a .wsf file.
When Microsoft started working on WSH 2.0, the WSH team needed to figure out a way to extend the programming power of scripting languages without heavily editing the language syntax or compromising WSH's inherent simplicity. VBScript and JScript—and, in general, all scripting languages—offer a simple structure that doesn't provide for metainformation such as external source files, importable libraries and constants, and customizable resources. The growing demand for function-related enhancements put the WSH team on the road to introducing a new and more powerful file format. The team had three alternatives: Improve the language's syntax to accommodate new functions, extend the WSH object model, or give programmers a chance to create applications through small project files, in which the code is just one of the constituent elements (along with external resources).
The team chose the third option and arranged a simplified project file rendered through an XML schema—WSF. Basically, the WSF schema defines one or more jobs, each of which evaluates to an individual script. Each job has a <script> tag to define the code to run and a few extra tags to link the application to external resources. In WSH 5.6, Microsoft has further extended the schema to provide for named and unnamed arguments and usage information. In particular, the WSF format supports Include statements, type libraries, multiple language engines, and multiple jobs. For more information about the format, see the Microsoft article "Using Windows Script Files (.wsf)" (http://msdn.microsoft.com/library/default.asp?url=/library/en-us/script56/html/wsadvantagesofws.asp).
The key element of the schema is the job, the atomic element that forms a WSF application. A job is a collection of one or more script blocks, resources, and external links. You can have multiple script blocks in a .wsf file, and each block can target a different script engine (e.g., VBScript, JScript, Perl). You can define scripts in the same .wsf file or import them from an external file. The inability to include external files is a significant deficiency of both the JScript and VBScript programming interface. In other languages and development tools, you address this deficiency with special directives that instruct the compiler to access the specified external modules. In a WSF job, you use a tailor-made XML tag—the <script> tag—to address the problem.
Converting VBScript Files to WSF Files
Here's how to rewrite a simple WSH application as a WSF job. The VBScript code
MsgBox "Hello from WSF"
is fully equivalent to the following WSF script:
<job> <script language="VBScript"> MsgBox "Hello from WSF" </script> </job>
A straightforward way to translate the previous VBScript file into a more flexible .wsf file is to save the VBScript code to a file called hellowsf.vbs and run the following XML code:
<job> <script language="VBScript" Src="hellowsf.vbs"> </script> </job>
Both WSF approaches produce the same results.
Listing 1 shows the ScriptToWsf.vbs file. This script attempts to use the WScript's Arguments collection to read the name of the input VBScript or JScript file from the command line. In case of failure, the script prompts the user to type the name of the file to process. Next, the script creates an XML wrapper for the file with the same name but the .wsf extension. To do so, the script creates a new text file that has the same name and path as the specified file but with the .wsf extension. (The system will overwrite any existing file with the same name.) The script takes into account the extension of the file and sets the <script> tag to use the VBScript or the JScript language accordingly. I recommend placing the ScriptToWsf script (or a shortcut) on the desktop and dragging VBScript and JScript files onto it from a Windows Explorer window.
The ScriptToWsf script works with only VBScript and JScript files. However, extending it to support any other type of script you might be using doesn't require any special effort. Just extend the If statement or, better yet, convert it into a Select Case statement. For example, to have the script work with VBScript, JScript, and Perl files, you replace the code at callout A in Listing 1 with the code
Select Case ext Case "js" f.Write "JScript" Case "vbs" f.Write "VBScript" Case "pl" f.Write "Perl" End Select
After you create a .wsf file that works exactly like the original script, you can more thoughtfully plan for extensions and enhancements. However, in this version of the ScriptToWsf script, be aware of a small, annoying requirement: For the code to work, you must have both the .wsf file and the original file available on the machine. The WSF code that the script generates is a mere shortcut that links to the original file as an external resource. Subsequently, if the original file is missing or can't be successfully resolved (i.e., it's a remote file and no connection is available), you'll get an error.
Copying VBScript Files into WSF Files
A better approach for converting VBScript or JScript files to .wsf files is to copy the VBScript or JScript file into the new WSF script. In this case, you'll get a larger file, but you don't need to worry about inner dependencies that can make usage problematic. The ScriptToWsfCopy script, which Listing 2 shows, performs this copy operation.
The ScriptToWsfCopy script works in much the same way as the ScriptToWsf script. ScriptToWsfCopy creates a new text file with a .wsf extension and starts filling it with XML code. Then, the script opens the <script> tag and assigns the proper language attribute. The next step is where the two routines differ. Instead of defining the src attribute, ScriptToWsfCopy simply reads all the original file's contents and writes it to the output stream.
The VBScript or JScript code is now part of the XML body—which might be cause for further problems. For example, what happens if the VBScript or JScript code includes the greater-than operator (>)? Such a character has a special meaning to XML parsers and must be escaped so that the XML parser treats it as any other character. Therefore, two small changes to the final .wsf file are necessary. First, you must put the file into strict XML mode by declaring the following directive at the top of the file:
<?XML version="1.0" ?>
In this mode, the XML parser will recognize the following CDATA markers, which comprise the second change:
<!\[CDATA\[ escaped code \]\]>
All the text (in this case, code) wrapped by <!\[CDATA\[ and \]\]> delimiters is considered raw text and isn't processed.
From the user's standpoint, the effect of using ScriptToWsf and ScriptToWsfCopy is exactly the same. However, in the latter case, you have one file to take care of, so maintenance and management are a bit easier.
Configuring the Windows Shell
You can use both ScriptToWsf and ScriptToWsfCopy as drop targets on the desktop or in any Windows folders. However, the Windows shell, in collaboration with the system registry, provides an even better method to expose both scripts. By tweaking the registry, you can add new items to the context menu of a VBScript file. These items would point to the scripts and work on the VBScript file that you selected. Listing 3 shows a short script that you need to run so that you can properly configure the Windows shell.
To add a context menu item to VBScript files, you must create a subkey under the HKEY_CLASSES_ROOT\VBSFile\Shell registry subkey. The name of this subkey is arbitrary; I've chosen ConvertToWsf. The system will use this name as the menu item text unless you enter descriptive text in the key's default value. The ConvertToWsf subkey must also have a subkey called Command, the default value of which points to the command to execute. To associate this subkey value with the ScriptToWsfCopy.vbs script, enter the following command:
wscript.exe "C:\scripttowsfcopy.vbs" "%L"
The script must reside in a public folder, or you must specify its full path. (Modify this line accordingly for your environment.) The %L placeholder represents the fully qualified name of the VBScript file that you selected in the Windows Explorer view.
After the shell extension is in place, right-click a VBScript file and choose the new Convert to WSF option. The system will instantly create the new file in the same folder. You've now converted your old VBScript file to a more modern .wsf file.
The script in Listing 3 works for VBScript files, but you can easily adapt it to work with any other file extension. Just replace the line
classType = ".vbs"
with a line that points to a different file extension, such as
classType = ".js"
Note that the file extension points you to the class indexer node for the type. This node is simply the name of the HKEY_CLASSES_ROOT key under which the system has stored any relevant information about the class type. This class type is VBSFile for .vbs files and JSFile for .js files. To cancel the changes in the registry for VBScript files, you need only remove the HKEY_CLASSES_ROOT\VBSFile\Shell\ConvertToWsf subkey.
Only the First Step
I've often used this article's small but useful scripts to simplify migrations from old-fashioned VBScript files to .wsf files. However, such tools are only the first step toward fully understanding the potential of .wsf files. Now that you have an XML schema successfully running your old script, you're ready for the next step: exploiting other aspects of the WSF schema.