Previous Section Table of Contents Next Section

Scripting and XML

WSCs are regular text files, but they require a special XML formatting to contain the script. The XML helps describe what the WSC does, and how it is activated and used. Probably the easiest way to see how it works is to see an example, so take a look at Listing 25.1. This is a sample WSC that performs several Windows Management Instrumentation (WMI) functions. I adapted the script from one first provided on www.wshscripting.com, an unfortunately now-defunct Web site that offered scripting examples.

Listing 25.1. WMIFunctions.wsc. Example WSC.

<?xml version="1.0"?>

<package>

 <comment>

 WMI Management Library

 </comment>

 <component id="WMILIB">

  <?component error="true" debug="true" ?>

  <registration progid="WMILIB.WSC"

  classid="{61E6E0DC-4554-4D12-A9F4-D8E70DBCF318}"

  description="WMI Library" remotable="no" version="1.00">

  </registration>

  <public>

   <method name="Shutdown">

    <parameter name="Host"/>

   </method>

   <method name="Reboot">

    <parameter name="Host"/>

   </method>

   <method name="StartProcess">

    <parameter name="Host"/>

    <parameter name="CommandLine"/>

    <parameter name="StartDirectory"/>

   </method>

   <method name="Processes">

    <parameter name="Host"/>

   </method>

   <method name="EndProcess">

    <parameter name="Host"/>

    <parameter name="ProcessID"/>

   </method>

  </public>

  <implements id="ASP" type="ASP"/>

  <reference guid="{00000205-0000-0010-8000-00AA006D2EA4}"

  version="2.0"/>

  <object id="Recordset" progid="ADODB.Recordset"/>

  <script id="Implementation" language="JScript">

<![CDATA[

var description = new WMILIB;



function WMILIB()

{

 this.Processes = Processes;

 this.StartProcess = StartProcess;

 this.EndProcess = EndProcess;

 this.Reboot = Reboot;

 this.Shutdown = Shutdown;

}



function Shutdown(Host)

{

 try

 {

  var wql = "SELECT * FROM Win32_OperatingSystem WHERE

  Primary=True";

  var os = GetObject("winmgmts://" + Host +

  "/root/cimv2").ExecQuery(wql);

  for(var en = new Enumerator(os); !en.atEnd();

  en.moveNext())

   en.item().ShutDown();

  return true;

 }

 catch(e)

 {

  return false;

 }

}



function Reboot(Host)

{

 try

 {

  var wql = "SELECT * FROM Win32_OperatingSystem WHERE

  Primary=True";

  var os = GetObject("winmgmts://" + Host +

  "/root/cimv2").ExecQuery(wql);

  for (var en = new Enumerator(os); !en.atEnd();

  en.moveNext())

   en.item().Reboot();

  return true;

 }

 catch(e)

 {

  return true;

 }

}



function StartProcess(Host, CommandLine, StartDirectory)

{

 try

 {

  var ProcID;

  var Proc = GetObject("WinMgmts://" + Host +

  "/root/cimv2").Get("Win32_Process");

  Proc.Create(CommandLine, StartDirectory, ProcID);

  return true;

 }

 catch(e)

 {

  return false;

 }

}



function EndProcess(Host, ProcessID)

{

 try

 {

  var wql = "SELECT * FROM Win32_Process WHERE ProcessId="

  + ProcessID;

  var procs = GetObject("WinMgmts://" + Host +

  "/root/cimv2").ExecQuery(wql);

  for(var en = new Enumerator(procs); !en.atEnd();

  en.moveNext())

   en.item().Terminate;

  return true;

 }

 catch(e)

 {

  return false;

 }

}



function Processes(Host)

{

 try

 {

  var wql = "SELECT * FROM Win32_Process";

  var procs = GetObject("WinMgmts://" + Host +

  "/root/cimv2").ExecQuery(wql);

  var values = new ActiveXObject("Scripting.Dictionary");

  for(var en = new Enumerator(procs); !en.atEnd();

  en.moveNext())

   values.Add(en.item().ProcessId, en.item().Description);

  return values;

 }

 catch(e)

 {

  return new Array(e.description);

 }

}

]]>

  </script>

 </component>

