November 21, 2014
Hot Topics:

Accessing And Enumerating Registry Keys

  • November 20, 2003
  • By Nancy Nicolaisen
  • Send Email »
  • More Articles »

Accessing And Enumerating Existing Keys

There are two ways of retrieving the information stored in registry keys: You can read the keys individually or you can enumerate them, looping until a particular key's supply of subkeys is exhausted. You can see examples of both methods in the sample code from our last installment, RegDemo.cpp. Notice how we initialize the dialog in HkeyLocalSubKeysDlg(). First, take a look at the call to RegOpenKeyEx():

case WM_INITDIALOG:
//open key to gain access to subkeys
rc = RegOpenKeyEx(
        HKEY_LOCAL_MACHINE, 
        TEXT ("\\"), 
        0,
        KEY_ENUMERATE_SUB_KEYS, 
        &hKeyN2);

The parameters, in the order shown, are the handle to the parent key, the name of the key we want to open, the requested access mode to the key we are opening, and the address of the variable to receive the returned handle. As you saw earlier, it isn't strictly necessary to open the HKEY_LOCAL_MACHINE root key. Notice two things here, however: In the second parameter, we set an empty subkey name; and in the next parameter, we set the access mode KEY_SET_VALUE. The value of the access mode determines what you can do with and to the opened key in subsequent manipulations using the returned handle. Here is a list of key access modes and their meanings.

Figure 7—Key Access Modes

Access Mode Constant Effect of Setting this Access Mode
KEY_ALL_ACCESS Equivalent to
KEY_QUERY_VALUE
| KEY_ENUMERATE_SUB_KEYS
| KEY_NOTIFY
| KEY_CREATE_SUB_KEY
| KEY_CREATE_LINK
| KEY_SET_VALUE
KEY_CREATE_LINK Allows creation of a symbolic link
KEY_CREATE_SUB_KEY Allows creation of subkeys
KEY_ENUMERATE_SUB_KEYS Allows enumeration of subkeys
KEY_EXECUTE Allows read access
KEY_NOTIFY Allows change notification
KEY_QUERY_VALUE Allows query of subkey data
KEY_READ Equivalent to
KEY_QUERY_VALUE
| KEY_ENUMERATE_SUB_KEYS
| KEY_NOTIFY
KEY_SET_VALUE Allows write of subkey data
KEY_WRITE Equivalent to
KEY_SET_VALUE
| KEY_CREATE_SUB_KEY

As a matter of defensive programming, it's best to set an access mode that allows no more than the capability required to accomplish what you intend.

If we manage to open the root key successfully, we set up to enumerate its sub keys. First, recall that at the entry to the HkeyLocalSubKeysDlg() function, we initialize the variable i:

INT i = 0, rc;

Next, we check the return from the key open call to make sure we don't use an invalid handle in the subsequent calls.

if (rc == ERROR_SUCCESS)
{

We initialize the parameters that give the size of the arrays for the key name and the key's class using the dim macro we defined at the top of this source file. Included below is the definition of dim:

//a handy macro for finding array dimensions
#define dim(x) (sizeof(x) / sizeof(x[0]))

  dwNSize = dim(szName);
  dwCSize = dim(szClass);

Now, we begin the enumeration at the key with an index of 0:

//set up enumeration of the key's subkeys
rc = RegEnumKeyEx (HKEY_LOCAL_MACHINE, i, szName, &dwNSize, NULL,
                   szClass, &dwCSize, NULL);

The parameters to RegEnumKeyEx(), in the order shown, are the handle to the key we are enumerating, the key's index, an array of TCHARS to hold the enumerated key's name, the size of this array in characters, including the terminal null, a NULL placeholder, an array of TCHARS to hold the enumerated key's class name, the size of the class buffer, and a final NULL place holder. Before we go on, note the usefulness of the dim macro here. You can't use sizeof() to set the buffer sizes in this call, because that returns the size in bytes, which is not the same as the size in characters when strings are in Unicode.

Porting Tip: If registry access code shows symptoms of memory overwrites, double check function calls that have buffer size parameters in character counts. Make sure sizes are expressed as Unicode character counts that include a terminal null.

As we continue the enumeration, we retrieve key names but ignore their class name information. We do this by setting the class name buffer and its size to NULL.

while (rc == ERROR_SUCCESS)
{
  ListBox_InsertString(hWndList, i, szName);
  dwNSize = dim(szName);
  rc = RegEnumKeyEx (HKEY_LOCAL_MACHINE, ++i, szName,
                     &dwNSize, NULL, NULL, 0, NULL);
  }

When we are done enumerating the subkeys, we close the handle we opened above.

  //clean up now
  RegCloseKey( hKeyN2 );
  }    //end if( ERROR_SUCCESS )

