[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:

- String (REG_SZ) Contains string values. The content must be enclosed in double quotes (as in "C:\WINDOWS\EXPLORER.EXE").

- Binary (REG_BINARY) Some drivers and applications need binary code values stored with this data type. The Registry Editor displays binary data as a hexadecimal byte sequence (as in 01 03 01 00 AB 00...). The data in a binary value can be a sequence between 1 byte and 64 KB long. Binary data are stored in little endian order (with the least significant byte first).

> 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).

- DWORD (REG_DWORD) A special binary data type that always contains 32 bits. DWORD values are shown as hexadecimal and decimal values in the Registry Editor.

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 Name | Long 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:

- If the string in the first parameter ends with a backslash, the WSH run-time system assumes that a key should be written. If the key doesn't exist, it is created. If the key exists, the method terminates without further processing.

- If the string ends without a backslash (as in "HKCR\.1G_BORN\Test"), it's a value. The method interprets this as a command to write a value. In this case, the RegWrite method writes the value into the key given in the string. If the value doesn't exist, a new entry with the name and the data is created.

> 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).

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:

- REG_SZ A simple string value is written.

- REG_MULTI_SZ A value with multiple string entries is written. This data type is supported only in Windows NT and Windows 2000. WSH 2 doesn't support this value.

- REG_EXPAND_SZ An expandable string value is written. This data type is supported only in Windows NT and Windows 2000.

- REG_DWORD A DWORD value is written. The value passed to RegWrite must be an integer.

- REG_BINARY A binary data stream is written. The value passed to RegWrite must be an integer that contains the data stream.

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).

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).

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