Walking The Registry Tree

In this installment, we'll take a detailed look at the recursive function we use to walk the Registry, WalkRegTree(), initializing the tree control to depict the structure of the Registry. The parameters, in the order shown, are the handle to an open Registry key and the handle to a tree item.

BOOL CWalkReg::WalkRegTree(HKEY hKey, HTREEITEM htiParent)
{

   DWORD dwNSize;
   DWORD dwCSize;
   WCHAR wszName[MAX_PATH];
   CHAR  szMBCSName[MAX_PATH];
   WCHAR wszClass[256];
   FILETIME ft;
   TVITEM tviTreeViewItem;
   DWORD dwNumChildren = 0;
   DWORD dwNumGrandChildren = 0;
   LONG rc;
   HTREEITEM      htiChild;
   HKEY           hSubKey;

Using the handle to the key passed into the function, we query to find out whether the key has any children. We do this with the CeRegQueryInfoKey() function, passing NULLs for all parameters except dwNumChildren. This instructs the function to return only a count of the child keys of this key.

// query for count of children
rc = CeRegQueryInfoKey (hKey, NULL, NULL, 0,
                        &dwNumChildren, NULL, NULL, NULL,
                        NULL, NULL, NULL, NULL);

Here's the declaration for CeRegQueryInfoKey(), which shows the other items of information you can retrieve about the key:

LONG RegQueryInfoKey ( HKEY hKey,    //Handle to open reg key
     LPWSTR lpClass,                 //Unicode key class name
     LPDWORD lpcbClass,              //Unicode buffer size for
                                     //class name
     LPDWORD lpReserved              //unused
     LPDWORDlpcSubKeys,              //count of child keys
     LPDWORD lpcbMaxSubKeyLen,       //size of longest child key name
     LPDWORD lpcbMaxClassLen         //longest child key class name
     LPDWORD lpcValues,              //count of key values
     LPDWORD lpcbMaxValueNameLen,    //longest value name
     LPDWORD lpcbMaxValueLen,        //unused
     PDWORD lpcbSecurityDescriptor,  //unused
     PFILETIME lpftLastWriteTime     //unused
);

Passing valid addresses in any of the parameters causes the corresponding data to be returned. To get the class name for a key, you must initialize both the lpClass and lpcbClass parameters.

Now we use the returned count of child keys, dwNumChildren, to enumerate the child keys of the key passed to the function.

//Iterate child keys
for( int iChildKeyEnumIndex = 0;
     iChildKeyEnumIndex < dwNumChildren; iChildKeyEnumIndex++ )
{
      //get child key name
      dwNSize = dim(wszName);
      dwCSize = dim(wszClass);
      rc = CeRegEnumKeyEx (hKey, iChildKeyEnumIndex,
                           wszName, &dwNSize, NULL,
                           wszClass, &dwCSize, &ft);

      if(rc != ERROR_SUCCESS)
        { return FALSE; }

When we find a child key, we open it by using the name returned by CeRegEnumKeyEx() and capture the child key's handle in hSubKey.

//open child key by name, capture HKEY

rc = CeRegOpenKeyEx(hKey, wszName, 0,
                    KEY_ALL_ACCESS, &amp;hSubKey );
if( rc != ERROR_SUCCESS )
{ return FALSE; }

Now we find out whether the child key has children by passing its handle to CeRegQueryInfoKey().

// query for count of grand children
rc = CeRegQueryInfoKey (hSubKey, NULL, NULL, 0,
                        &dwNumGrandChildren, NULL, NULL, NULL,
                        NULL, NULL, NULL, NULL);

Every time we find hSubKey exists, we insert a new item in the tree control. First, we translate the key's Unicode name to multibyte format to make a caption for the tree item, and then we call our CWalkReg class member InsertTreeItem() to create and position the new tree item.

//convert the name string from WCS to MBCS
wcstombs( szMBCSName, wszName, sizeof(szMBCSName) );

// Add key to tree view.
htiChild = InsertTreeItem (htiParent, szMBCSName, 0, 
                           dwNumGrandChildren, hSubKey);

If we know that the item has no children, we tell it so by passing a 0 (FALSE) flag in the children member of the TVITEM structure and setting this new item data with the CTreeCtrl member SetItem().

if(dwNumChildren == 0)
{
   tviTreeViewItem.hItem = htiChild;
   tviTreeViewItem.mask = TVIF_CHILDREN | TVIF_HANDLE;
   tviTreeViewItem.cChildren = 0;
   m_RegTree.SetItem( &tviTreeViewItem );
}

If the subkey has children, we need to recurse to continue walking out the Registry tree.

      //if !children, return TRUE
      if( dwNumChildren != 0 )
      {
         //recurse walk tree
         WalkRegTree( hSubKey, htiChild );
         m_RegTree.Expand(htiChild, TVE_COLLAPSE );

      }//end if(dwNumChildren)

   } //end for(iterate children )  

return TRUE;
}

Initialization of the Walk Registry Tree Dialog

Well, it took a long darn time to get here, but we finally understand all the constituent parts of the Walk Registry Tree page, and it's time to initialize it. First, we initialize the RAPI subsystem with a call to RapiInit(). We call the balancing RapiUninit() in the destructor for the CWalkReg() class.

