Newsletters #4

Here is my 4th newsletter dealing with Windows Script Host 2.0 issues. My apologizes for the long delay between the 3th and the 4th newsletter. Building my new house reaches the final state (after one contractor went into bankrupty, and another hit several times the deadline, I wasn't in the mode to undertake any further investigation in WSH scripting - I have had to solve a couple of crisises every day<g>). And there were also other challenging projects like a Linux book for Windows users, or the Windows 2000 Professional handbook for MS Press Germany. Anyway, since a few weeks I begun updating my WSH title, and I found time to investigate WSH 2.0 a bit. To avoid other scripters run into the same frustration like me (because of incomplete and wrong docs), I decided to devote this newsletter to some WSH 2.0 items. Below is a kind of FAQ list about several topics dealing with WSH 2.0.

(c) Günter Born, http://www.borncity.de

WSH 2.0 FAQ (Version 1.0)

The FAQ list given below may be used as free (and may be distributed in its whole and unmodified state for free). In no event shall the author be liable for any losses or damages resulting from using the information contained herein. So, after keeping the lawayers happy, we can dive into the details. I intend to extend this FAQ in future and publish it in the WSH Bazaar.

Where to obtain WSH 2.0?

WSH 2.0 is shipped already with the upcoming Microsoft Windows 2000 operating system. Also Internet Explorer 5.1 comes with the new versions of the host and language engines. Documents and the files for other platforms (Windows 9x, Windows NT 4.0) may be downloaded from Microsofts web site http://msdn.microsoft.com/scripting.

What is with .WS versus .WSF file name extension?

Microsoft uses .WS as an extension for WSH 2.0 files only within betas of WSH 2.0. Since WSH 2.0 is released, the file name extension changes to .WSF (because other application already uses .WS).

What is new within .WSF files?

Scripts using the .WSF file name extension are XML documents containing scripts and also other XML documents. A typical .WSF file may contain the following content:

<job id="WSFExample">
<?Job debug="true"?>
<script language="VBScript">
 WScript.Echo "Hello world"
</script>
</job>

The sample given above contains just a short script creating a simple dialog box window containing the text "Hello world". The statement contained in line 4 is responsible for this dialog box. This is the ordinary VBScript statement we already know from old .vbs files.

There is a lot of stuff around this simple VBScript statement. What's about this stuff? The "words" enclosed in brackets < ...> are XML elements, which acts as a container for options or for data. The <script> element (also called a tag) contains the script between the starting and the ending tag.

Note: Many user know tags from HTML. It is important in XML that each start tag has its corresponding end tag (like <script> .... <script>). There is also a short version, used for tags which contain no other data like <reference object="Word.document" version="8.0" />. Within the following text I use the term "element" instead of tag, to express that I mean an element consisting of a starting tag and an ending tag. The word <job> tag for instance mean in other hand, that we are dealing with the start tag of the job element. Element names are case-sensitive within xml documents. Although WSH 2.0 accepts element names not given in a right case, you should use the right case within .wsf files. See also my explanations given below about wellformed xml documents.

The <script> tag supports the language attribute which defines the script language used within the script. In the sample above we use VBScript.

The <job> tag is required within the .WSF file, and it encloses the script element. The id attribute defines an unique name associated within this job. A .WSF file may contain several jobs like:

<package>
<job id="Example1">
<?Job debug="true"?>
<script language="VBScript">
WScript.Echo "Hello world 1"
</script>
</job>
<job id="Example2">
<?Job debug="true"?>
<script language="VBScript">
WScript.Echo "Hello world 2"
</script>
</job>
</package>

Executing this .WSF file using a double-click forces WSH 2.0 to execute the first job (with the ide "Example1" within the file. You may invoke WSH 2.0 with the job parameter, defining explicit another id like:

WScript //job:Example2 D:\WSH-material\experiment1\Hello1.wsf

You may use the run dialog box from the Windows start menu to submit the options. The //job:xxx parameter contains the id of the job. This id xxx must be defined within the .WSF file using the id attribut of a job element.

Note: If you intend to use several jobs within one .WSF file, you have to enclose these job elements within a package element.

Can you show me a sample showing the advantage of .WSF files?

Perhaps you ask you, what's the advantage of a .WSF file. The samples given above just contain a lot of additional stuff to enclose a simple WScript.Echo statement. Why add all thouse additional lines, if you can create the same result with a simple line stored within a .VBS file?

Ok, let's have a look at a simple example. JScript programmers suffers from a missing feature for user input. Although I have introduced several alternatives in my WSH Bazaar, it seems to be a bit an overkill, just to have a simple input box window. Would it not be nice to call the VBscript InputBox function from a JScript script? In WSH 1.0 its not possible, because you may use only one scripting language within one .js or .vbs file. The file name extension defines the language implicite. Well, let's have a look at a solution provided in a .WSF file.

<job id="InputExample">
<comment>
************************************************
File: InputTest.wsf (WSH 2.0 sample)
Author: Günter Born

Demonstrates how to implment a user input in JScript.
The script uses a VBScript function
************************************************
</comment>
<script language="VBScript">
' Implement an InputBox-Funktion for JScript
Function WSHInputBox (Message, Title, Value)
WSHInputBox = InputBox(Message,Title,Value)
End Function
</script>

<script language="JScript">

var vbOKOnly = 0;
var vbInformation = 64;
var vbCancel = 2;
var title = "Born's InputBox function for JScript";
var prompt = "Name:";

// Create the Shell object (needed to use Popup).
var WSHShell = WScript.CreateObject("WScript.Shell");

// open Input dialog
var result = WSHInputBox (prompt,title,"Born");

if (result != "") // Test, whether Cancel clicked
{ // No, get input
var intDoIt = WSHShell.Popup("You entered: " + result,
0,
"Result",
vbOKOnly + vbInformation );
}
else
{ // Cancel button was clicked
var intDoIt = WSHShell.Popup("Sorry, no input",
0,
"Result",
vbOKOnly + vbInformation );
}

// here I have omitted WScript.Quit() to demonstrate
// that it also works, because the WSH automatically executes
// Quit after reaching the last statement.

// End
</script>
</job>

The sample given above contains two script elements. The first <script> tag sets the language attribute to "VBscript", therefore the script element must contain VBScript code. I defined a simple VBScript function WSHInputBox which acts as a wrapper to the VBScript build-in InputBox function.

The second script element contains the JScript program which shall invoke an input box window and which will show the user input. Within the script we may call simply the VBScript function:

// open Input dialog
var result = WSHInputBox (prompt,title,"Born");

The second line invokes the input box window. The other lines within the JScript script are used to check the user input (whether the OK or Cancel button was used to close the window) and to show the result.

Using this approach, you can collect several functions implemented in different languages within a .WSF file. The "main" script may use these function within simple procedure and function calls.

Important: At this place I should note, that all functions/procedures calleable from a "main" script, need to be places within the same job element. The scope of a function/procedure is only valid within one job element.

May I still use .vbs and .js files in WSH 2.0?

Yes, just proceed as with WSH 1.0. If WSH 2.0 detects a file name extension .js or .vbs, the file is processed as in WSH 1.0.

How to "reuse" .vbs and .js files in WSH 2.0?

Does your hard disk contain a huge collection of .js and .vbs files. Do you like to re-use them within .wsf files without loading them into an editor, cut the code and past the statements into a second window with the .wsf code?

Well, finally .wsf files provides an include feature, which allows you to access existing script files within a job or within several jobs. The following code listing includes three different existing .vbs and .js files. Because thouse files doesn't contain functions or procedures, I have included them in different job elements. So it is possible to invoke them submitting the job parameter:

WScript //job:2 IncludeTest.wsf

The command invokes the job containing the id="2" within the .wsf file.

<package>
<comment>
************************************************
File: IncludeTest.wsf (WSH 2.0 sample)
Author: Günter Born

Demonstrates how to include a few script files.
************************************************
</comment>
<job id="1">
<script language="VBScript" src="currentdir.vbs"/>
</job>
<job id="2">
<script language="VBScript" src="Properties.vbs"/>
</job>
<job id="3">
<script language="JScript" src="Sendkeys1.js"/>
</job>
</package>

If your .vbs or .js include file contain a function or a procedure, you may call this function/procedure from a script within the .wsf file. This is used within the next sample, which assumes, that a separate file VBSInput.vbs is already available.

### Here is the code for the VBSInput.vbs file:

'File: VBSInput.vbs (WSH 1.0 sample)
'Author: Günter Born
' Implements an InputBox-Funktion
Function WSHInputBox (Message, Title, Value)
WSHInputBox = InputBox(Message,Title,Value)
End Function
' The End, Finito, Fin, Fertig, Basta! ...

### Here comes the code for the .wsf file

<job id="Input">
<comment>
************************************************
File: InputTest1.wsf (WSH 2.0 sample)
Author: Günter Born

Demonstrates how to implment a user input in JScript.
The script uses a VBScript function contained within an external file.
************************************************
</comment>

<script language="VBScript" src="VBSInput.vbs" />
<script language="JScript">

var vbOKOnly = 0;
var vbInformation = 64;
var vbCancel = 2;
var title = "Born's InputBox function for JScript";
var prompt = "Name:";

// Create the Shell object (needed to use Popup).
var WSHShell = WScript.CreateObject("WScript.Shell");

// open Input dialog
var result = WSHInputBox (prompt,title,"Born");

if (result != "") // Test, whether Cancel clicked
{ // No, get input
var intDoIt = WSHShell.Popup("You entered: " + result,
0,
"Result",
vbOKOnly + vbInformation );
}
else
{ // Cancel button was clicked
var intDoIt = WSHShell.Popup("Sorry, no input",
0,
"Result",
vbOKOnly + vbInformation );
}

// here I have omitted WScript.Quit() to demonstrate
// that it also works, because the WSH automatically executes
// Quit after reaching the last statement.

// End
</script>
</job>

NOTE: I have set the <job> start tag into the first line, because WSH requires that the .wsf file starts with a job or package element. The comment element may be used within a .wsf file to include some ordinary comments (which are independent from comments included in scripts).

The sample given above contains two script elements. The first <script> tag sets the language attribute to "VBscript", therefore the script element must contain VBScript code. I defined a simple VBScript function WSHInputBox which acts as a wrapper to the VBScript build-in InputBox function.

The second script element contains the JScript program which shall invoke an input box window and which will show the user input. Within the script we may call simply the VBScript function:

// open Input dialog
var result = WSHInputBox (prompt,title,"Born");

The second line invokes the input box window. The other lines within the JScript script are used to check the user input (whether the OK or Cancel button was used to close the window) and to show the result.

Using this approach, you can collect several functions implemented in different languages within a .WSF file. The "main" script may use these function within simple procedure and function calls.

Important: At this place I should note, that all functions/procedures calleable from a "main" script, need to be places within the same job element. The scope of a function/procedure is only valid within one job element.

How to obtain information about options submitted to WSH 2.0?

Perhaps you have noticed that I submitted some options to WSH 2.0 within the samples given above. WSH 2.0 supports a set of switches in both CScript.exe and WScript.exe to force several features. Thouse switches are passes as //name after the host name. For instance:

WScript //job:2 IncludeTest.wsf

uses the job switch to execute the job with id "2" contained within IncludeTest.wsf. You may retrieve a complete list of all options available entering the command:

WScript /?

within the run dialog (click run in the start menu).

Why my debugger won't work in WSH 2.0 anymore?

Situation: You have installed already Microsoft's script debugger on a machine. Since you have installed WSH 2.0 (or Internet Explorer 5.1), the debugger won't work anymore.

Concratulation! You join the community of "frustrated" scripters (the author took several hours to find out what's going on - at least with a little help from Mike Whalen /Microsoft - I solved the problem). I know also of several other scripters trapped into the same problem.

According to Microsoft's explanation: This is a know problem that is going to stick around. There are three products involved in this behavior, and none of them agree on how the debugger should be invoked.

Mike Whalen kindly delivered also a workaround. There is a key in the Registry at:

HKEY_CURRENT_USER\Software\Microsoft\Windows Script\Settings

This key contains the value JITDebug which is of type DWORD. If the value is set to 0, debugging is enabled. Set the value to 1, then debugging is enabled, and you can proceed with debugging. See also my notes within the next question.

Note: If you won't hack your registry, cut the following code and save it using Notepad to a simple file DebugOn.reg. After double-clicking the .reg file, your registry will be updated automatically.

REGEDIT4

[HKEY_CURRENT_USER\Software\Microsoft\Windows Script\Settings]
"JITDebug"=dword:00000001

How to debug scripts in WSH 2.0?

I have installed already Microsoft's script debugger on my machine, and I have also enabled debugging (according to the workaround described above). But it seems that things won't work.

WSH 2.0 comes with a slight different behavior as WSH 1.0. Whilst WSH 1.0 invokes the script debugger after detecting an error, WSH 2.0 just shows an error message.

You may use simple statements like stop (VBscript) and debugger (JScript) within your .vbs and .js script files to invoke an (already installed) script debugger. This statement was true in WSH 1.0 and it is still true in WSH 2.0. But, if you try to use these statements, nothing happens. Debugging scripts in a WSH 2.0 environment requires that you invoke your script using either the //D or the //X switch in the command line.

WScript //D MyScript.vbs

This command enables active debugging. This means, the debugger window will be shown, if a stop or debugger statement is reached within the script code. If you intend to execute your script under debugger control, you may invoke the script using the following command:

WScript //X MyScript.vbs

Debugging .wsf files require that you use the <?job debug="true"?> processing instruction within your <job> elements. This instruction forces WSH to launch the script debugger, if a stop or debugger instruction is found within a script. A <?job debug="true"?> statement within the <job> element disables the script debugger. The following code demonstrates how to set the <?job …. ?> processing instruction within a .wsf file.

<?xml version="1.0"?>
<job id="T1">
<?job debug="true"?>
<script language="VBScript">
stop
WScript.Echo "hello"
</script>
<script language="JScript">
debugger
WScript.Echo ("hello1");
</script>
</job>

NOTE: You will find the sample file Test3.wsf within the ZIP-archive shipped with this news letter. Further details about debugging may be obtained from chapter 2 of my WSH Tutorial. This chapter may be downloaded for free as a sneak preview from my WSH Bazaar. You need only Adove Acrobat Reader to view the chapter.

TIP: Executing a .WSF file and submitting switches using the Run dialog box may be a bit "fussy". No more double-click, to execute the file. For script development I use the PrimalScript 2.0 script editor, which supports also debugging. The new version 2.0 will be available for public use in February 2000. Further information may be obtained from Sapien's website www.sapien.com.

How to use the <reference> tag in WSH 2.0?

The <reference> element allows you to access type libraries from WSH 2.0 script. This enables your script to use named constants already defined within a type library. Unfortunately using the <reference> element is a bit "tricky", because not all files comes with a type library. Here is a sample which uses Microsoft Word 2000 type library to obtain a named constand within the VBScript script:

<?xml version="1.0"?>
<job id="RefExample">
<?job debug="true"?>
<comment>
we may use the element
<reference guid='{00020905-0000-0000-C000-000000000046}'/>
to access the library. Or we can use the code given below,
in this case a version if mandatory. 
</comment>
<reference object="word.document" version="8.0"/>
<script language="VBScript">
<![CDATA[
 Set Tmp = WScript.CreateObject("Word.Application")
 WScript.Echo "wdBlack = " & wdBlack
]]>
</script>
</job>

Executing the sample results in a simple dialog box, which contains the current value of the wdBlack constant (already defined within the type library).

The reference to the Type Library is created using the <refrence> element. The start tag supports several attributes, which defines the references. One possibility is to use the guid attribute. The value of this attribute contains the classid key used from the Type Library. The following element defines a valid reference to the Word 8.0 type libray:

<reference guid='{00020905-0000-0000-C000-000000000046}'/>

NOTE: You may use Microsoft's OLE/COM viewer, to obtain the classid codes and the names of constants of type libs. A short description of this tool may be found in chapter 2 of my WSH Tutorial. This chapter may be downloaded for free as a sneak preview from my WSH Bazaar. You need only Adove Acrobat Reader to view the chapter.

Using a classid key isn't the most approbiate way to define the reference. Therefor you may use also the object attribute within the <reference> element as shown below:

<reference object="word.document" version="8.0"/>

The object attribute contains the name of the library and the class. Here I used word.document. At this point I need to give a few notes: First of all, in my experience it is mandatory to combine the version attribute and the object attribute. My attempt to use only the object attribute caused a run time error (WSH 2.0 reports that the type library could not be loaded, although the classid code was registered already). The second remark: In my first attempts I assumed, that the application object of the application shall be used to reference the Type Library. Michael Harris gave me the tip, that a "reference" via the Word.Application object (or any other Office-Application object) isn't possible. Instead I have to use the Word.document object (or any of the other Office document objects) to retrieve the Type Library.

What's about well formedness in .WSF files?

XML documents are defined using an explicite or implicite document type definition (dtd). This document type definition defines the rules used to build the document using xml elements. One of the most important issues is to confirm that a xml document is wellformed or valid. Whilst the validity is much more restringent , a xml documet shall be at least "well formed". This means, the document fits the requirements of the XML declaration. This means, using case sensitive keywords for elements and attributes, following the rules to enclose attribut values into " " or ' ', using nested start and ending tag, each element need to have an ending tag and so on.

NOTE: Microsoft's WSH documentation doesn't contain much about these facts. Even the WSH.chm file contains samples showing WSH files, which are not wellformed xml documents. So I wrote my first samples also without taking care of this behavior. But soon I relized that I join the risk, that my files won't run with future WSH versions. Also xml tools won't support my .WSF script files. And I run into several errors causes by such code.

To avoid conflicts with not wellformed .wsf files, you should advice the xml parser contained within WSH 2.0 to refuse all scripts which doesn't fit thouse rules. As shown in the examples given above, you may write your .wsf files wellformed or not wellformed. If a .wsf file starts with the command:

<?xml version="1.0"?>

the parser is forced to check the content as a valid xml document. If you use wrong cases for element names like:

<?xml version="1.0"?>
<job ID="15">
...
</JOB>

a run time error occurs. Within the sample, the attribute name and the endig tag doesn't use the right case.

And there is also another important behavior I have to mention. Script code shall be enclosed always in CDATA elements. These elements encapsulate the code against the parser. Although most of your script will be executed without further problems, if you just use the <script> element without further CDATA elements, there is a risk that the parser fails to process several scripts. I came across this behavior after I wrote a simple VBScript program like it is shown below:

<?xml version="1.0"?>
<job id="Test">
<?job debug="true"?>
<script language="VBScript">
Dim name
name = "World"
 WScript.Echo "Hello " & name
</script>
</job>

The script just contains one statement which concatenates two sub-strings and creates a message box. This script causes a run-time error reporting "Unterminated entity reference - matching ';' not found". First I had no idea what an "entity" shall do within a VBScript script. As I begun to view the code thought the "classes" of an xml parser, I came quickly across the problem. In xml we may define entities like &name; within the document. These entities will be expanded during parsing. And this is also the case within the sample code given above. The WSH xml parser finds the & character and a name. So it searches for the terminal character ";". After I exchanged the &-operator (for string concatenation) with a +-Operator the sample file works without further problems. But this is the wrong approach. To avoid further conflicts, we must enclose the script code into an CDDATA element like it is shown below:

<?xml version="1.0"?>
<job id="Test">
<?job debug="true"?>
<script language="VBScript">
<![CDATA[
Dim name
name = "World"
 WScript.Echo "Hello " & name
]]>
</script>
</job>

TIP: At this point I like again to point script developers to the PrimalScript 2.0 script editor (no, I'm don't belong to this company). This editor inserts your script code automatically into CDATA elements, if the source contains <?xml version=1.0">. Further information may be obtained from Sapien's website www.sapien.com.

And here is the ZIP archive with the sample files: News4.zip

Ok, that's all for this time. I continue to investigate WSH 2.0, and I will provide (hopefully I have time enough) the results in future news letters. And at least, I guess/hope the results will be available soon as a printed WSH title from Microsoft ...

Further samples and details may be found also in my WSH Bazaar. Have a look at the sample page and at the WSHExtend Programmers Reference.

Other topics I mentioned in my last newsletters (like shell access) are already described in my WSH Bazaar as ordinary samples. So check out the WSH Bazaar.

Planned topics for newsletter #5 and future newsletters:

If I had time, I will investigate WMI and more. But first I have to dig a bit more into WSH 2.0. To all of you scripters, I wish you a merry christmas and a happy new year. Let's continue "happy" scripting in the next millenium, till the next newsletter arrives...


(c) G. Born, 16 - Dec. 1999