Cloning a registry branch

Skip to main content (skip navigation menu)
Letterhead logo






Cloning a registry branch

 

RegCloneBranch() is a function that copies all values and all sub-keys below a given key to a new key. It copies a complete branch from the Microsoft Windows registry, hence. RegCloneBranch() should perhaps be called RegCloneKey() —which is the name I chose initially, but I found that this name looks too much like the Win32 API function RegCloseKey().

Why?

I implemented this function because I needed it (of course) and because a search on the Internet for such a function turned up empty. Three applications that I have for RegCloneBranch() are:

Usage

The definition of RegCloneBranch() is:

        long RegCloneBranch(HKEY hkeyDestRoot, HKEY hkeySrcRoot);

Call RegCloneBranch() with the source and the destination keys (HKEY types). Both of these keys must already exist. If the destination key already contains data, RegCloneBranch() overwrites it if the source key contains the same value settings. However, values that exist in the destination key and that do not exist in the source key are not deleted.

Implementation

I implemented RegCloneBranch() in straight C. No C++ features are used and there are no dependencies on anything other than the Win32 API. When you have used the registry functions earlier, the implementation is probably straightforward (though lengthy).

RegCloneBranch()
#if !defined ASSERT
  #define ASSERT assert
#endif

long RegCloneBranch(HKEY hkeyDestRoot, HKEY hkeySrcRoot)
{
  long result=ERROR_SUCCESS;
  DWORD index;
  DWORD subkeys,maxkeyname,values,maxvaluename,maxdata,type;
  LPSTR lpName=NULL, lpData=NULL;

  /* get information, so that we know how much memory to allocate */
  result=RegQueryInfoKey(hkeySrcRoot,NULL,NULL,NULL,&subkeys,&maxkeyname,
                         NULL,&values,&maxvaluename,&maxdata,NULL,NULL);
  if (result!=ERROR_SUCCESS)
    return result;
  /* in Windows NT/2000/XP, the name lengths do not include the '\0' terminator */
  maxkeyname++;
  maxvaluename++;

  /* allocate buffers, one for data and one for value & class names */
  if (maxvaluename>maxkeyname)
    maxkeyname=maxvaluename;
  ASSERT(maxkeyname>0);
  lpName=malloc(maxkeyname);
  if (lpName==NULL) {
    result=ERROR_NOT_ENOUGH_MEMORY;
    goto error_exit;
  } /* if */
  if (maxdata>0) {
    lpData=malloc(maxdata);
    if (lpData==NULL) {
      result=ERROR_NOT_ENOUGH_MEMORY;
      goto error_exit;
    } /* if */
  } else {
    lpData=NULL;
  } /* if */

  /* first walk through the values */
  for (index=0; index<values; index++) {
    DWORD namesize=maxkeyname;
    DWORD datasize=maxdata;
    ASSERT(lpData!=NULL);
    result=RegEnumValue(hkeySrcRoot,index,lpName,&namesize,NULL,&type,lpData,&datasize);
    ASSERT(result!=ERROR_MORE_DATA);
    if (result!=ERROR_SUCCESS)
      goto error_exit;
    result=RegSetValueEx(hkeyDestRoot,lpName,0L,type,lpData,datasize);
    if (result!=ERROR_SUCCESS)
      goto error_exit;
  } /* for */
  /* no longer need the data block */
  if (lpData!=NULL) {
    free(lpData);
    lpData=NULL;
  } /* if */

  /* no walk through all subkeys, and recursively call this function to copy the tree */
  for (index=0; index<subkeys; index++) {
    DWORD namesize=maxkeyname;
    HKEY hkeySrc;
    HKEY hkeyDest;
    RegEnumKeyEx(hkeySrcRoot,index,lpName,&namesize,NULL,NULL,NULL,NULL);
    result=RegOpenKeyEx(hkeySrcRoot,lpName,0L,KEY_READ,&hkeySrc);
    if (result!=ERROR_SUCCESS)
      goto error_exit;
    result=RegCreateKeyEx(hkeyDestRoot,lpName,0L,NULL,REG_OPTION_NON_VOLATILE,
                          KEY_WRITE,NULL,&hkeyDest,NULL);
    if (result!=ERROR_SUCCESS)
      goto error_exit;
    RegCloneBranch(hkeyDest,hkeySrc);
    RegCloseKey(hkeySrc);
    RegCloseKey(hkeyDest);
  } /* for */

  ASSERT(lpName!=NULL);
  free(lpName);
  lpName=NULL;

  return ERROR_SUCCESS;

error_exit:
  if (lpName!=NULL)
    free(lpName);
  if (lpData!=NULL)
    free(lpData);
  return result;
}