</package>

This particular WSC is actually written in JScript (also called JavaScript or ECMAScript), not VBScript. That's an important thing to note, because it doesn't matter what language the WSC is in. You can still use it in your own VBScript files. For this example, I ignore the actual script code and focus just on the XML packaging that makes this a WSC.

All WSCs need to start with a basic XML declaration, and a <package> tag. This tag marks the beginning of the WSC package.


<?xml version="1.0"?>

<package>

Next, the script includes a comment contained in <comment> tags. The comment provides a helpful description of what the WSC does. Notice the closing </comment> tag; all XML tags must come in pairs. Therefore, <comment> is paired by </comment>. Tags must also be nested, which means the </comment> tag must appear before a </package> tag, thus fully enclosing the comment within the package.


<comment>

WMI Management Library

</comment>

Next, the script creates an actual component. Note that each WSC file can contain multiple components within a single package, but a single file can only contain a single package. This component also contains a special tag that specifies how errors will be handled. Setting error equal to true forces errors that occur within the WSC to be displayed; setting debug to true allows the component to be debugged using the Windows Script Debugger.


<component id="WMILIB">

 <?component error="true" debug="true" ?>

Next is an important piece of the WSC: the registration. Just as the FileSystemObject has a class ID and GUID, so must your WSCs. Most importantly, these must be unique. There are a number of parameters required.

  • Progid is optional, but provides other programmers with a friendly way of referencing your WSC. "Scripting.FileSystemObject" is an example of a progid.

  • Classid is required, and must be a unique GUID. Microsoft provides utilities such as Uuidgen.exe to produce unique GUIDs that you can use. Editors like PrimalScript can also make one up for you.

  • Description is optional, and provides a brief description of the component. This description appears in certain visual development tools when your component is loaded.

  • Version is also optional, and should be a numeric version number as shown here.

  • Remotable is optional, and indicates whether the script can be running remotely using Distributed COM. I won't be covering remoted WSCs in this book, although you can read more about them at http://msdn.microsoft.com/scripting.


<registration progid="WMILIB.WSC"

classid="{61E6E0DC-4554-4D12-A9F4-D8E70DBCF318}"

description="WMI Library" remotable="no" version="1.00">

</registration>

Next, your WSC needs to advertise the functions and subs it offers. These are referred to using the COM term, method. As you can see here, each method has its own name and list of parameters, which correspond to the input parameters of the appropriate functions or subs. These are all contained with a <public> section, indicating that these methods can be used by other scripts.


<public>

 <method name="Shutdown">

  <parameter name="Host"/>

 </method>

 <method name="Reboot">

  <parameter name="Host"/>

 </method>

 <method name="StartProcess">

  <parameter name="Host"/>

  <parameter name="CommandLine"/>

  <parameter name="StartDirectory"/>

 </method>

 <method name="Processes">

  <parameter name="Host"/>

 </method>

 <method name="EndProcess">

  <parameter name="Host"/>

  <parameter name="ProcessID"/>

 </method>

</public>

This WSC specifies an <implements> tag, which in this case grants it access to the ASP object model. This isn't necessary unless you want the WSC to be accessible from ASP pages. The <reference> tag specifies an external type library used by the script; this is also optional. In this case, the external type library is Microsoft's ActiveX Data Objects (ADO), so an <object> tag is used to reference it.


<implements id="ASP" type="ASP"/>

<reference guid="{00000205-0000-0010-8000-00AA006D2EA4}"

version="2.0"/>

<object id="Recordset" progid="ADODB.Recordset"/>

Next comes the actual script, enclosed by a <script> tag that includes the language. Following that are the actual functions and subs that make up the script-in this case, all Jscript, but they could be VBScript just as easily. Notice that the parameters of each correspond to the parameters specified for the <method> tags earlier.


  <script id="Implementation" language="JScript">