Adding Values to Keys

In order to add values to a key, you must open it with at least KEY_SET_VALUE access permissions:

case IDM_ADD_SINGLE_VALUE:
  //open the key to gain access to the value
  rc = RegOpenKeyEx (HKEY_LOCAL_MACHINE,
                     TEXT ("Software\\n2"), 0,
                     KEY_SET_VALUE, &hKeyN2);

If all goes well, you are presented with great flexibility in regards to what gets stored in the value. Here are the most useful key types supported by Windows CE:

Figure 8—Windows CE Registry Key Data Types

Registry Key Data Types Usage
REG_BINARY Byte string data
REG_DWORD A 32-bit number
REG_DWORD_LITTLE_ENDIAN A 32-bit number in little-endian format. Same as REG_DWORD
REG_DWORD_BIG_ENDIAN A 32-bit number in big-endian format
REG_MULTI_SZ An array of null-terminated strings, terminated by two null characters
REG_SZ A null-terminated string

You should use the smallest registry type that will accommodate the data you plan to store. In this case, we add a single, named value of type REG_DWORD. At the top of the function, we initialized the DWORD dwRegVal in its declaration.

DWORD dwRegVal = 0x1234;

  //set a name and value under the open key 
  rc = RegSetValueEx (hKeyN2, TEXT("OneDWORD"), 0,
                      REG_DWORD , (LPBYTE)&dwRegVal,
                      sizeof(dwRegVal));

The parameters to RegSetValueEx (), in the order shown, are the handle to the key receiving the value, the name of the value, the type, the address of the first byte of the buffer where the data is stored, and the size of the data in bytes. Notice that we use sizeof() to set the buffer size in this case. This is a good practice, because if the data in this key were one of string types, sizeof() would automatically account for the space taken up by a NULL terminator.

Finally, skipping over the message box code, we close the key.

//always clean up after ourselves
RegCloseKey( hKeyN2 );
break;

What if you have several items of registry data under a key? You could add a named value for each item, but that approach would use a lot of space to store the value names and require a registry access to get or set each value. If your data are all strings, you can use the REG_MULTI_SZ type, and combine all of the strings into a single named value. If the data are of mixed type, consider aggregating them and storing them in a single binary valued key. Your application can retrieve the array in a single registry access and parse them much more quickly than if they were retrieved individually. Here is how we set a binary array as a registry value in RegDemo:

//set the binary value
rc = RegSetValueEx (hKeyN2, TEXT ("Aggregate"), 0,
                    REG_BINARY , (PBYTE)pBuff,
                    LocalSize( (HLOCAL)pBuff ));

The Buffer identified by pBuff was allocated by LocalAlloc(), so the most reliable way of getting its size is with LocalSize(). The data is written to the registry as a string of bytes, which exactly preserves the order of the bytes as they were written. This means that if your application stores a mixture of types (int, float, char, and so forth) in the byte array, you'll have to take care to read the items out of the string using pointers that are properly cast to the types of data being transferred.





Page 1 of 2



Comment and Contribute

 


(Maximum characters: 1200). You have characters left.

 

 


Enterprise Development Update

Don't miss an article. Subscribe to our newsletter below.

Sitemap | Contact Us

Rocket Fuel