There are several ways to export the registry from a Windows CE device including the Remote Registry Editor and third-party registry editors. The Remote Registry Editor requires a connection to the device and third-party registry editors typically require a display. I don’t always have a connection to the device or a display so I thought it would be interesting to enumerate the registry to export the registry to the debug port where I can capture it to a file.
I set out to figure this out just based on the documentation alone which turned out to be more of a challenge than I expected. Part of my problem was that I assumed that similar functions would work in a similar way, but I was wrong. Looking at the registry functions the function RegEnumKeyEx() seems like a likely candidate for enumerating the registry keys, and it is. But it behaves much different from how functions like RegQueryValueEx() work. I have given an example of using RegQueryValueEx() in Windows CE: Reading a String from the Registry where I show how to determine the length of a string value rather than just allocating a huge buffer.
RegEnumKeyEx() doesn’t provide a method for getting the length of the subkey name. This little quirk had me stumped for a little while until I found RegQueryInfoKey() which provides all of the information that I needed including the maximum length of the subkey name, value name, and value data.
So the first step is to enumerate the subkeys, which I did in EnumerateRegistry(). EnumerateRegistry() takes three parameters:
·         HKEY hKey – a handle to a registry key
·         TCHAR *BaseName – a string representing the open key name, including the full path. This will be used to output the key name in the output registry file.
·         TCHAR *RegKeyName – a string passed to RegOpenKeyEx to open the next key.
The function will open the next key, get information about it using RegQueryInfoKey() and then enumerate the subkeys and values. This function doesn’t handle the values, I will show that later. This function does use recursion to descend the registry. I am not convinced that recursion is necessarily a good thing in an embedded system, but I am using this in a very small application.
void EmumerateRegistry( HKEY hKey, TCHAR *BaseName, TCHAR *RegKeyName )
{
                HKEY hRegKey;
                DWORD Result;
                DWORD Index = 0;
                TCHAR *ResultName = NULL;
                DWORD ResultNameLength;
                FILETIME LastWriteTime;
                DWORD MaxSubKeyLength;
                DWORD SubKeys;
                DWORD NumValues;
                DWORD MaxValueNameLen;
                DWORD MaxValueLen;
                TCHAR *NextSearchKey;
 
                if( ERROR_SUCCESS != RegOpenKeyEx( hKey, RegKeyName, 0, 0, &hRegKey ) )
                {
                                return;
                }
 
                Result = RegQueryInfoKey(
                                hRegKey,
                                NULL,
                                NULL,
                                NULL,
                                &SubKeys,
                                &MaxSubKeyLength,
                                NULL,
                                &NumValues,
                                &MaxValueNameLen,
                                &MaxValueLen,
                                NULL,
                                NULL
                                );
 
                // Only output this key if there are values, or if
                // there are no values and no subkeys meaning that this
                // key is itself a leaf.
                if( ( SubKeys == 0 && NumValues == 0 ) ||
                                ( NumValues > 0 ))
                {
                                RETAILMSG( 1, (TEXT("[%s]\n"), BaseName ));  
                                EnumerateRegistryValues( hRegKey );
                }
 
                if( SubKeys > 0 )
                {
                                // Need to add one for the null character at the end.
                                ResultNameLength = MaxSubKeyLength + 1;
                                // Allocate a buffer to read the subkeys. We only need to
                                // do this once because we know the length of the longest
                                // subkey is set in ResultNameLength
                                ResultName = malloc( ResultNameLength * sizeof( TCHAR ));
                                do {
 
                                                // ResultNameLength will change when RegEnumKeyEx() is called
                                                // so reinitialize it on each iteration
                                                ResultNameLength = MaxSubKeyLength + 1;
                                               
                                                Result = RegEnumKeyEx(
                                                                hRegKey,
                                                                Index,
                                                                ResultName,
                                                                &ResultNameLength,
                                                                NULL,
                                                                NULL,
                                                                NULL,
                                                                &LastWriteTime
                                                               );
                                                if( Result == ERROR_SUCCESS )
                                                {
                                                                NextSearchKey = malloc( (wcslen( BaseName ) +
                                                                                                wcslen( ResultName ) + 2 ) * sizeof( TCHAR ));
                                                                swprintf( NextSearchKey, TEXT("%s\\%s"), BaseName, ResultName );
 
                                                                EmumerateRegistry( hRegKey, NextSearchKey, ResultName );
 
                                                                free( NextSearchKey );
                                                }
                                                Index++;
 
                                } while( Result != ERROR_NO_MORE_ITEMS );
                                free( ResultName );
                }
                RegCloseKey( hRegKey );
}
I put the enumeration of the registry values in EnumerateRegistryValues(). EnumerateRegistryValues() has only one parameter, HKEY hKey, a handle to an open registry key. This function only handles four value types, mainly because I didn’t find examples to any other types.
void EnumerateRegistryValues( HKEY hKey )
{
                DWORD Index = 0;
                DWORD ValueNameLength;
                TCHAR *ValueName;
                DWORD Type;
                void *Data;
                DWORD DataLength;
                DWORD Result;
                DWORD SubKeys;
                DWORD NumValues;
                DWORD MaxValueNameLength;
                DWORD MaxValueLength;
                DWORD MaxSubKeyLength;
 
                Result = RegQueryInfoKey(
                                hKey,
                                NULL,
                                NULL,
                                NULL,
                                &SubKeys,
                                &MaxSubKeyLength,
                                NULL,
                                &NumValues,
                                &MaxValueNameLength,
                                &MaxValueLength,
                                NULL,
                                NULL
                                );
 
                ValueName = malloc( (MaxValueNameLength + 1) * sizeof( TCHAR ) );
                Data = malloc( MaxValueLength );
 
                do {
                                ValueNameLength = MaxValueNameLength + 1;
                                DataLength = MaxValueLength;
                                Result = RegEnumValue(
                                                hKey,
                                                Index,
                                                ValueName,
                                                &ValueNameLength,
                                                NULL,
                                                &Type,
                                                Data,
                                                &DataLength
                                                ) ;
                                if( Result == ERROR_SUCCESS )
                                {
                                                switch( Type )
                                                {
                                                                case REG_DWORD:
                                                                                RETAILMSG( 1, (TEXT("\t\"%s\"=dword:%X\n"), ValueName, *(DWORD *)Data ));
                                                                                break;
                                                                case REG_SZ:
                                                                                RETAILMSG( 1, (TEXT("\t\"%s\"=\"%s\"\n"), ValueName, (TCHAR *)Data ));
                                                                                break;
                                                                case REG_MULTI_SZ:
                                                                                RETAILMSG( 1, (TEXT("\t\"%s\"=multi_sz:\"%s\""), ValueName, (TCHAR *)Data ));
                                                                                {
                                                                                                DWORD NextIndex = wcslen( Data ) + 1;
                                                                                                TCHAR *pMString = (TCHAR *)Data;
                                                                                                while( '\0' != pMString[ NextIndex ] )
                                                                                                {
                                                                                                                RETAILMSG( 1, (TEXT(",\"%s\""), &pMString[ NextIndex ]));
                                                                                                                NextIndex += wcslen( &pMString[ NextIndex ] ) + 1;
                                                                                                }
                                                                                                RETAILMSG(1, (TEXT("\n")));
                                                                                }
                                                                                break;
                                                                case REG_BINARY:
                                                                                RETAILMSG( 1, (TEXT("\t\"%s\"=hex:"), ValueName ));
                                                                                {
                                                                                                BYTE *pdData = (BYTE *)Data;
                                                                                                DWORD Index ;
                                                                                                if( DataLength > 25 )
                                                                                                                RETAILMSG(1, (TEXT("\\\n\t\t")));
                                                                                                RETAILMSG( 1, (TEXT("%02.2X"), *pdData ));
                                                                                                for( Index = 1; Index < DataLength; Index++ )
                                                                                                {
                                                                                                                RETAILMSG( 1, (TEXT(",")));
                                                                                                                if( Index % 25 == 0 )
                                                                                                                                RETAILMSG(1, (TEXT("\\\n\t\t")));
                                                                                                                RETAILMSG( 1, (TEXT("%02.2X"), pdData[Index] ));
                                                                                                }
                                                                                                RETAILMSG(1, (TEXT("\n")));
                                                                                }
                                                                                break;
                                                                default:
                                                                                RETAILMSG( 1, (TEXT("\t;;\"%s\"= Type %d not handled\n"), ValueName, Type ));
                                                                                break;
                                                }
                                }
                                Index++;
 
                } while( Result != ERROR_NO_MORE_ITEMS );
                RETAILMSG( 1, (TEXT("\n")));
                free( ValueName );
                free( Data );
}
To test these functions, I called EnumerateRegistry() with the following calls:
                EmumerateRegistry( HKEY_LOCAL_MACHINE, TEXT("HKEY_LOCAL_MACHINE\\Drivers\\Builtin"), TEXT("Drivers\\Builtin") );
                EmumerateRegistry( HKEY_LOCAL_MACHINE, TEXT("HKEY_LOCAL_MACHINE\\Drivers\\Active"), TEXT("Drivers\\Active") );
                EmumerateRegistry( HKEY_LOCAL_MACHINE, TEXT("HKEY_LOCAL_MACHINE"), TEXT("") );
                EmumerateRegistry( HKEY_CURRENT_USER, TEXT("HKEY_CURRENT_USER"), TEXT("") );
Of course these examples use RETAILMSG to output the registry. I do that to keep the code simple and to leave fancy user interfaces to application developers. I did find a need to use this from a command prompt and wrote about that in Windows CE: Redirecting RETAILMSG to a File.
You may also want to look at Summary of Registry Posts
Tags: 
Copyright © 2008 – Bruce Eitman
All Rights Reserved