[Previous] [Table of Contents] [Next]

Accessing the Windows Registry

You can use methods of the WshShell object to access the Registry. The Registry is a database that stores internal configuration data.

WARNING
Altering the Registry can seriously damage your Windows system. Use and modify the upcoming scripts at your own risk. For tips on backing up the Registry, see my book Inside the Microsoft Windows 98 Registry (Microsoft Press, 1998).

You don't need to deal directly with these data files because Windows provides API functions for accessing the Registry. These functions organize the Registry as hierarchical trees of keys, subkeys, and values. The tree structure is also shown in the Registry Editor.

Figure 11-5 shows part of the Registry in the Registry Editor. The left pane contains the key's hierarchy, and the right pane contains the value of the current key. The Registry in Windows 95 and Windows 98 consists of six root keys. In Windows NT and Windows 2000, only five root keys (sometimes called hives) are used. All root keys start with HKEY_, and they group subkeys into categories. The root keys of the Windows 2000 Registry are shown in the figure.

Figure 11-5 Keys and values in the Registry Editor

Each key can contain zero or more subkeys. A key can have a default value associated with it; if this default value isn't set, (Default) and the string (value not set) are shown in the right pane of the Registry Editor. In the Registry Editor, you can define your own values using letters, the numerals 0 to 9, and the underscore character (_). Values can be of several data types for storing the content:

TIP
The WSH RegWrite method supports only write access for binary values with up to 4 bytes. If you need to write a Registry value of the type Binary that contains more than 4 bytes, use the following trick: write a .reg file (using the FileSystemObject object discussed in the next chapter) with the necessary entries. Then use the Run method to shell the Registry Editor, which imports the file (as in WshShell.Run "RegEdit.exe D:\Data.reg", ,True).

