Conventional wisdom holds that the Windows Scripting Host (WSH) provides the graphical equivalent of the humble DOS batch file. Although this assertion has an element of truth, looking at WSH as a mere batch file replacement sells it short. WSH scripts can easily read and write to files--tasks that your garden-variety batch files can't handle. You just have to know how to use ActiveX objects to bring additional functionality into the WSH environment.
As Michael Otey pointed out in "An Introduction to WSH" (December 1998), WSH is a script processor that can support multiple scripting languages. WSH natively supports Visual Basic Script (VBScript) and JScript, but it can also support just about any other scripting environment, provided a clever programmer has written a processing engine and made the proper file associations. Programmers have already accomplished these tasks for the Perl and Python script languages.
Deciding which language to use depends on personal preference. (For more information about two commonly used scripting languages, see the sidebar "The Pros and Cons of Perl and VBScript," page 2.) In this article, I'm using VBScript, because Windows power users and developers often use it. More important, you can easily push VBScript beyond your usual expectations by using ActiveX objects to create simple applications. Built on the Component Object Model (COM), ActiveX is a technology that lets software components interact with one another, no matter what language the software developers used. In other words, ActiveX provides language-neutral objects that you can use with any scripting language. In this article, I'll show you how to use an ActiveX object to create a basic revision control system. After I explain how this application operates, I'll describe how the ActiveX object works and how to use it in scripts.
The revision control system consists of two scripts (getfile.vbs and putfile.vbs) whose purpose is to make sure that only one member of a workgroup has permission to change a file at any given time. To use this system, you need a shared drive that contains a directory on which workgroup members store shared documents.
Here's how the revision control system works. When workgroup members want to change a file, the getfile.vbs script retrieves a writable copy of the file. (In this system, the shared files that you store have read-only permissions.) The script consults an .ini file in the same directory as the shared file to see whether anyone else is using the file. If another workgroup member has control of the file, a message box specifies who has the file open. If no one is using the file, the system opens it for the member and notes that member's username in the .ini file. After the member has finished making revisions, the putfile.vbs script returns the new version to the shared directory.
The crux of this revision control system is the .ini file, which shows whether a particular file is available for editing or whether someone else is editing the file. The .ini files are text files. They have named sections (e.g., Status) that store key and key value pairs, which typically contain configuration information.
To keep this example simple, I'm using one .ini file for each shared file and giving the .ini file the same name as the shared file, except that it has the .ini extension. I'm assuming that some other process originally created the .ini files when the workgroup members first posted the shared files to the shared directory. However, in a real application, you need to check for .ini files and create new .ini files when you need them. Because several shared files might have the same root name but different extensions, I recommend that you include a separate section within the .ini file for each extension type. To save space, I'll leave these tasks as an exercise for you to tackle.
Understanding and Finding the ActiveX Object
If you want to manipulate entries within an .ini file, you need to read and write to that file. Although most books about VBScript state that this scripting language doesn't support access to local files, this statement is a myth. A batch file system that doesn't let you move, copy, and delete files is useless.
VBScript's Scripting object model provides an object, FileSystemObject, that gives you access to the local hard disk. For example, Listing 1 contains code that uses the FileSystemObject to copy a file. In this code, you use WScript's CreateObject method to create an instance of Scripting.FileSystemObject, which is a COM object. You then use VBScript's Set statement to assign that instance to the FileObject variable. Next, you assign MySource to the path where the original file resides. Finally, you use VBScript's If...Then statement to first test whether the original version of the file you want to copy exists (you'll get a runtime error if you try to copy a file that doesn't exist), then copy the file to the "c:\test2\" directory. (If you're unfamiliar with FileSystemObject, see "An Introduction to WSH," December 1998.)
You can use the FileSystemObject not only to copy files and folders, but also to move, delete, and rename them. In addition, you can read and write to files, if you're willing to stick to sequential file access. You can even tackle .ini file management tasks by reading the entire file into a buffer, using string manipulation functions to make changes, and writing the buffer back.
However, there's an easier way to handle .ini file management tasks. In Listing 1, you created an instance of Scripting.FileSystemObject so that you can access the local file system. If you can create an instance of a COM object, you can create an instance of other types of objects, including ActiveX. If you can find an ActiveX object that performs the task you want to do, you can use that object in WSH.
I searched for ActiveX components that might fit this example. An ActiveX component typically consists of an Object Linking and Embedding (OLE) custom control (OCX) file that contains the ActiveX object (most components contain only one object) and documentation. At first I found several Web sites (such as CNET's Download.com at http://www.download.com and Gamelan at http://www.gamelan.com) that offer ActiveX components that open and manipulate files. But then I discovered Kupex INI Tools 1.0, a component that specializes in reading and writing to .ini files. You can download this component for free from Kupex Software's Web site at http://www.kupex.com.
Kupex INI Tools is blissfully simple, offering one object with four methods. Table 1 lists those methods and describes the tasks they perform.
Activating Your ActiveX Component
If you're not used to working with COM objects outside the realm of those in mainstream applications such as Microsoft Office, you might be stumped after you've downloaded an ActiveX component such as Kupex INI Tools. How does the system know it has this object available? How does the system relate the name of the object as it appears in a script with the file name of the OCX file that contains the object?
The ActiveX component might come with an installer that automatically enters the globally unique ID (GUID) in the Registry. However, Kupex INI Tools doesn't include any installation tools. This situation is common, because developers often assume that you'll use this component within the VB development environment, which would handle some of the installation tasks for you.
If the component doesn't have an installer, you can manually enter the information or use Microsoft's Regsvr32.exe utility. To use this utility, just run the command
where filename is the name of the OCX file containing the ActiveX object. The utility then automatically registers the object. You can find this useful utility in the \Tools\RegUtils directory on the VB distribution CD-ROM. Another way to get the utility is to download an ActiveX component that includes it. (Distributing Regsvr32.exe alone is a license violation.) For example, dcsMixer, which is available from CNET's Download.com, includes Regsvr32.exe.
Having registered INI Tools, you're ready to use it. You might, however, face another problem: How do you refer to the object in a script? ActiveX components typically have poor documentation. In the case of Kupex INI Tools, Kupex Software includes only a page of documentation that lists the methods the object uses and the parameters the methods expect. So, you know the names of the methods that this ActiveX object supports, but not the name of the object. In addition, you know the OCX filename, but not the internal, or short, name. In a script, you need to refer to the OCX file in such a way that COM can search the Registry to find and load the file when you run the script. Running Regsvr32.exe performs the preliminary work by creating entries to associate a short name (in this case, INITools), a GUID, and the location of the OCX file on your system. You must find out the short name so that COM can find the associated information in the Registry.
You use an object browser to peer into the OCX file to view the file's short name, its object, and the object's methods. For an inexpensive object browser, you can use Microsoft Word's Visual Basic for Applications (VBA) development environment. Open Word, and select Tools, Macro, Visual Basic Editor. When the Visual Basic Editor (VBE) appears, select View, Object Browser. The Object Browser appears as a window within the VBE environment, as Screen 1 shows.
When the Object Browser first appears, it displays Word objects but not the ActiveX object you added to your system, even though you registered the OCX file. To display the ActiveX object, select Tools, References from the VBE menu. Click Browse. In the window that appears, highlight the OCX file you added and click Open. The Available References list now contains the OCX file, which VBE automatically checks. Click OK.
In the drop-down list of available libraries, you'll now find the short name (i.e., INITools) that you'll later use to create instances of the object stored in this component. Select INITools. As Screen 2 shows, the browser shows the ActiveX object, INITool. To view this object's methods and properties, highlight INITool. With short name and object information now in hand, you can start writing code.
Controlling the ActiveX Object
You use ActiveX objects in much the same way you use WSH objects. For example, Listing 2 shows how you can use the INITool object to extract information from an .ini file. The first line creates an instance of the object you want to use ("INITools.INITool") and assigns it to the FileObject variable. The second line uses the FileObject's GetFromINI method to open the local file "MyIniFile.ini" and extract the value for the key MyKey from the "My Section Header" section of the file.
ActiveX objects can differ from WSH objects in one important respect: ActiveX object methods expect you to pass in typed variables as parameters. A typed variable is a variable that holds a specific type of data (e.g., string, integer, objects), which you must previously declare before you use that variable. VBScript supports only the variant type. Variant variables can hold any type of data, so you don't have to declare in advance what type of data this variable will hold. Thus, if you were to send a variable to one of the string parameters of the GetFromINI method, the script would fail at runtime because you'd be passing a variant variable (which might, for example, contain integers) when the GetFromINI method insists on a string variable. (If you send an explicit string, however, the script would work.) To get around this problem, the getfile.vbs script uses the CStr function. This function forces the contents of a variant variable to be string data so that you can hand a string variable off to the ActiveX object's methods.
Now you're ready to see how the getfile.vbs script in Listing 3 works. Suppose a workgroup member, Mary, wants to use a file, projects.txt. After Mary logs on, the getfile.vbs script uses an InputBox to query her for the file she wants. After Mary types projects.txt, the getfile.vbs script derives the .ini filename from the filename Mary typed. The script derives the .ini filename by taking all of the characters in the filename except the last four, which make up the extension of the file (i.e., the dot and three characters). This approach isn't too sound, because in real life the extension might not be three characters. I used this approach only to illustrate the useful Left function, which returns a specified number of characters from the left side of a string. (I use a more sensible approach to handling parts of filenames in the section of the script that copies the file to the user's My Documents directory.)
With everything but the extension now stored in the iniName variable, you concatenate the string with an additional string using the plus-sign character (+). The string you're adding is the dot that separates the base file name from the .ini extension. The script then reads the Status section of the .ini file to see whether another workgroup member has checked out this file.
The Status section of the .ini file would look something like
\[Status\] AVAIL=NO OWNER=RobertR
If another user has the file open, the .ini file's AVAIL key value is NO (i.e., the file is unavailable) and the OWNER key value contains the name of the user who has checked out the file. If no one has the file open, the AVAIL key value is YES (i.e., the file is available) and the OWNER key value is blank.
If the script reads AVAIL=NO, Mary receives a message such as File not available for modification because RobertR is already editing it. If the script reads AVAIL=YES, the script resets this value to NO. The script then uses WSH's networking object (oWshNetwork) to get Mary's username and writes her username as the value of the OWNER key so that someone else trying to open the file will know who to look for.
Next, the script uses the oFileSystem object to copy the projects.txt file to the local My Documents directory. The script changes the local file's permissions attribute from read-only to writable so that Mary can edit the file. I set this attribute by writing the ToggleOffReadOnlyBit function to illustrate WSH's ability to handle subroutines and functions. This support for modular code is yet another advantage of using WSH over batch files.
The putfile.vbs script returns the new version of the file to the shared directory. Putfile.vbs uses many of the same objects, methods, and functions as getfile.vbs, so I'll let you explore the putfile.vbs script on your own. You can download putfile.vbs and the three listings in this article from the Win32 Scripting Journal Web site at http://www.winntmag.com/newsletter/scripting.
After you've explored putfile.vbs, you can try your hand at using ActiveX objects to extend WSH's functionality. Soon, you'll be writing customized applications for your system.