Windows machines depend on a rat's nest of data called the registry. This database of configuration information is a vital part of any Win32 machine. It's so important to the operation of Windows that the OS has built-in functions to manage it. Any good programming language, such as Perl, provides access to these functions through the Win32::Registry module (among other modules).
In "Searching the Registry," December 2001, InstantDoc ID 23043, I explained how you can search the registry. I didn't explain how to modify data held in the registry because adding such an explanation would have made the column too long. So, this month I discuss two types of registry modifications: copying and renaming registry keys and values.
Finding the Root Key
The Perl script RegRename.pl, which you can find in the Code Library on the Windows Scripting Solutions Web site (http://www.winscriptingsolutions.com), copies and renames keys and values. The script's Configure() subroutine, which Listing 1 shows, determines which registry subtree, or root key, to use. At callout A in Listing 1, the subroutine creates a hash that maps the different root strings (e.g., $HKEY_LOCAL_MACHINE) with their respective root keys (e.g., HKEY_LOCAL_MACHINE). For the sake of convenience, the hash also includes abbreviations of the various root key names. For example, the root key HKEY_CURRENT_CONFIG has the abbreviation HKCC.
At callout B in Listing 1, the Configure() subroutine uses a splice to populate the %Config hash, which the subroutine passes into the Configure() function as a reference. The splice accepts the resulting array from a regular expression that breaks apart the user-specified path to a key into various components: machine name, root key, and key. Next, the subroutine deletes the temp key because it's no longer needed—it was only used as a placeholder in the regular expression. Another regular expression then breaks up the key into its path and name.
The code at callout C in Listing 1 references the root name that's determined by the first regular expression to find which registry root key to use as the key to the %Roots hash. Finally, the code at callout D in Listing 1 determines whether the user passed in a value name. If so, the script copies or renames (depending on the option the user specified when he or she typed the command) the value instead of the specified key. Additionally, the code obtains the name of the new key or value.
If the user passes in a registry key that begins with a machine name, the script attempts to connect to the remote machine before opening the key. For example, if the user specifies the \\Machine\HKEY_LOCAL_MACHINE\Software\Microsoft or \\Machine\HKLM\Software\Microsoft subkey, the script connects to \\Machine's HKEY_LOCAL_MACHINE root key. To do so, the script calls its OpenRoot() subroutine.
Copying Registry Keys
All registry keys have names that identify them. For example, Microsoft places most of its software configuration data in a subkey named Microsoft. Microsoft Word stores information in the registry in the HKEY_CURRENT_USER\Software\Microsoft\Office\Word subkey. Each backslash-delimited name in a registry key is a subkey. You can create registry subkeys, such as HKEY_CURRENT_USER\Software\Roth Consulting\Articles\2002.
The OS doesn't have a copy, rename, or move function for registry keys as it does for files. Copying a registry key requires that you create a new key, then create values in the new key that are identical to the original key's values. You must also create in the new key any subkeys that are in the original key and create the subkey values in the new subkeys. The process sounds pretty easy; let's take a look at how it's done.
An easy way to copy registry keys is to use the Win32::Registry Save() function to save a specific registry key to a binary file. This technique is simple but limited. The Win32 API functions that perform the save to a file require the file to be on a hard disk physically attached to the machine that houses the registry. Therefore, to copy data from one machine's registry to another machine's registry means you would have to save the file, copy it to the other machine, connect to that machine, and use the Load() function to load the saved key. This process can be quite time-consuming and laborious, especially if you need to migrate the key to hundreds of machines. Instead of using the Save() and Load() functions, relying on external files and copying the files from machine to machine, you can use a script to perform the copying by connecting to a remote registry and creating the necessary keys and values.
Copying values. Copying keys and copying values require different techniques. Copying a value requires that you open the root key, then open the key in which the value lives. The script lets you copy values only within the same key; therefore, you need to open only one key.
After the script opens the key, the script calls the CopyValue() subroutine, which Listing 2 shows, to query the original value and copy it to the new value. The subroutine first attempts to access a value that has the name of the new value. If the subroutine can access the value, it prints an error and returns 0 (indicating that the subroutine failed).
If the new value doesn't exist, the CopyValue() subroutine calls the QueryValueEx() method to procure the original value's data type and data. Then, the subroutine calls the SetValueEx() method to create the new value and set it with the data type and value data.
As you can see, copying a key's values is relatively simple. Copying keys, however, is a little bit more difficult.
Copying keys. Copying a registry key is a bit involved because, in addition to copying the key, you must copy the values within the key and you must repeat the process for each subkey (and its values and subkeys). The script first opens the root key and the specified source key (as it does when copying values). Next, the script tries to open the new key to check whether the new key already exists. If the key doesn't yet exist, the script creates it and passes both the source and new keys into the CopyKey() subroutine, which Listing 3 shows.
The CopyKey() subroutine accomplishes several tasks. It calls the GetValues() method to collect all the key's values. The method populates a hash with value names, data types, and value data. The subroutine then collects a list of subkeys found in the key.
For each of the key's values, the CopyKey() subroutine creates a new and identical value in the new key. Next, for each subkey, the subroutine opens the subkey, creates a new subkey in the copied key, then reiteratively calls the CopyKey() subroutine. In this way, the script creates a copy of the original key that contains all values and subkeys.
Renaming keys and values is a tad more complicated than copying them because you need to first copy the key or value, then delete the original. Ideally, Win32 would provide a function that just renames a key or value, but for now, the copy and delete method must suffice.
I've already discussed how to copy values and keys. To delete the original values and keys, the script uses the DeleteValue() method and DeleteKeyTree() subroutine, respectively. Note that the script doesn't call this method and this subroutine if the user passes a -c flag to the script to specify that he or she wants to copy the key or value.
The DeleteKeyTree() subroutine deletes a registry key and all its subkeys. The script uses this subroutine to remove the original key if it's been successfully copied and if the user didn't pass the -c flag to the script. The script also calls the subroutine if the copy of the original key fails. In this case, the script uses the subroutine to clean up any new key that the code might have created before the copy failed.
Using the Script
Using RegRename.pl is easy. To rename a registry key, you just pass in the source key name and the new name. For example, if you use the command
perl RegRename.pl HKLM\Software\ActiveState ActiveState.Renamed
the script renames the ActiveState key in the HKEY_LOCAL_MACHINE root key to ActiveState.Renamed. To rename that registry key on a remote machine named Server, you would use the command
perl RegRename.pl \\Server\HKLM\Software ActiveState ActiveState.Renamed
To rename a key value, you specify the name of the value. For example, if you use the command
perl RegRename.pl HKLM\SYSTEM\CurrentControlSet Control CurrentUser OldCurrentUser
the script renames the value in the HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Control key from CurrentUser to OldCurrentUser.
Any of these commands will copy (instead of rename) the key or value if you pass in the /c or —c option. For example, if you use the command
perl RegRename.pl /c HKLM\Software\ActiveState ActiveState.Renamed
the script creates the Active.State.Renamed key and copies the contents of the ActiveState key into the Active.State.Renamed key.
Although the idea of copying or renaming registry keys and values is pretty simple, its implementation can be quite complex. RegRename.pl is a great way to get started with serious editing of the registry. Adding support to copy security permissions from each key to its new clone would be an interesting exercise. Additional modifications could enable the user to specify a full path for the copy, which could be on a remote machine. These capabilities could be of great use to network administrators who need to copy registry data from one server to another.