The function RegQueryInfoKey() is very convenient, because it returns the number of values and sub-keys below a key, as well as the maximum sizes (or lengths) of names and data. This allows you to allocate precisely the right amount of memory (that you need to read each item) before seeing every item.

RegCloneBranch() has two loops. The first loop walks through all "values" of the source key and copies each into the destination key. The second loop walks through all sub-keys in the source key, creates those sub-keys below the destination key and calls itself recursively.

For good measure, I have sprinkled a few assert() macros through the source. Please don't choke over the goto statements, if you look carefully, you will see that I have used them in a way that a C++ programmer would use exception handling. It is an idiom that I use from time to time and that has never caused me any trouble.

section separator

Download

The file regclone.c contains the code snippet presented above.

section separator

LeadTOOLS Filter Drivers

I built a DirectShow filter driver, which I registered with:

regsvr32 MyFilter.ax
This creates, in my case, the following registry settings:
HKEY_CLASSES_ROOT\CLSID\{2BA4F180-8C86-48A3-A99B-91BF23DD3622}(Standard)"MyFilter-1.0: Video Filter"
  +       InprocServer32(Standard)"C:\MyPath\MyFilter.AX"
  ThreadingModel"Both"
HKEY_CLASSES_ROOT\CLSID\{083863F1-70DE-11d0-BD40-00A0C911CE86}(Standard)"ActiveMovie Filter Class Manager"
  +       Instance(Standard)no value
  +         +         {2BA4F180-8C86-48A3-A99B-91BF23DD3622}(Standard)no value
  CLSID"{2BA4F180-8C86-48A3-A99B-91BF23DD3622}"
  FilterData02 00 00 [...] 00 00 00
  FriendlyName"MyFilter-1.0: Video Filter"

This installs the MyFilter driver and marks it as a DirectShow transform filter. LeadTOOLS, however, does not work with just any DirectShow transform filter, it only considers those that have been marked as "compatible". Do this with the LeadTOOLS Filter Manager (filename "LTMMFMgr.exe"). I have marked the four bottom lines in yellow to make a point a bit later on.

The LeadTOOLS Filter Manager adds the following settings to the registry for the filter:

HKEY_CLASSES_ROOT\CLSID\{DA4E3DA0-D07D-11d0-BD50-00A0C911CE86}(Standard)"ActiveMovie Filter Categories"
  +       Instance(Standard)no value
  +         +         {E526D606-22E7-494C-B81E-AC0A94BFE603}(Standard)no value
  CLSID"{E526D606-22E7-494C-B81E-AC0A94BFE603}"
  FriendlyName"LTMM Video Processors"
  Merit0x00200000
HKEY_CLASSES_ROOT\CLSID\{E526D606-22E7-494C-B81E-AC0A94BFE603}(Standard)no value
  +       Instance(Standard)no value
  +         +         {2BA4F180-8C86-48A3-A99B-91BF23DD3622}(Standard)no value
  CLSID"{2BA4F180-8C86-48A3-A99B-91BF23DD3622}"
  FilterData02 00 00 [...] 00 00 00
  FriendlyName"MyFilter-1.0: Video Filter"

Observe how the settings of the last four lines (marked in yellow) in the registry settings table are an exact copy of the lines that the filter driver marks itself with to the ActiveMovie Filter Class Manager. That is, the LeadTOOLS Filter Manager does two things:

  1. It creates a new filter category (if it does not exist already) and this category is called "LTMM Video Processors".
  2. It adds a DirectShow filter into this category, by copying the branch from the "ActiveMovie Filter Class Manager" category.

The RegCloneBranch() function can perform this last step easily. Its convenience is that the FilterData field is a fairly large binary block, with a structure/contents unknown to me. Thus, I prefer to copy the registry branch (and this binary block) block rather than coding it hard in an installation script or a ".REG" file.