<![CDATA[

var description = new WMILIB;



function WMILIB()

{

 this.Processes = Processes;

 this.StartProcess = StartProcess;

 this.EndProcess = EndProcess;

 this.Reboot = Reboot;

 this.Shutdown = Shutdown;

}



function Shutdown(Host)

{

 try

 {

  var wql = "SELECT * FROM Win32_OperatingSystem WHERE

  Primary=True";

  var os = GetObject("winmgmts://" + Host +

  "/root/cimv2").ExecQuery(wql);

  for(var en = new Enumerator(os); !en.atEnd();

  en.moveNext())

   en.item().ShutDown();

  return true;

 }

 catch(e)

 {

  return false;

 }

}



function Reboot(Host)

{

 try

 {

  var wql = "SELECT * FROM Win32_OperatingSystem WHERE

  Primary=True";

  var os = GetObject("winmgmts://" + Host +

  "/root/cimv2").ExecQuery(wql);

  for (var en = new Enumerator(os); !en.atEnd();

  en.moveNext())

   en.item().Reboot();

  return true;

 }

 catch(e)

 {

  return true;

 }

}



function StartProcess(Host, CommandLine, StartDirectory)

{

 try

 {

  var ProcID;

  var Proc = GetObject("WinMgmts://" + Host +

  "/root/cimv2").Get("Win32_Process");

  Proc.Create(CommandLine, StartDirectory, ProcID);

  return true;

 }

 catch(e)

 {

  return false;

 }

}



function EndProcess(Host, ProcessID)

{

 try

 {

  var wql = "SELECT * FROM Win32_Process WHERE ProcessId="

  + ProcessID;

  var procs = GetObject("WinMgmts://" + Host +

  "/root/cimv2").ExecQuery(wql);

  for(var en = new Enumerator(procs); !en.atEnd();

  en.moveNext())

   en.item().Terminate;

  return true;

 }

 catch(e)

 {

  return false;

 }

}



function Processes(Host)

{

 try

 {

  var wql = "SELECT * FROM Win32_Process";

  var procs = GetObject("WinMgmts://" + Host +

  "/root/cimv2").ExecQuery(wql);

  var values = new ActiveXObject("Scripting.Dictionary");

  for(var en = new Enumerator(procs); !en.atEnd();

  en.moveNext())

   values.Add(en.item().ProcessId, en.item().Description);

  return values;

 }

 catch(e)

 {

  return new Array(e.description);

 }

}

]]>

The script winds up by closing the open <script>, <component>, and <package> tags. That's it!


  </script>

 </component>

</package>

To make the WSC usable on your computer, there are two additional steps you need to take. First, you should generate a type library. This enables editors like PrimalScript to display pop-up help when using the WSC in another script; you can use tools like PrimalScript to generate the type library file, which is saved in a file with a TLB filename extension. Generally, you can also right-click the WSC file itself and select Generate type library from the context menu.

You also need to register the library. This adds it to the system registry by using Regsvr32, in much the same way that new DLLs are registered with the system. Again, right-clicking the WSC file usually displays a Register component option on the context menu, and tools like PrimalScript also offer registration menu options. You can also manually register the component from the command line:


Regsvr32 scrobj.dll /n /i:file:\\path\filename.wsc

After the WSC is properly registered, you can start using it within your scripts. For the example in this chapter, you would use something like this.


Dim oWMILib

Set oWMILib = CreateObject("WMILIB.WSC")

If a WSC isn't registered, you can still get to it. You just have to use a slightly different method. If the WSC file is named WMILib.wsc, and stored in C:\My Documents, you could use the following.


Dim oWMILib

Set oWMILib = GetObject("script:c:\My Documents\WMILib.wsc")

This technique locates and loads the script, without having the WSC actually listed in the system registry. However, you do have to know the exact location of the WSC file.

    Previous Section Table of Contents Next Section