BOOL CWalkReg::OnInitDialog()
{
   CImageList      *pImageList;
   CBitmap         bitmap;
   CRapiDemoApp    *pApp;
   HKEY            hKey;
   LONG            rc;

   HRESULT hr = CeRapiInit();
   if ( hr != ERROR_SUCCESS )
      { return FALSE; }

The next few lines are commented, but included here for the following reason. As you've noticed if you've built and run the RapiDemo program over a serial link, it takes a long time to initialize the tree control that displays the registry. For this reason, we only show one of the root Registry hierarchies in RapiDemo's tree control. However, in any practical application, you'd probably want to see the whole Registry, not just the shortest tree, and most likely you'd have an Ethernet link, so the performance issue would be moot. The following commented lines show the HKEY constants you'd use if you wanted to open and traverse all of the Registry hierarchy, along with properly formatted tree control captions.

   /*

   CString             szBaseKeys[4];
   HKEY                hRootKeys[4];

   szBaseKeys[0]= _T("HKEY_CLASSES_ROOT");
   szBaseKeys[1]= _T("HKEY_CURRENT_USER");
   szBaseKeys[2]= _T("HKEY_LOCAL_MACHINE");
   szBaseKeys[3]= _T("HKEY_USERS");

   hRootKeys[0]= HKEY_CLASSES_ROOT;
   hRootKeys[1]= HKEY_CURRENT_USER;
   hRootKeys[2]= HKEY_LOCAL_MACHINE;
   hRootKeys[3]= HKEY_USERS;
*/

To show icons next to the tree control items, we must create and initialize an image list control. The parameters to Create() are the x and y dimensions of the bitmaps we'll add to the image list, the mask that is used to display the images, the number of bitmaps the image list will initially hold, and the number of bitmaps by which the image list can grow.

CPropertyPage::OnInitDialog();    // let the base class do the
                                  // default work
pImageList = new CImageList();
pImageList->Create(16, 16, ILC_COLOR, 2, 0);

Next, we load a single bitmap resource, which contains both image bitmaps. We add the bitmaps to the image list, and delete the bitmap object. Because we allocated the image list on the heap with new, we use delete to dispose of it in the destructor for this class.

bitmap.LoadBitmap(IDB_FOLDER_BITMAPS);
pImageList->Add(&bitmap, (CBitmap * )NULL);
bitmap.DeleteObject();

Next, we set the image list in the tree control. The parameters to SetImageList() are a pointer to the initialized image list object and a flag that means that these images are to be used as tree item icons.

m_RegTree.SetImageList(pImageList, TVSIL_NORMAL);

Now, we open one of the four root Registry keys, HKEY_CURRENT_USER.

rc = CeRegOpenKeyEx(HKEY_CURRENT_USER, NULL,
                    0, KEY_ALL_ACCESS, &hKey );
if( rc != ERROR_SUCCESS )
{ return FALSE; }

We get a count of the children for this key, calling CeRegQueryInfoKey().

// query for count of children
DWORD dwNumChildren;
CeRegQueryInfoKey (HKEY_CURRENT_USER,
                   NULL, NULL, 0, &dwNumChildren,
                   NULL, NULL, NULL,
                   NULL, NULL, NULL, NULL);

Now, we insert the root key in the tree view, calling the CWalkReg member InsertTreeItem(), and capture the handle to the tree item it returns.

m_rghItem = InsertTreeItem( NULL, "HKEY_CURRENT_USER", 0,
                            dwNumChildren, hKey );

If the key has children, we call WalkRegTree(), which will recursively walk out the Registry tree, adding and initializing tree control items as it goes.

   if( dwNumChildren > 0 )
   {
      WalkRegTree( hKey, m_rghItem );
   }

   return FALSE;
}

Looking Ahead

In the next article, we'll use RAPI to find databases on the remote machine. In particular, you'll see how to find and manipulate the native databases for CE integrated applications.



About the Author

Nancy Nicolaisen

Nancy Nicolaisen is a software engineer who has designed and implemented highly modular Windows CE products that include features such as full remote diagnostics, CE-side data compression, dynamically constructed user interface, automatic screen size detection, and entry time data validation. In addition to writing for Developer.com and CodeGuru, she has written several books, including Making Win 32 Applications Mobile.

Comments

  • There are no comments yet. Be the first to comment!

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Intelligent N+X Redundancy, Placement Affinities, & Future Proofing in the Virtualized Data Center Virtualization brought about the ability to simplify business continuity management in IT. Workload portability and data replication capabilities mean that physical infrastructure failures no longer need impact application services, and they can rapidly be recovered even in the event of complete site failure. However, Enterprises and Service Providers face new challenges ensuring they have enough compute …

  • Live Event Date: August 14, 2014 @ 2:00 p.m. ET / 11:00 a.m. PT Data protection has long been considered "overhead" by many organizations in the past, many chalking it up to an insurance policy or an extended warranty you may never use. The realities of today make data protection a must-have, as we live in a data driven society. The digital assets we create, share, and collaborate with others on must be managed and protected for many purposes. Check out this upcoming eSeminar and join eVault Chief Technology …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds