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, &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 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, she has written several books, including Making Win 32 Applications Mobile.
# # #