Windows NT and Windows 2000 also support two other data types. REG_EXPAND_SZ contains expandable strings, and REG_MULTI_SZ contains multiple strings. Windows 2000 also has a new data type, REG_FULL_RESOURCE_DESCRIPTOR, that describes hardware resources. (It isn't supported in WSH.)

WARNING
The user shouldn't modify binary entries (or any Registry entries for that matter) because this data is coded specifically to a program. The risk of writing the wrong data into the Registry and damaging the system is simply too high. I recommend that you use Control Panel or install programs to update binary Registry values.

Accessing the Registry in WSH

You can access the Registry (keys and values) from a WSH script by using the methods of the WshShell object: RegRead, RegWrite, and RegDelete. To create a new key, you use the following statement:

WshShell.RegWrite "HKCR\.1G_BORN\", ""

Here, the RegWrite method creates the subkey .1G_BORN in the root key HKEY_CLASSES_ROOT. The first parameter of the RegWrite method passes the name of a key or value, which should be written. The name must begin with one of the six root keys or the abbreviation of that name (if there is one), as shown in Table 11-1.

Table 11-1 Names of Root Keys

Short NameLong Name
HKCU HKEY_CURRENT_USER
HKLM HKEY_LOCAL_MACHINE
HKCR HKEY_CLASSES_ROOT
HKEY_USERS
HKEY_CURRENT_CONFIG
HKEY_DYN_DATA

The subkeys must be separated with backslashes. Because the first parameter of RegWrite passes either a key name or a value name and because these are very different types of data, the system must determine whether a key or value is provided:

NOTE
Uppercase and lowercase are preserved when you create an entry. When you access the keys or values, the names are case insensitive.

The method's second parameter contains the data for the entry (key or value) whose name is defined in the first parameter. A third parameter defines the data type used for a value:

WshShell.RegWrite "HKCR\.1G_BORN\Test", "Hello, world", "REG_SZ"

The statement creates the value Test in the key HKEY_CLASSES_ROOT\.1G_BORN and writes the string "Hello, world". Thus, the value's data type, passed in the third parameter, should be set to REG_SZ. Other data types are REG_EXPAND_SZ, REG_DWORD, and REG_BINARY. Windows 95 and Windows 98 support only REG_SZ, REG_DWORD, and REG_BINARY.

NOTE
For more information on the data types of Registry entries, see the Platform SDK. (See http://msdn.microsoft.com/library/default.asp, for example.)

To read a value, you use the RegRead method with the following syntax:

sregval = WshShell.RegRead("HKCR\.1G_BORN\Test")

The method requires the name of the key, including the value's name, and returns the value. If the key doesn't exist, the method causes a run-time error. Later, I'll show you how to catch such run-time errors and how to test whether a key exists.

To delete a value or a key, you can use the RegDel method with the following syntax:

WshShell.RegDelete "HKCR\.1G_BORN\Test"

The parameter contains the path to a value or a key. If the string ends with a backslash, the parameter contains a key name. The key or value is deleted. If a key contains other subkeys or values, all entries are deleted automatically.

WARNING
If a key or value doesn't exist, the method causes a run-time error (as described shortly).

Accessing the Registry in VBScript

Let's look at a simple example that applies the methods we just discussed. The script first creates a new key and then a new value. After starting, the script asks whether the key .1G_BORN in the branch HKEY_CLASSES_ROOT should be created. If the user clicks Yes, a new key is created (or an existing key is retained). Because the key .1G_BORN isn't normally present in Windows, there should be no problem with Registry access. Another dialog box is shown before the value is written and before the value of the key is deleted.

The dialog boxes enable you to check each step. They also allow you to skip the functions to delete the value or the key. After creating the key and the value, you can examine the branch HKEY_CLASSES_ROOT using the Registry Editor (Figure 11-6).

Click to view at full size.

Figure 11-6 Registry entries

IMPORTANT
In Windows NT and Windows 2000, the script needs the right to access the key (read, write, and delete), which it has if the script is executed using the administrator account. In Windows 2000, you can create a shortcut to the script file. On the shortcut's property page, you can select the option to execute the script under another user account. An administrator can thus allow scripts to be executed at user level but grant the script administrator-level access rights.

The full VBScript implementation is shown in Listing 11-12. By the way, in this sample, the key is always present in the Registry, so your script probably won't cause a run-time error. After you do your tests, you should remove the entries from the Registry using the Registry Editor or the script itself.

Listing 11-12 Registry.vbs

'************************************************
' File:    Registry.vbs (WSH sample in VBScript) 
' Author:  (c) G. Born
'
' Accessing the Registry
'************************************************
Option Explicit

Dim Root, key, valname, valx, Response, sregval

' Initialize variables.
Root = "HKEY_CLASSES_ROOT"
key = "\.1G_BORN\"
valname = "Test"
valx ="Hello, world"

' Create WshShell object.
Dim WshShell
Set WshShell = WScript.CreateObject("WScript.Shell")

' Ask user before creating a key.
Response = MsgBox("Create key?", vbYesNo + vbQuestion _
                  + vbDefaultButton2, "Registry access")

If Response = vbYes Then    ' Selected Yes
    ' Create key/value.
    WshShell.RegWrite Root & Key, ""               
    ' Show success.
    MsgBox "Key " & Root & Key & " created" 
End If

' Ask user before creating a value.
Response = MsgBox("Create value?", vbYesNo + vbQuestion _
                  + vbDefaultButton2, "Registry access")

If Response = vbYes Then    ' Selected Yes
    ' Create string entry.
    WshShell.RegWrite Root & Key & valname, valx, "REG_SZ"
    WshShell.Popup "Value " & Root & Key & valname & " created"

    ' Try to read value.
    sregval = WshShell.RegRead(Root & Key & valname)
    MsgBox "Value " & valname & ": " & sregval
End If

' Ask before deleting value.
Response = MsgBox("Delete value?", vbYesNo + vbQuestion + _
                  vbDefaultButton2, "Registry access")
If Response = vbYes Then    ' Yes selected.
    WshShell.RegDelete Root & Key & valname
    MsgBox "Value " & valname & " deleted"
End If

' Ask before deleting the key.
Response = MsgBox("Delete key?", vbYesNo + vbQuestion + _
                  vbDefaultButton2, "Registry access")
If Response = vbYes Then    ' Yes selected.
    WshShell.RegDelete Root & Key
    MsgBox "Key " & key & " deleted"
End If

'*** End

The JScript implementation

The only difference between the VBScript and JScript implementations is that in JScript you must use a double backslash instead of a single backslash in the string because the single backslash is an escape character in JScript. The JScript implementation is shown in Listing 11-13.

Listing 11-13 Registry.js

//************************************************
// File:    Registry.js (WSH sample in JScript) 
// Author:  (c) G. Born
//
// Accessing the Registry
//************************************************

var vbOKCancel = 1;
var vbInformation = 64;
var vbCancel = 2;

// Initialize variables.
var Root = "HKEY_CLASSES_ROOT";
var key = "\\.1G_BORN\\";
var valname = "Test";
var valx ="Hello, world";
var result;

// Create WshShell object.
var WshShell = WScript.CreateObject("WScript.Shell");

// Ask user before the key is created.
result = WshShell.Popup("Create key?",
                        0,
                        "Registry access",
                        vbOKCancel + vbInformation);
if (result != vbCancel) 
{  // Use "REG_SZ" or "REG_BINARY" or "REG_BINARY" as type
    WshShell.RegWrite(Root + key + valname, valx, "REG_SZ");
    WshShell.Popup("Inserted: " + Root + key + valname);

    // Try to read value back.
    sregval = WshShell.RegRead(Root + key + valname);
    WshShell.Popup("Value: " + sregval);
}

// Ask before deleting the value.
result = WshShell.Popup("Delete value?",
                        0,
                        "Registry access",
                        vbOKCancel + vbInformation);
if (result != vbCancel) 
{    
    WshShell.RegDelete(Root + key + valname);
    WshShell.Popup("Deleted: " + Root + key + valname);
}

// Ask before deleting the key.
result = WshShell.Popup("Delete key?",
                        0,
                        "Registry access",
                        vbOKCancel + vbInformation);
if (result != vbCancel) 
{    
    WshShell.RegDelete(Root + key);
    WshShell.Popup("Deleted: " + Root + key);
}

//*** End

Run-Time Error Handling for Registry Access

The Registry.vbs and Registry.js examples don't encounter problems when reading from the Registry because they create the .1G_BORN key before they try to read it. In the real world, however, you'll run into the following trap sooner or later: if the key or value specified in a Registry read operation doesn't exist, WshShell.RegRead produces a run-time error. In VBScript, you can use the On Error Resume Next statement to catch run-time errors, and you can use the Err object's Number property to analyze the error code. The code to read a Registry key with error handling can look like this:

On Error Resume Next
' Try to read the value. If value doesn't exist, catch run-time error.
sregval = WshShell.RegRead(Root & Key & valname)
If Err.Number <> 0 Then
    MsgBox "Value doesn't exist"
    WScript.Quit     ' Terminate script.
End If

The next code listing uses this approach and tries to read a key in VBScript. The sample contains error-handling code to detect whether a key exists. To test the sample, execute the script Registry1.vbs and click No in the dialog boxes that ask whether the key or the value should be created. The required key and value aren't created, and a run-time error occurs during read access of the nonexistent value. The script handles this run-time error by displaying a message that the key doesn't exist.

The full implementation is shown in Listing 11-14.

Listing 11-14 Registry1.vbs

'************************************************
' File:    Registry1.vbs (WSH sample in VBScript) 
' Author:  (c) G. Born
'
' Accessing the Registry and catching run-time
' errors while accessing missing keys
'************************************************
Option Explicit

' Warning: Enable run-time error handling for the entire script. 
On Error Resume Next

Dim Root, key, valname, valx, Response, sregval

' Initialize variables.
Root = "HKEY_CLASSES_ROOT"
key = "\.1G_BORN\"
valname = "Test"
valx ="Hello, world"

' Create WshShell object.
Dim WshShell
Set WshShell = WScript.CreateObject("WScript.Shell")

' Ask user before creating a key.
Response = MsgBox("Create key?", vbYesNo + vbQuestion _
                  + vbDefaultButton2, "Registry access")

If Response = vbYes Then    ' Yes selected.
    ' Create key/value.
    WshShell.RegWrite Root & Key, ""   
    ' Show success.
    WshShell.Popup "Key " & Root & Key & " created"
End If

' Ask user before creating a value.
Response = MsgBox("Create value?", vbYesNo + vbQuestion _
                  + vbDefaultButton2, "Registry access")

If Response = vbYes Then    ' Yes selected.
    ' Create string entry.
    WshShell.RegWrite Root & Key & valname, valx, "REG_SZ"
    MsgBox "Value " & Root & Key & valname & " created"
    ' Get value.
    sregval = WshShell.RegRead(Root & Key & valname) 
    MsgBox "Value " & valname & ": " & sregval
Else
    ' Try to read value back. If the value doesn't exist,
    ' catch the run-time error using the Err object.
    sregval = WshShell.RegRead(Root & Key & valname)
    If Err.Number <> 0 Then
        MsgBox "Value doesn't exist"
        WScript.Quit
    Else  
        MsgBox "Value " & valname & ": " & sregval
    End If
End If

' Ask before deleting value.
Response = MsgBox("Delete value?", vbYesNo + vbQuestion + _
                  vbDefaultButton2, "Registry access")
If Response = vbYes Then    ' Yes selected.
    WshShell.RegDelete Root & Key & valname
    MsgBox "Value " & valname & " deleted"
End If

' Ask before deleting key.
Response = MsgBox("Delete key?", vbYesNo + vbQuestion + _
                  vbDefaultButton2, "Registry access")
If Response = vbYes Then    ' Yes selected.
    WshShell.RegDelete Root & Key
    MsgBox "Key " & key & " deleted"
End If

'*** End

Checking the Existence of a Key

The error handling shown above is too much work because you have to add all the error-handling code to each Registry read statement. It's much more convenient to use a function that tests for the existence of a key or value. In VBScript, you can implement such a function with just a few lines of code.

Function KeyExists(key)
    Dim key2
    On Error Resume Next
    key2 = WshShell.RegRead(key)
    If Err <> 0 Then
        KeyExists = False
    Else
        KeyExists = True
    End If
    On Error GoTo 0
End Function

You pass the expression containing the key or value name as a parameter to the function, which then tries to read the key or value. Depending on the value of the Err object, the function returns the value True or False.

This new function is shown in Listing 11-15. The program asks the user to enter the name of a key or a value. After the user closes the input dialog box, the program tests whether the key or value exists. If an entry exists, its value is shown in a dialog box (Figure 11-7, top). Otherwise, an error dialog box is shown (Figure 11-7, bottom).

Key names must end with a backslash. Using HKCR\.bmp results in a message that the key doesn't exist because the RegRead method requires the terminating slash for keys. Using HKCR\.bmp\ forces the program to read the content of the key .bmp in the branch HKEY_CLASSES_ROOT. This entry defines the .bmp file type within the Registry.

Figure 11-7 Dialog boxes for accessing a Registry key

Listing 11-15 Registry2.vbs

'************************************************
' File:    Registry2.vbs (WSH sample in VBScript) 
' Author:  (c) G. Born
'
' Accessing the Registry and using KeyExist to
' check whether a key or value exists
'************************************************
Option Explicit

Dim key1
Dim WshShell

' Get WshShell object for Registry methods.
Set WshShell = WScript.CreateObject("WScript.Shell")

' Query user for new key.
key1 = InputBox("Enter a key (for instance HKCR\.bmp\)", "Key", "HKCR\")
If KeyExists(key1) = True Then
    MsgBox "Key: " & key1 & " Value: " & WshShell.RegRead(key1)
Else
    MsgBox "Key: " & key1 & " doesn't exist"
End If

Function KeyExists(key)
    Dim key2
    On Error Resume Next
    key2 = WshShell.RegRead(key)
    If Err <> 0 Then
        KeyExists = False
    Else
        KeyExists = True
    End If
    On Error GoTo 0
End Function

'*** End

The JScript implementation

JScript doesn't have an input function, so the sample in Listing 11-16 is implemented as a .wsf file using the technique I described in Chapter 8 to retrieve user input. The function to test a Registry key's existence is implemented in JScript.

Listing 11-16 Registry2.wsf

<?xml version="1.0" encoding="ISO-8859-1"?>
<!-- 
    File:   Registry2.wsf (WSH 2)
    Author: (c) G. Born
-->

<job id="T1">
    <script language="VBScript">
    <![CDATA[
        Function WSHInputBox(Message, Title, Value)
            ' Provides an InputBox function for JScript
            ' It can be called from JScript as follows:
            ' var result = WSHInputBox("Enter a name", "Input", test);
            WSHInputBox = InputBox(Message, Title, Value)
        End Function
    ]]>
    </script>

    <script language="JScript">
    <![CDATA[
        function KeyExists(obj, key)
        {
            // Checks whether a key exists.
            try
            {
                var key2 = obj.RegRead(key);
            }
            catch (e)
            {
                if (e != 0)   // Error
                    return false;
            }
            return true;
        }
    ]]>
   </script>

   <script language="VBScript">
   <![CDATA[
       Dim key1
       Dim WSHShell

       ' Get WSHShell object for Registry methods.
       Set WSHShell = WScript.CreateObject("WScript.Shell")

       ' Query key name for new key.
       key1 = WSHInputBox("Enter a key (for instance HKCR\.bmp\) ", _
                          "Key", "HKCR\")
       If KeyExists(WSHShell, key1) = True Then
           MsgBox "Key: " & key1 & " Value: " & WSHShell.RegRead(key1)
       Else
           MsgBox "Key: " & key1 & " doesn't exist"
       End If
    ]]>
    </script>
</job>

Techniques for Registry Access in WSH

In Listing 11-16, only simple values are read from Registry keys. According to the Windows Script Host Reference, the methods to read from and write to the Registry support several data types:

You can use these types to read values, and you can use some of them to create a value, as I'll demonstrate shortly.

At this point, I should mention what happens when a new key is created. If you create a new key using the Registry Editor, the default value is undefined. Writing a new Registry key by using RegWrite sets the default value to a zero string:

WshShell.RegWrite "HKCR\.123\", ""

If you need similar behavior in a script, you have to use a trick. Don't create a new key; instead, insert a new value and a key into the Registry. Then you can delete the value, and a new key with a (Default) value set to "value not set" is left. You can create a new value, including the hosting key, by using the following statement:

' Create a value directly, forcing an empty default value.
WshShell.RegWrite "HKCR\.123\Test", "Hello, world", "REG_SZ"

This statement creates the value Test in the key HKEY_CLASSES_ROOT\.123. If the key is created when the value is created, the default value is undefined.

Using RegRead to read the values can also cause a few problems. First, if you attempt to read a default value of a given key, WSH 2 forces a run-time error if the value isn't defined. The error code is the same as for a nonexistent key. In a moment, I'll show a sample program, RegAccessTest.vbs, that allows you to test this behavior. (See Listing 11-19.)

Dealing with binary values

When you use WSH to write REG_BINARY values in the Registry, you're limited to 4-byte entries because the parameter passed for the value must be a long integer. When you read values containing REG_BINARY, you use RegRead to return a byte array, which can be extracted within a For Each loop. Listing 11-17 writes a REG_BINARY value and reads it back.

Listing 11-17 BinaryRead.vbs

'************************************************
' File:   BinaryRead.vbs (WSH sample in VBScript) 
' Author: (c) G. Born 
'
' Reading a binary entry from the Registry and
' displaying it
'************************************************
Option Explicit

Dim WshShell, value, entry, text

' Create WshShell object to access Registry.
Set WshShell = WScript.CreateObject("WScript.Shell")

' Add a binary value using the REG_BINARY type.
WshShell.RegWrite "HKCR\.123\Binary", 123456789, "REG_BINARY"

' Read value.
value = WshShell.RegRead("HKCR\.123\Binary")

' Extract array values.
text = "Entry is of type " & typename(value) & vbCrLf
For Each entry In value
    text = text & entry & vbCrLf
Next

MsgBox text, vbOKOnly, "Read REG_BINARY entry"

WshShell.RegDelete "HKCR\.123\"

WScript.Echo "Key removed"

'*** End

Dealing with REG_MULTI_SZ values

WSH doesn't support REG_MULTI_SZ values. You can create a value of type REG_MULTI_SZ, but you can't set a true multistring entry. You can read a REG_MULTI_SZ value by using the RegRead method, which returns an array containing strings. To show these values in a dialog box, the script must process this array. Listing 11-18 reads a REG_MULTI_SZ value (using a predefined key from the Registry).

Listing 11-18 MultiStrRead.vbs

'***************************************************
' File:   MultiStrRead.vbs (WSH sample in VBScript) 
' Author: (c) G. Born 
'
' Reading a REG_MULTI_SZ entry from the Registry 
' and displaying it
'***************************************************
Option Explicit

Dim WshShell, val, entry, text

' Create WshShell object to access Registry.
Set WshShell = WScript.CreateObject("WScript.Shell")

' Read a REG_MULTI_SZ  entry.
val = WshShell.RegRead _
    ("HKLM\System\CurrentControlSet\Control\ServiceGroupOrder\list")

' Process values.
text = "Entry is of type " & typename(val) & vbCrLf
For Each entry In val
    text = text & entry & vbCrLf
Next

MsgBox text, vbOKOnly, "Read REG_MULTI_SZ entry"

'*** End

Accessing Registry values and keys

The VBScript program in Listing 11-19 accesses Registry values and keys in WSH. The script creates a new key called HKCR\.123 and adds some values of type REG_SZ, REG_DWORD, REG_BINARY, and REG_EXPAND_SZ. In each step, the values added to the Registry are shown in a dialog box. You can thus inspect the Registry using the Registry Editor. Next, the script creates a new value in the key HKCR\.123 in one step, causing the (Default) value of the key to be undefined. The RegRead method causes a run-time error. You can use this script to test the behavior of the methods for accessing the Registry.

Listing 11-19 RegAccessTest.vbs

'******************************************************
' File:   RegAccessTest.vbs (WSH sample in VBScript) 
' Author: (c) G. Born
'
' Accessing different types in the Registry (in WSH 2) 
'******************************************************
Option Explicit

Dim WshShell
Dim value

' Create WshShell object to access the Registry.
Set WshShell = WScript.CreateObject("WScript.Shell")

' First, create a simple key. Note that the 
' default value is set implicitly to ""
WshShell.RegWrite "HKCR\.123\", ""
value = WshShell.RegRead("HKCR\.123\")
WScript.Echo "Key HKCR\.123 written ..." & vbCrLf & _
             "Default value: ", value

' Now set the default value to "Default value".
WshShell.RegWrite "HKCR\.123\", "Default value"
value = WshShell.RegRead("HKCR\.123\")
WScript.Echo "Key HKCR\.123 default value written ..." & vbCrLf & _
             "Default value: ", value

' Now add a Text value using the REG_SZ type.
WshShell.RegWrite "HKCR\.123\Test", "Hello, world", "REG_SZ"
value = WshShell.RegRead("HKCR\.123\Test")
WScript.Echo "Key HKCR\.123 value 'Test' written ..." & vbCrLf & _
             "Test value: ", value

' Now add a Number value using the REG_DWORD type.
WshShell.RegWrite "HKCR\.123\Number", 8000, "REG_DWORD"
value = WshShell.RegRead("HKCR\.123\Number")
WScript.Echo "Key HKCR\.123 value 'Number' written ..." & vbCrLf & _
             "Number value: ", value

' Now add a Binary value using the REG_BINARY type.
WshShell.RegWrite "HKCR\.123\Binary", 123456789, "REG_BINARY"
' We can't read a binary value; RegRead returns an error value
' that can't be used.
'value = WshShell.RegRead ("HKCR\.123\Binary")
value = "not readable"
WScript.Echo "Key HKCR\.123 value 'Binary' written ..." & vbCrLf & _
             "Binary value: ", value

' Now add the expandable TextEx value using the REG_EXPAND_SZ type.
WshShell.RegWrite "HKCR\.123\TestEx", "%WINDIR%\Notepad.exe", _
                  "REG_EXPAND_SZ"
value = WshShell.RegRead("HKCR\.123\TestEx")
WScript.Echo "Key HKCR\.123 value 'TestEx' written ..." & vbCrLf & _
             "Test value: ", value 

WshShell.RegDelete "HKCR\.123\"
WScript.Echo "Key HKCR\.123 removed ..."

' ### Now make a second attempt. ###
' Create a value directly; this forces an empty Default value.
WshShell.RegWrite "HKCR\.123\Test", "Hello, world", "REG_SZ"
value = WshShell.RegRead("HKCR\.123\Test")
WScript.Echo "Key HKCR\.123 value 'Test' written ..." & vbCrLf & _
             "Test value: ", value 

' Try to read the default value. This causes a run-time error.
On Error Resume Next
value = WshShell.RegRead("HKCR\.123\")
If Err <> 0 Then
    WScript.Echo "Error ", Err.Number, Err.Description
Else
    WScript.Echo "Key HKCR\.123 read ..." & vbCrLf & _
                 "Default value: ", value
End If

On Error GoTo 0

WshShell.RegDelete "HKCR\.123\"
WScript.Echo "Key HKCR\.123 removed ..."

'*** End

Enumerating Registry Keys and Values

You can't enumerate values and subkeys within a given key in WSH. But several API functions are available that let you retrieve the necessary information. Microsoft offers a small module named Regobj.dll that allows a script to access API functions via an object model.

NOTE
Regobj.dll is available on the companion CD in the \Tools\RegObj folder. An accompanying Microsoft Word file briefly introduces the RegObj object model. The object model not only lets you enumerate a key but also lets you access the Registry of remote computers. You can also download Regobj.dll free from Microsoft's Web site (http://www.microsoft.com) if you have a valid Microsoft Visual Basic license. (Search on Regobj.dll.)

You can retrieve an instance of the object by using the following command:

Set objReg = WScript.CreateObject("RegObj.Registry")

If the DLL is registered on the system, a reference to the object instance is associated with the object variable objReg. The script can then use the properties and methods of the RegObj object. The RegKeyFromString method retrieves the content of a given key. This method requests the key name as a parameter:

Set RootKey = objReg.RegKeyFromString(key1)

You can then use a simple For Each…In loop to process the collection returned from the method. To enumerate all subkeys of a given key, use the following sequence:

For Each oVal In RootKey.SubKeys ' Loop through collection.
    name = name & oVal.Name & vbCrLf
Next

The SubKeys collection contains all keys of a given parent key. The Name property of a collection item contains the name of the key. If you want to read the values of a given key, you must use the following loop:

For Each oVal In RootKey.Values  ' Loop through collection.
    name = name & oVal.Name & vbTab & oVal.Value & vbCrLf
Next

The loop uses the Values property, which returns a collection containing all values of a given key. You can then use the Name property of a given item to retrieve the value name. The Value property of an item returns the value itself.

IMPORTANT
Windows 2000 uses a new value type called REG_FULL_RESOURCE_DESCRIPTOR, which Regobj.dll doesn't support. If this value is retrieved, a run-time error occurs.

The VBScript sample in Listing 11-20 enumerates the subkeys in a specified key. In a second step, it enumerates the values of the key.

Listing 11-20 Enumerate.vbs

'*************************************************
' File:   Enumerate.vbs (WSH sample in VBScript) 
' Author: (c) G. Born
'
' Using Regobj.dll to enumerate the subkeys in a
' Registry key and then enumerating the values
' of the key 
'*************************************************
Option Explicit

' Define the two keys.
Const key1 = "\HKEY_Classes_Root\Folder"
Const key2 = _
    "\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run"

Dim objReg
Dim RootKey
Dim name
Dim oVal

' Create object reference to Regobj.dll.
Set objReg = WScript.CreateObject("RegObj.Registry")

' Retrieve property of parent key object.
Set RootKey = objReg.RegKeyFromString(key1)

name = "Registry key " & key1 & " entries are" & vbCrLf

For Each oVal In RootKey.SubKeys ' Loop through collection.
    name = name & oVal.Name & vbCrLf
Next

MsgBox name, vbOKOnly, "WSH Registry Enumerate sample"

' Retrieve property of key object.
Set RootKey = objReg.RegKeyFromString(key2)

name = "Registry key " & key2 & " value entries are" & vbCrLf

For Each oVal In RootKey.Values  ' Loop through collection.
    name = name & oVal.Name & vbTab & oVal.Value & vbCrLf
Next

MsgBox name, vbOKOnly, "WSH Registry Enumerate sample"   

'*** End

NOTE
To use the Enumerate.vbs file, you must register Regobj.dll using the Regsvr32.exe program. (See Chapter 2.) Regobj.dll is in the \Tools\RegObj folder on the companion CD.

Accessing the Registry Remotely

WSH doesn't support remote Registry access. Fortunately, the Regobj.dll file provides this feature. After retrieving an object instance to RegObj, you can use the following statement to obtain an object instance of the remote Registry object:

Set objRemote = objReg.RemoteRegistry(host)

The RemoteRegistry method has one parameter, which contains the host name (such as \\Rom). You can then use the object variable objRemote to call other methods:

Set RootKey = objRemote.RegKeyFromString(key1)

You call the RegKeyFromString method in the same way that you do for local Registry access. The only difference is that you use the objRemote object variable, which contains an instance of the remote object. Listing 11-21 contains the same functionality as the sample in Listing 11-20. The only difference is that it connects to the remote Registry of the host \\Wien. To execute the script in your environment, you must adapt the settings for the host.

Listing 11-21 RemoteReg.vbs

'************************************************
' File:   RemoteReg.vbs (WSH sample in VBScript) 
' Author: (c) G. Born
'
' Using Regobj.dll to enumerate the subkeys in a
' Registry key and then enumerating the values
' of the key
'************************************************
Option Explicit

' Define the two keys.
Const key1 = "\HKEY_CLASSES_ROOT\Folder"
Const key2 = _
    "\HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\CurrentVersion\Run"
Const host = "\\Wien"

Dim objReg
Dim RootKey, objRemote
Dim name
Dim oVal

' Create object reference to Regobj.dll.
Set objReg = WScript.CreateObject("RegObj.Registry")

' ### Connect to remote Registry. ###
Set objRemote = objReg.RemoteRegistry(host)

' Retrieve property of parent key object.
Set RootKey = objRemote.RegKeyFromString(key1)

name = "Registry key " & key1 & " entries on " & _
       host & " are" & vbCrLf

For Each oVal In RootKey.SubKeys ' Loop through collection.
    name = name & oVal.Name & vbCrLf
Next

MsgBox name, vbOKOnly, "WSH Registry Enumerate sample"

' Retrieve property of key object.
Set RootKey = objRemote.RegKeyFromString(key2)

name = "Registry key " & key2 & " value entries on" & _
       host & " are" & vbCrLf

For Each oVal In RootKey.Values  ' Loop through collection.
    name = name & oVal.Name & vbTab & oVal.Value & vbCrLf
Next

MsgBox name, vbOKOnly, "WSH Registry Enumerate sample"   

'*** End

NOTE
Accessing the Registry on a remote machine requires that the machine supports remote Registry access. In Windows 2000, remote Registry access is part of the operating system, but in Windows 95 and Windows 98 you must install the optional Remote Registry service. If the script raises the run-time error "An unexpected error has been returned from the registration database," the remote machine isn't reachable (because of either a missing connection or a missing service). Also, if the script doesn't have sufficient user privileges, a run-time error reporting that the key isn't available occurs. In this case, try to execute the script with administrator privileges.

Changing the Windows 98 Installation Path

During the setup process, Windows 98 stores the path to the source that contains the installation files in the Registry. The path is located in the branch HKEY_LOCAL_MACHINE in the following key:

\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup

This key contains the value SourcePath, which stores the installation path. If you want to add a Windows component to the system, the Setup program retrieves the library files from the source specified in this path. If the path isn't valid (because the source is now on a different drive), Windows asks you for a new path. If the operating system was installed from a CD, you're asked to put the CD in the drive.

Some users are unnerved by this behavior and copy the Windows 98 CD content to a local folder on the hard disk in the hope that Windows will use these files during the next installation. Unfortunately, Windows continues to request the CD. A similar thing happens if you install a new hard disk or a new ZIP drive because Windows assigns a new drive letter for the CD. When you try to reinstall Windows components, an error message appears because the source can't be found on the requested drive.

OEM systems often keep the installation files in a folder on the local hard disk. If you delete this folder to free up space, Setup reports an error when you try to add Windows components.

NOTE
These scenarios force you to enter the installation path in a dialog box each time you add a Windows component because Windows doesn't store the path used in the last session.

If you run into this sort of trouble, you might want to use the script shown in Listing 11-22. This program reads the installation path contained in the Registry. The user can overwrite this path in an input dialog box (Figure 11-8).

Click to view at full size.

Figure 11-8 Dialog box for reading the installation path

After the user closes the dialog box, the new path is written to the Registry. During the next setup process, Windows uses the new path to retrieve the library files.

Listing 11-22 SetInstPath.vbs

'***************************************************
' File:    SetInstPath.vbs (WSH sample in VBScript) 
' Author:  (c) G. Born
'
' Setting the path for locating the Windows 
' installation CD (Use this at your own risk!)
'***************************************************
Option Explicit

Dim WshShell
Dim Root, key, valname, valx
Dim text, title

' Variables for Registry access.
Root = "HKEY_LOCAL_MACHINE"
key = "\SOFTWARE\Microsoft\Windows\CurrentVersion\Setup\"
valname = "SourcePath"
valx =""

text = "Change installation path" & vbCrLf & vbCrLf & _
       "Change the path for installation files."
title = "WSH sample - by G. Born "

' Create WshShell object. 
Set WshShell = WScript.CreateObject("WScript.Shell")

' Get installation path from Registry.
valx = WshShell.RegRead(Root & key & valname)

' Allow user to change the path.
valx = InputBox(text, title, valx)

If valx <> "" Then   ' Test for empty string.
    WshShell.RegWrite Root & Key & valname, valx, "REG_SZ"
End If
   
'*** End

Hiding the Last User Name at Logon

During Windows logon, the name of the last user is shown in the logon dialog box. Most administrators prefer to hide this name because unauthorized users might use it to enter the system. Both the Tweak UI utility and the System Policy Editor that ship with Windows support a function to show or hide the last user name in the logon dialog box. Windows 95 and Windows 98 store this information in the Registry in the branch HKEY_LOCAL_MACHINE in the following key:

\SOFTWARE\Microsoft\Windows\CurrentVersion\Winlogon

If the key contains the value DontDisplayLastUserName and if this value is set to 0x0000001, the last user name is hidden in the logon dialog box. (See Inside the Microsoft Windows 98 Registry.)

The JScript program shown in Listing 11-23 hides the last user name. When the script starts, you see a dialog box asking whether the last user name should be shown or hidden.

Listing 11-23 HideUserName.js

//**************************************************
// File:    HideUserName.js (WSH sample in JScript) 
// Author:  (c) G. Born
//
// Showing or hiding the last user's logon name
//**************************************************

var vbYesNo = 4;
var vbOK = 0
var vbInformation = 64;
var vbCancel = 2;
var vbYes = 6

// Initialize variables.
var Root = "HKEY_LOCAL_MACHINE";
var key = "\\SOFTWARE\\Microsoft\\Windows\\CurrentVersion\\Winlogon\\";
var valname = "DontDisplayLastUserName";
var result;

var WshShell = WScript.CreateObject("WScript.Shell");

result = WshShell.Popup(
    "hide last user in logon dialog box?\n\n\r" +
    "Hide last user's name in logon dialog box",
    0,
    "Windows 98 - powered by G. Born",
    vbYesNo + vbInformation );

if (result == vbYes) 
{
    WshShell.RegWrite(Root + key + valname, "1", "REG_SZ");
    WshShell.Popup("Show user name disabled" +
                   "\n\n\rHide last user's logon name",
                   0,
                   "http://www.borncity.de",
                   vbOK);
} 
else
{
    WshShell.RegWrite(Root + key + valname, "1", "REG_SZ");
    WshShell.RegDelete(Root + key + valname);
    WshShell.Popup("Enable user name" +
                   "\n\n\rShow last user's logon name", 
                   0,
                   "http://www.borncity.de",
                   vbOK); 
}

//*** End

Retrieving the Workgroup Name in Windows 98

If your machine is part of a workgroup network, you might want to retrieve information about which workgroup the current user belongs to. Unfortunately, WSH doesn't offer any methods for retrieving workgroup information. But you can use the methods of the WshNetwork object to retrieve networking information such as user name, domain name, and computer name.

Let's look at a sample in Windows 98. Information about the domain is kept in the Windows 98 Registry in the branch HKEY_LOCAL_MACHINE in the following key:

\SYSTEM\CurrentControlSet\Services\VxD\VNETSUP

The value Workgroup contains the workgroup name as a string, which you can easily retrieve and display (Figure 11-9).

Click to view at full size.

Figure 11-9 Dialog box showing networking information (in Windows 98)

The VBScript sample in Listing 11-24 is fairly simple. It uses the sample file Network.vbs as a foundation and adds the KeyExists function (which we used earlier). The sample retrieves the information provided by the WshNetwork properties. In a second step, it looks for the Workgroup value in the Registry. If the value exists, the name is inserted into a string. The results are then displayed in a dialog box.

Listing 11-24 GetDomain.vbs

'*************************************************
' File:    GetDomain.vbs (WSH sample in VBScript) 
' Author:  (c) G. Born
'
' Showing the user name, domain name, computer 
' name, and workgroup name
'*************************************************
Option Explicit 

Dim Text, Title
Dim WshNetwork         ' Object variable
Dim key1
Dim WshShell

' Get WshShell object for Registry access.
Set WshShell = WScript.CreateObject("WScript.Shell")

Text = "Networking information" & vbCrLf & vbCrLf
Title = "WSH sample - by G. Born"

' Create a new WshNetwork object to access network properties.
Set WshNetwork = WScript.CreateObject("WScript.Network")

Text = Text & "Computer name : " & WshNetworK.ComputerName & vbCrLf
Text = Text & "Domain : " & WshNetworK.UserDomain & vbCrLf
Text = Text & "User name : " & WshNetworK.UserName & vbCrLf
Text = Text & "Workgroup name : "

' Check whether Workgroup entry (a value) exists.
key1 = "HKEY_LOCAL_MACHINE\System\CurrentControlSet" & _
       "\Services\VxD\VNETSUP\Workgroup"
If (KeyExists(key1) = True) Then
    Text = Text & WshShell.RegRead(key1)
End If
   
Text = Text & vbCrLf

MsgBox Text, vbOKOnly + vbInformation, Title

'##############################################
' Helper function tests whether the key exists.
'##############################################
Function KeyExists(key)
    Dim key2
    On Error Resume Next
    key2 = WshShell.RegRead(key)
    If Err <> 0 Then
        KeyExists = False
    Else
        KeyExists = True
    End If
    On Error GoTo 0
End Function

'*** End

The JScript implementation

Listing 11-25 shows how to retrieve a user name, domain name, computer name, and workgroup name using JScript. The KeyExists function is the JScript implementation of the VBScript KeyExists function.

Listing 11-25 GetDomain.js

//*************************************************
// File:    GetDomain.js (WSH sample in JScript 5) 
// Author:  (c) G. Born
//
// Showing the user name, domain name, computer 
// name, and workgroup name
//*************************************************

// Get WshShell object for Registry access.
var WshShell = WScript.CreateObject("WScript.Shell");

var Text = "Networking information\n\n";
var Title = "WSH sample - by G. Born";

// Create a new WshNetwork object to access network properties.
var WshNetwork = WScript.CreateObject("WScript.Network");

Text = Text + "Computer name : " + WshNetwork.ComputerName + "\n";
Text = Text + "Domain : " + WshNetwork.UserDomain + "\n";
Text = Text + "User name : " + WshNetwork.UserName + "\n";
Text = Text + "Workgroup name : ";

// Check whether Workgroup entry (a value) exists.
key1 = "HKEY_LOCAL_MACHINE\\System\\CurrentControlSet\\" +
       "Services\\VxD\\VNETSUP\\Workgroup";
if (KeyExists(WshShell, key1))
    Text = Text + WshShell.RegRead(key1)

Text = Text + "\n";

WScript.Echo(Text);

//##############################################
// Helper function tests whether the key exists.
//##############################################
function KeyExists(obj, key)
{
    var key2;
    try 
    { 
        key2 = obj.RegRead(key) 
    }
    catch(e)
    {
        if (e != 0)
            return false;
    }
    return true;  // Key found.
}

//*** End


at:  http://www.freeweb.hu/wsh2/ch11e.html

[ Aug. 2007  pcd ]