Tricks to make it work and tasks you can perform
Microsoft Word is familiar to most systems administrators as an everyday application. In fact, we're so used to using it as a word processor that few of us ever consider using it in administrative scripting. However, Word supports automation internally, and because it's a COM class, you can start it from a script, then use most of its internal tools. Word started from a script is a useful tool if you know the details to watch for when working with it and some specific tasks you can perform with it. In this article, I cover these details and tasks and demonstrate a VBScript class named WordWrapper that simplifies using Word from a Windows Script Host (WSH)hosted VBScript script. WordWrapper automates some of the more difficult details of using Word from a script--in particular, a successful exit when your script terminates.
People sometimes feel uncomfortable using Word for scripting. One reason is that the Word object model is extremely complex and the documentation for it isn't installed by default when you install Microsoft Office. Another problem if you want to use Word remotely is that it's designed to run interactively in a graphical session; Microsoft doesn't support Word running as a remote application, although Word can do so under the right conditions.
To obtain details about the Word object model, you can do a custom installation of Word or Office and choose to install the Word Visual Basic for Applications (VBA) Help documentation. The location of the VBA Help files varies depending on your version of Word, so I can't point you to the exact spot at which you'll find the object-model Help, but you might find the Microsoft article "HOWTO: Find and Use Office Object Model Documentation" (http://support.microsoft.com/?kbid=222101) useful. For tips about browsing the Word object model, see the Web-exclusive article "How to Examine Components with Word's Object Browser," December 2003, InstantDoc ID 40884. The author focuses on the Object Browser as a generally useful tool for examining object models; it works quite well for looking at Word, too. If you need to run Word in a session without a GUI or with no profile loaded, you might want to refer to the Microsoft article "INFO: Considerations for Server-Side Automation of Office" (http://support.microsoft.com/?kbid=257757).
I occasionally hear from scripters that using an incredibly complex tool such as Word to perform simple tasks from a script seems silly, but Word's complexity needn't be a consideration. Scripting is fundamentally a "gluing" activity in which we use preexisting tools to perform a job--and Word is definitely a tool.
Starting Word from a Script
Starting Word from a WSH-hosted VBScript script is simple. You can use the code at callout A in Listing 1 to set an object reference to Word. This code creates an empty Word application with no open documents. Although I don't use documents in this article, you should remember that you need to open or create a document to perform document manipulation, as you do in any application that uses documents. That fact might seem obvious now, but it's easy to forget the first time you try to use a property of Word (or Microsoft Excel or Microsoft Internet Explorer--IE) documents.
Word runs out of process--it's an executable and thus works in a separate process from your script. Like any other out-of-process executable, it can continue to run after your script ends if you don't explicitly close it. After you finish using Word in a script, always exit it by using its Quit method, as the code at callout E in Listing 1 shows.
False is an optional argument that tells Word to discard any documents you've modified in your session. Because I don't manipulate documents in this article's code, saving isn't important. If you do eventually use this article's code to work with documents, you should be aware that the complete syntax of Word's Quit method is
You can use the OriginalFormat field to specify the format of a saved document (important when you open a non-Word file in Word) and the RouteDocument field to route the Word document if a routing slip is attached. See the Word Help documentation for details about these parameters.
The Quit method closes only the instance of Word that you opened with the script. If you have another instance of Word running, it won't be affected.
Word Functions That Don't Work Well with Windows Scripts
Because of its age and use on multiple platforms, Word has some idiosyncrasies that can confuse scripters and cause problems with Windows scripts. For example, Word's FreeDiskSpace property is built on a legacy API that returns a 32-byte Long value and thus can display a maximum of 2GB of free-space information, which is inadequate for most modern hard drives. Word's ShowClipboard, MountVolume, ComputerType, MacintoshName, and QuickDrawInstalled properties are for the Macintosh platform and don't work at all on Windows systems. To learn more about these and other Word properties and methods, you can look them up in the VBA Help files or in the Word Object Browser.
You can use a script to iterate through Word document paragraphs. However, doing so can be very slow.
Using Word's System Space
Word has a small medley of methods that can provide information about the current system configuration and the user. You can use Windows Management Instrumentation (WMI) to obtain these property values on many systems, of course, but Word works fine on legacy OS systems that have Word but not WMI.
The code at callout B in Listing 1 echoes four pieces of information that are useful but are hard to obtain from a Windows 9x system. The Version property of Word.System is particularly useful. Version returns a number (e.g., 5.1 for Windows XP, 5.0 for Windows 2000, 4.1 for Windows 98), so you can easily use it to test for a system's OS level. For example, if you have a script component that will work only on Win2K or later, you can insert the lines
into your code so that the code will quit on systems running earlier Windows versions.
Word.System's OperatingSystem property echoes back "Windows NT" on Windows 2003, XP, Win2K, and NT 4.0 systems, letting you know that the system supports special capabilities inherent in NT-based kernels that might be needed for domain and user administration.
The Word.System methods are generally reliable, but they don't return the breadth of information available from WMI. If Word is already available on legacy Win9x and NT 4.0 systems that you plan to upgrade soon, you might find the Word.System functions useful. If you plan to keep those systems around, however, installing the WMI extensions for them is probably a good idea. For more information about doing so, see the Windows & .NET Magazine article "Adding WMI to NT 4.0 and Win95 Systems," May 2001, InstantDoc ID 20377. You can download the core WMI components for these legacy systems from http://www.microsoft.com/downloads/details.aspx?familyid=afe41f46-e213-4cbf-9c5b-fbf236e0e875.
Using Word to Read from and Write to .ini and .reg Files
Word.System's PrivateProfileString property provides a valuable capability: wrapping up the PrivateProfileString API calls used for reading from and writing to .ini files. Not only will .ini files be with us for a long time to come (Microsoft .NET uses them for __AssemblyInfo__.ini files in its assembly cache), but a host of legacy custom applications and tools use them. You can also use .ini files to make data that's used by a script but isn't tied to a particular machine or user persist. PrivateProfileString's most significant restriction is that values that you read or write must be a one-line unique key-and-value pair separated by the equals (=) sign. The code at callout C in Listing 1 shows how to write custom data to a Windows 2003, XP, Win2K, or NT setup file for instant customization of automated installs; the code at callout D shows reading the FullName value back from the file and echoing it for inspection. I've found this ability so convenient that I wrote a command-line script--iniword.wsf--just to retrieve or set values in .ini files.
Using Word to Clean Up Text
Word's CleanString method is like an industrial-strength scrubber for random text--useful for strings that occasionally have unusual nonprinting characters. CleanString leaves tabs, spaces, and carriage returns alone but replaces any other unprintable character with a space. One notable exception is that when a carriage return precedes a line-feed character, CleanString deletes the line-feed character, but when the line-feed character is alone, CleanString replaces it with a carriage return. Both actions preserve the original line count because Windows sees a carriage return plus a line-feed character or a carriage return alone as one end-of-line character.
A Word Class Wrapper
Word is an excellent candidate for wrapping up inside a VBScript class. If you aren't familiar with VBScript classes, look up Class in the WSH documentation, or check the online Help at http://msdn.microsoft.com/library/en-us/script56/html/vsstmclass.asp. A more advanced and much more useful approach to creating a wrapper is to use Windows Script Components (WSC); writing a WSC component makes the wrapper available to any script on the system on which the component is installed. For more information about WSC, see "Introducing Windows Script Components" at http://msdn.microsoft.com/library/en-us/script56/html/letintro.asp. You can find an introduction to WSC in the Web-exclusive article "Use WSC to Write Reusable Code," November 2000, InstantDoc ID 16166, and instructions for writing a WSC component in a four-article series that begins with "Scripting Solutions with WSH and COM: Using WSC to Build a Progress Bar Dialog Box, Part 1," September 2000, InstantDoc ID 9802.
Writing a custom class lets you create custom objects that work exactly the way you want them to; I've done this with the WordWrapper class that Web Listing 1 shows. The class begins with a Class statement and the class name. If you haven't used classes before, a few VBScript keywords will be new to you. The keyword Public used with a function, subroutine, property, or variable declaration explicitly makes that item a usable method or property that you can call from outside the class. Marking a procedure with the keyword Private hides the procedure from view outside the class. Making the declaration for Application public rather than private facilitates experimenting with other features of Word; Application is an object reference to Word. You can directly reference the Word.Application object inside the WordWrapper class by using a line such as
Whenever a class starts up, the Class_Initialize subroutine runs automatically; you don't need to call it explicitly. The Class_Initialize method sets the object reference to Word.
The code inside the Class_Terminate subroutine performs a crucial task that makes running Word from a class easier than running Word directly. The Class_Terminate procedure runs automatically when WordWrapper exits and forces Word to close so that you don't end up with Word running hidden after the script finishes execution.
To use the class, Word must be on your PC, and you must include the class in your script. You then must create a reference to it just as you do any class before you use it, as the code at callout A in Listing 2 shows.
The WordWrapper property named Properties is nothing more than a dictionary collection of the systems administratorusable properties exposed by Word. To obtain the OS version, you'd first set a reference to the dictionary:
then call the Version property by name:
just as you would any member of a scripting dictionary. To show all the properties in the Properties dictionary, you just iterate through all the dictionary keys, as the code at callout B shows.
Writing and reading .ini file strings are simple method calls, as the code at callout C shows. These calls are similar to calls to the PrivateProfileString methods when using Word directly; wrapping the functions just makes the method names more intuitive for people who don't know the API call names.
Word is well worth exploring, because it exposes literally hundreds of classes and interfaces, each of which contain at least a few and as many as a hundred or so properties, methods, and events. To find out more information about the exposed classes and interfaces, consult the resources mentioned near the beginning of this article.