Many everyday programming problems exist for which the Windows Script Host (WSH) object model just can't provide a definitive and satisfying solution. The WSH object model is designed to offer minimal support to scripters—a sort of survival kit. Certainly, the WSH object model can't compete with the Windows software development kit (SDK); indeed, it isn't supposed to.
The WSH object model collects facilities for script programmers, but if you consider it a kind of COM-automation version of the Windows SDK, you couldn't be further from the truth. For example, tasks that you can easily accomplish in Microsoft Visual Basic (VB) are impossible in VBScript. This month, I illustrate some of these problems and provide a COM object that solves them.
The Windows XP Password Component
In Bob Wells, Rem, November 2001, InstantDoc ID 22712, Wells answered a question about whether WSH has a component that can accept and verify a password without displaying it as the user types it in a text box. Although WSH doesn't provide a facility for data entry, you can rely on the services of VBScript's InputBox function to obtain basic input functionalities. However, the dialog box that the InputBox function displays works only with clear text and provides no programmatic way to hide the typed text.
Wells also explained that Windows XP and Windows .NET Server (formerly code-named Whistler) come with a new system component called ScriptPW.Password, which lets you silently retrieve a user's password (i.e., retrieve the password while hiding it). The code snippet in Listing 1, page 6, shows how to use the component. After you've created an instance of the component, you use its GetPassword method to collect a password without displaying the typed text. GetPassword returns as a string the password that the user entered.
The ScriptPW.Password component has two major problems. First and foremost, it's available only to XP and .NET Server users. Second, it doesn't work in a GUI environment. A third and probably less important drawback of this component is that it captures any text entered but doesn't display any feedback to the user. You don't see the typical asterisks (*) of protected text; instead, you just see a blinking cursor that doesn't even move as you type.
Another limitation is that Script- PW.Password works only if the script that calls it runs on a console box under the control of cscript.exe. In other words, if you need to pick up a password in a shell-level script—in which the host is wscript.exe—you need another solution. The only valid alternative is writing or somehow obtaining an ad hoc external component.
A Windows Password Component
The vbsinput.dll file, which is available for download from the Code Library on the Windows Scripting Solutions Web site (http://www.winscripting solutions.com), includes an object whose programmatic identifier (Prog-ID) is VbsInput.InputBox. The InputBox component has a UI and runs under wscript.exe. However, it's not just a simple component that captures a password while hiding its characters. The InputBox object is a full-featured object specialized for data entry. Among other things, you can use the object to retrieve a password.
You create an instance of the InputBox object with the code that Listing 2 shows. The object displays a dialog box that's nearly identical to the one that pops up when you call VBScript's InputBox function. The VbsInput .InputBox object is COM based, so you use properties to program it instead of command-line parameters. Table 1 lists all of VbsInput.InputBox's properties. You set the title of the dialog box through the Title property. You can also add a description of the information the user is required to enter. You add this description through the Label property:
dlg.Title = "My Application"
dlg.Label = _
"Type in your password"
To embellish the dialog box, you can customize the icon that appears on its left side. This icon defaults to the Windows logo. A comma-separated string identifies the icon. The first part of the string is the name of the file that contains the icon, and the second part is the 0-based index of the icon in that file:
dlg.Icon = "shell32.dll,44"
If you identify the icon through an .ico file, the index is typically 0 because .ico files usually contain only one icon. However, some tools let you create .ico files with multiple icons. Several .dll and .exe files include one or more icons. You can extract any icon from such executable files by specifying the index that points to the icon's ordinal position. For example,
points to icon 45 in the resource section of the shell32.dll file. To see which icons an executable contains, you can right-click an existing shortcut (or create a temporary one), then select Properties. On the Shortcut tab, click Change Icon. In the Change Icon dialog box, which Figure 1 shows, make sure the icon you want belongs to the executable. Find the index of the desired icon by counting the icons from top to bottom and subtracting 1 from the number that corresponds to the desired icon. Icon 45 (index 44) is the one selected in the figure.
By default, the InputBox object is simply a box that can collect plaintext. To make it work for passwords (i.e., to make it hide the typed text), you must set the Password property to True or a nonzero value, as in
dlg.Password = true
The property defaults to False. When you've properly configured the InputBox object, you use the Show method, which Listing 3 shows, to display the dialog box. The Show method in Vbs- Input.InputBox takes no argument and returns a string value that contains the contents of the input buffer. Figure 2 shows the resulting dialog box. If the user clicks Cancel in this dialog box, the method returns an empty string. You can use the Text property to access the contents of the input buffer:
MsgBox "You typed: " & dlg.Text
Listing 4 shows the complete script for generating a password-entry text box. The overall behavior of the component doesn't change significantly whether the component is working in password or normal mode. The only real difference is in how the typed text appears: as clear text or hidden by a sequence of asterisks.
Setting Password Restrictions
The InputBox component is a COM automation object that doesn't rely on any feature specific to a particular version of Windows. For this reason, you can use it with any 32-bit version of Windows (i.e., Windows 95 and later). Make sure that you copy and register the object on any machine on which a script using it will run. To register the component, you can use the familiar Regsvr32 system utility.
Passwords are usually subject to restrictive rules. For example, their length must fall within a certain range or they can't have a mix of lowercase and uppercase letters. To help with these restrictions, the InputBox object provides three properties: MinLen, MaxLen, and Uppercase. You set these properties as follows:
dlg.MinLen = 4
dlg.MaxLen = 8
dlg.Uppercase = true
The MinLen property indicates the minimum number of characters that the user must enter for the dialog box to close successfully. The MaxLen Property lets you specify the maximum length of the text that users can enter. When the user attempts to close the dialog box by clicking OK, the dialog box will close only when the number of characters in the text buffer falls within the range of MinLen to MaxLen. When the user has entered the maximum number of characters, the control stops accepting further input. If the length of the text typed is less than the MinLen value, a beep warns the user. If you set the Uppercase property to True, the property automatically converts the text to uppercase.
Multiline Text Boxes
VBScript's InputBox function is good at retrieving one line of text and, consequently, is ideal when you need to retrieve short text. But what about potentially lengthier contents, such as user comments? VbsInput.InputBox's Multiline property serves just this purpose. When you set this property's value to True (False is the default), the text box provides space for 250 characters, with three lines of text visible at one time and a scroll bar to navigate the text. Listing 5, page 8, shows the code to create a multiline text box.
The multiline InputBox text box interprets the Enter key as a command to move to the next line. The single-line text box doesn't process the Enter key, which the surrounding form interprets as a shortcut to execute the code behind the button with the focus. Whatever the number of displayed lines of text, both the Text property and the return value of the Show method contain the text that the user has typed. Carriage returns and blanks are maintained.
Numbers-Only Text Boxes
Another frequently asked-for script input-box feature is the ability to accept only numbers. The VbsInput .InputBox component provides this capability through its NumberOnly property. If you set this property to True (False is the default), the control automatically accepts only digits. If a user enters a letter, the character is refused and a beep warns the user. Listing 6 shows how to set the NumberOnly and Align properties for a numbers-only text box.
A Powerful Solution
The WSH object model isn't particularly rich in UI functionality. However, it provides good support for COM automation objects, letting you extend the WSH environment functions with components purchased or written by inhouse C++ or VB developers. As a general rule, remember that anything you can accomplish at the Windows SDK level you can accomplish at the WSH level as long as you know how to write a tailor-made COM automation component.
The InputBox COM component provides a powerful way to perform a number of everyday tasks for which scripts just don't exist (at least so far). However, the next landing of the Microsoft .NET Framework on scripting soil promises to make the whole Windows API available to scripters.