Introducing the Penton.RegObject object
The introduction of Windows Management Instrumentation (WMI) as a core OS component in Windows 2000 made many administrative scripting tasks far easier, but Microsoft has recognized that registry management still plays an important role in Windows management. The company has provided several tools for reading from and writing to the registry in a script. These tools include:
*Registry import (.reg) files. You can modify the registry by using a .reg file and the Regedit /s command, but this method lets you only update the registry. You can't read data from the registry, and there's no error checking. Also, a .reg file can update the registry only on a local computer, not a remote computer.
*The Reg.exe command-line utility. You can use the Reg.exe command-line utility, which is part of the OS in Windows XP and part of the resource kits for Win2K and Windows NT, to update the registry. You can also use Reg.exe to read data from the registry. However, to use the output, you have to parse it, which is an error-prone process.
*The WshShell object methods. Windows Script Host's (WSH's) WshShell object provides a set of methods for reading from and writing to the registry, but these methods suffer from limitations. As with .reg files, you can read from and write to the registry only on the local computer. Also, there are limits to the types of data you can write. For example, you can't write a REG_MULTI_SZ value or more than 32 bits of data in a REG_ BINARY value.
Although all three tools are useful in various scenarios, Microsoft has provided a more flexible tool to access the registry programmatically: WMI's StdRegProv class. This class provides a set of methods for reading from and writing to the registry. However, despite their flexibility, the StdRegProv methods are cumbersome to use and have three main limitations:
*The registry hive must be specified numerically. For example, Instead of writing HKEY_LOCAL_ MACHINE or HKLM, you have to use the hexadecimal value 80000002. Although you can use variables or constants in a script to represent these values, it would be simpler and more convenient if the StdRegProv class were able to accept the registry hive name as a string.
*There are separate methods for each data type. The StdRegProv class methods let you read and write all the commonly used registry value types, but unfortunately, you have to use a separate method for each type. For example, to read a REG_MULTI_SZ value, you have to call the GetMultiStringValue method. This limitation is bothersome when you want to read a registry value regardless of its type or if you don't know the type beforehand.
*Accessing the methods from JScript is difficult. JScript's language design dictates that primitive data types (i.e., numeric and Boolean values) are always passed by value. There's no equivalent of VBScript's ByRef keyword in a JScript function declaration that lets you pass a primitive data item by reference. ByRef parameters are used in several of the StdRegProv methods. Although you can use the SWbemMethod object and related objects to call these methods, you can't simply call the methods directly as you can in VBScript. Also, the StdRegProv methods use VBArray objects, which are Visual Basic (VB) safe arrays--and there's no built-in way to convert a JScript array to a VBArray object.
Because of these limitations, I decided to write RegObject.wsc, a Windows Script Components (WSC) component to handle the WMI StdRegProv methods. You can download RegObject.wsc from the Windows Scripting Solutions Web site. Go to http://www.windowsitpro.com/windowsscripting, enter 47540 in the InstantDoc ID text box, then click the 47540.zip hotlink. To use RegObject.wsc, you need to know about the Penton.RegObject object and its methods.Introducing the Versatile Penton.RegObject Object
The Penton.RegObject object is implemented in the RegObject.wsc file. A .wsc file is an XML-formatted file containing an ActiveX object implemented using script code. One advantage to using WSC is that you can implement the object in multiple languages. RegObject.wsc uses both VBScript and JScript to do its work.
A .wsc file contains four main XML elements: <component>, <registration>, <public>, and <script>. The <component> element contains the component's definition. The <registration> element contains the information necessary to register the component on the computer. The <public> element defines the component's interface (i.e., its properties and methods). The <script> element contains script code for the component.
You can have multiple <script> elements that implement code in different scripting languages. The Penton.RegObject object is implemented in both VBScript and JScript, so it contains two <script> elements. Listing 1 shows an excerpt from the VBScript <script> element. Code that's inside a <script> element but outside any procedures is executed when an instance of the component gets created (e.g., by using VBScript's CreateObject function).
When an instance of the component gets created, the VBScript part of the Penton.RegObject object starts by declaring a set of constants and five global variables, as callout A in Listing 1 shows. The global variables start with the g_ prefix to make them easy to identify. The g_RegTypes and g_EnumDict variables both contain Scripting.Dictionary objects. The script sets the CompareMode property of both Dictionary objects to vbTextCompare so that they perform case-insensitive string comparisons.
Next, the component populates the g_RegTypes Dictionary object with useful lookup values so that the calling code can specify string values such as HKLM instead of the numeric values required by the StdRegProv methods. The lookup values also provide for quick translation between the numeric and textual registry value types. For example, when you're writing a string to the registry, you can specify the type as the string REG_SZ rather than 1.
As callout B shows, both the g_RegResult and g_RegValueType variables are initialized with the Null value to indicate that they don't contain valid data yet. The Penton.RegObject object's Result and ValueType properties return information in these two variables. Table 1 describes the types of information they return.
The JScript <script> element uses the global variable g_dict to store a Dictionary object. The component uses this object to implement the toVBArray method, which converts a Dictionary object into a JScript array.
Understanding the Object Methods
To use the Penton.RegObject object, you need to understand its methods, which Table 2, page 12, lists. Table 3, page 12, lists the parameters common to two or more methods. Parameters unique to a method are defined in the method descriptions that follow:
Connect. Before you can use the Penton.RegObject object to work with the registry on a computer, you must first call its Connect method. This method uses the syntax
where ComputerName is a string variable containing a computer name or string constant (quoted appropriately for the language you're using). To connect to the local computer, simply specify a period ("."). The Connect method returns a zero when the WMI connection succeeds and a nonzero value when it fails. Note that if you receive a return value of 462, it means that the specified remote computer wasn't accessible. (WMI connects to remote computers using DCOM; DCOM error 462 indicates an inaccessible remote computer.)
The Connect method first uses the On Error Resume Next statement to disable VBScript's default error handling, then attempts to connect to the WMI StdRegProv object on the specified computer. The method's Result variable (not to be confused with the Penton.RegObject object's Result property) gets the result of the WMI connection attempt.
CheckAccess. The CheckAccess method lets you determine whether the current user has a desired level of access to a registry subkey. The method's syntax is
CheckAccess(DefKey, SubKeyName, Required)
where DefKey is the target hive and SubKeyName is the target subkey, as Table 3 shows. For the Required parameter, you specify a number that represents the desired access level. To determine what value to use for this number, see the StdRegProv class documentation at http://msdn.microsoft.com/library/default.asp?url=/library/en-us/wmisdk/wmi/checkaccess_method_in_class_stdregprov.asp.
When CheckAccess succeeds (i.e., the subkey exists and the method was able to retrieve the access level), the method returns a zero exit code. When CheckAccess returns a zero, you must read the Penton.RegObject object's Result property to determine whether the user has the required permissions. The Result property will contain a True value when the user has the necessary permissions or a False value when the user doesn't have the necessary permissions. When CheckAccess returns a nonzero value, the Result property will contain Null.
CreateKey. You use the CreateKey method to create a registry key. The Penton.RegObject object's CreateKey method works the same way as the StdRegProv object's CreateKey method, except that you can specify a string rather than a number when you're specifying the target hive (i.e., the DefKey parameter). The CreateKey method uses the syntax
The method returns a zero when it succeeds or a nonzero value when it fails.
DeleteKey. The StdRegProv object's DeleteKey method is limited in that you can only delete a registry key when that key contains no subkeys. The Penton.RegObject object's DeleteKey method works around this limitation by using the syntax
DeleteKey(DefKey, SubKeyName, DeleteSubKeys)
What's special about this syntax is the DeleteSubKeys parameter, which takes a Boolean value. When you specify True, DeleteKey recursively deletes any subkeys of the specified key before attempting to delete the key itself. When you specify False, DeleteKey behaves the same way as the StdRegProv DeleteKey method. If the DeleteSubKeys parameter is True but an error occurs when trying to delete a subkey, DeleteKey will simply exit with the last relevant exit code. The DeleteKey method returns a zero when it succeeds or a nonzero value when it fails.
DeleteValue. To delete a registry value, you use the DeleteValue method. DeleteValue follows the syntax
DeleteValue(DefKey, SubKeyName, ValueName)
where ValueName is the name of the value you want to manipulate (in this case, delete). The method returns a zero when it succeeds or a nonzero value when it fails.
EnumKey. The EnumKey method populates the g_EnumDict object with the subkeys in a registry key. You specify the target key by following the syntax
To access the g_EnumDict object, you use the EnumDict property, which Table 1 describes. The g_EnumDict object's keys will contain the registry subkey names; the Dictionary object's values will be empty strings. EnumKey returns a zero when it succeeds or a nonzero value when it fails.
EnumValues. The Penton.RegObject object's EnumValues method uses the StdRegProv object's EnumValues method to populate the g_EnumDict object with the specified registry subkey's values and data types. The method follows the syntax
You can use the EnumDict property to access the g_EnumDict object. This object's key/value pairs will contain the values and corresponding data types (e.g., REG_SZ, REG_DWORD). EnumValues returns a zero when it succeeds or a nonzero value when it fails.
EnumValuesAndData. The EnumValuesAndData method is the same as the EnumValues method, except that the values in the g_EnumDict object's key/value pairs contain the contents of the registry values rather than the values' data types. The EnumValuesAndData method uses the syntax
EnumValuesAndData works by first calling the StdRegProv object's EnumValues method, then iterates through the returned array of values. For each value, EnumValuesAndData retrieves the content by calling the corresponding StdRegProv Get<xxx>Value method, where <xxx> is that value's data type. For example, if the value is type Binary, EnumValuesAndData calls the GetBinaryValue method.
EnumValuesAndData adds the values' contents to the g_RegResult Dictionary object. However, the StdRegProv object's GetBinaryValue and GetMultiStringValue methods return arrays, so EnumValuesAndData converts those arrays to strings before adding the contents to g_RegResult. Note that REG_BINARY values are represented as a series of hexadecimal digits and the strings in a REG_MULTI_SZ value are delimited by pipe (|) characters.
ExistKey. You use the ExistKey method to determine whether a registry key exists. This method uses the syntax
ExistKey(DefKey, SubKeyName, KeyName)
where KeyName is the name of the key you're checking for. This method iterates through the subkeys in the specified key using the StdRegProv object's EnumKey method, which returns an array of subkey names. The Penton.RegObject object's ExistKey method compares each subkey name in the array with the name specified in the KeyName parameter. If a match occurs, ExistKey returns True; otherwise, it returns False.
ExistValue. You use the ExistValue method to determine whether a certain value exists in a registry key. This method follows the syntax
ExistValue iterates through the values in the specified subkey using the StdRegProv object's EnumValues method, which returns an array of value names. ExistValue compares each value name in the array with the name specified in the ValueName parameter. If a match occurs, ExistValue returns True; otherwise, it returns False.
ReadValue. The ReadValue method reads a registry value and its data type. ReadValue uses the syntax
ReadValue iterates through the values in the specified subkey using the StdRegProv object's EnumValues method, which returns an array containing the value names and data types. ReadValue compares each value name in the array with the name specified in the ValueName parameter. If a match occurs, ReadValue calls the appropriate StdRegProv Get<xxx>Value method. When the Get<xxx> Value method succeeds, a zero is returned. The Penton.RegObject object's Result and ValueType properties, in turn, will contain the registry value's data and data type as a string (e.g., "REG_DWORD"), respectively. When the Get<xxx>Value method fails, a nonzero value is returned. The Result property won't be changed, but the ValueType property will contain Null.
WriteValue. The WriteValue method writes data to the registry. It uses the syntax
WriteValue(DefKey, SubKeyName, ValueName, ValueType, RegData)
where ValueType is the type of value you want to write. You can specify one of the following values: REG_SZ, REG_EXPAND_SZ, REG_BINARY, REG_DWORD, or REG_MULTI_SZ. For the RegData parameter, you specify the value's data.
The WriteValue method calls the StdRegProv object's Set<xxx>Value method, where <xxx> is the type of value that corresponds to the RegData parameter. For example, if the RegData parameter specifies REG_SZ, WriteValue calls the StdRegProv SetStringValue method. If you specify REG_BINARY or REG_MULTI_SZ, the RegData parameter should contain an array of bytes or strings, respectively. WriteValue returns a zero when it succeeds or a nonzero value when it fails.
toVBArray. The toVBArray method provides a JScript script the ability to convert a JScript array into a VB safe array. This method is useful when you need to create a new VBArray object from a JScript array. For example, you can create a JScript array containing the data for a REG_BINARY value and use the toVBArray method to convert it to a VBArray object as input for the WriteValue method.
The toVBArray method uses the syntax
where JSArray contains a JScript array. The JScript array must contain numeric subscripts that start with zero and are contiguous (e.g., subscripts of 0, 1, and 3 wouldn't work, but 0, 1, and 2 would work).
The toVBArray method first empties the g_dict object, then populates it with key/value pairs. The keys contain the numeric array subscript numbers. (The keys in a Dictionary object must be unique.) The values contain the elements from the JScript array. The toVBArray method uses the Items method to return the g_dict object's items as a VBArray object.
dictToJSArray. The dictToJSArray method provides a JScript script the ability to convert a Dictionary object into a JScript array. The dictToJSArray method is useful when you're using the component's EnumKey, EnumValues, or EnumValuesAndData method in a JScript script because these methods all return a reference to a Dictionary object.
The dictToJSArray method uses the syntax
where Dictionary is a Dictionary object.
The dictToJSArray method works by calling the g_dict object's Keys method, which returns a VBArray object. Using the toArray method, dictToJSArray converts this object into a JScript array and creates a new Array object to contain that array. Using JScript's for...in statement, the dictToJSArray method iterates through the JScript array, populating it with the key/value pairs. Finally, dictToJSArray returns the resulting JScript array.
Accessing the Registry Made Simple
The Penton.RegObject object addresses the shortcomings inherent in the WMI registry methods and makes it both simple and elegant to read and make changes to the registry, no matter whether you prefer to use VBScript or JScript. Next month, I'll provide some sample VBScript and JScript code so you can get a better feel for how the object works and how to implement it in your own scripts.
|Table 1: The Penton.RegObject Object's Properties|
|Result||After calling the CheckAccess method, the property will contain True (-1) if the user has access or False (0) if the user doesn't have access. |
After calling the ReadValue method, the property will contain the registry value's data.
|ValueType||After calling ReadValue method, the property will contain the value's data type as a string (e.g., REG_SZ).|
|EnumDict||The property contains the Dictionary object that's populated by the EnumKey, EnumValues, or EnumValuesAndData method.|
|Table 2: The Penton.RegObject Object's Methods|
| ||Method ||Description ||Connect ||Connects to the StdRegProv provider on a computer. You must call this method before you can use any of the other methods. ||CheckAccess ||Determines whether the user has the permissions necessary to access a registry subkey. ||CreateKey ||Creates a registry key. ||DeleteKey ||Deletes a registry key. Unlike the StdRegProv object's DeleteKey method, this method supports deleting a key that contains subkeys. ||DeleteValue ||Deletes a registry value. ||EnumKey ||Populates a Dictionary object with the subkeys in a registry key. ||EnumValues ||Populates a Dictionary object with a registry key's values and their corresponding data types (e.g., REG_SZ, REG_DWORD). ||EnumValuesAndData ||Populates a Dictionary object with a registry key's values and their contents. ||ExistKey ||Determines whether a registry key exists. ||ExistValue ||Determines whether a registry value exists in a key. ||ReadValue ||Reads a registry value and its data type. ||WriteValue ||Writes a registry value. ||toVBArray ||Converts a JScript array into a VBArray object. ||dictToJS ||ArrayConverts a Dictionary object into a JScript array. |
|Table 3: Common Parameters for the Penton.RegObject Object's Methods|
| ||Parameter Name ||Description ||DefKey ||Specifies a registry hive. Can be one of the following string values: HKEY_CLASSES_ROOT (or HKCR), HKEY_CURRENT_USER (or HKCU), HKEY_LOCAL_MACHINE (or HKLM), HKEY_USERS, or HKEY_CURRENT_CONFIG. ||SubKey ||NameSpecifies a registry subkey (e.g., SYSTEM\CurrentControlSet\Services). ||ValueName ||Specifies the name of the registry value you want to manipulate. |