883 lines
21 KiB
C
883 lines
21 KiB
C
//*************************************************************
|
|
//
|
|
// Personal Classes Profile management routines
|
|
//
|
|
// Microsoft Confidential
|
|
// Copyright (c) Microsoft Corporation 1995
|
|
// All rights reserved
|
|
//
|
|
//*************************************************************
|
|
|
|
#include "uenv.h"
|
|
#include "windows.h"
|
|
|
|
// Local Data Structures
|
|
|
|
LPTSTR SpecialSubtrees[] =
|
|
{
|
|
TEXT("CLSID"),
|
|
TEXT("Interface"),
|
|
TEXT("TypeLib"),
|
|
TEXT("Licenses"),
|
|
TEXT("FileType")
|
|
};
|
|
|
|
#define MAX_SPECIAL_SUBTREE (sizeof(SpecialSubtrees)/sizeof(LPTSTR))
|
|
//
|
|
// Local function proto-types
|
|
//
|
|
typedef struct _RegKeyInfo {
|
|
DWORD SubKeyCount;
|
|
DWORD MaxSubKeyLen;
|
|
DWORD ValueCount;
|
|
DWORD MaxValueNameLen;
|
|
DWORD MaxValueLen;
|
|
DWORD SDLen;
|
|
LPTSTR pSubKeyName;
|
|
LPTSTR pValueName;
|
|
LPTSTR pValue;
|
|
} REGKEYINFO, *PREGKEYINFO;
|
|
|
|
//*************************************************************
|
|
//
|
|
// PrepForEnumRegistryTree()
|
|
//
|
|
// Purpose: prepare to duplicate a source bunch of keys into the destination.
|
|
//
|
|
// Parameters: hkSourceTree - source registry tree
|
|
// pRegKeyInfo - info block for use doing enumeration
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
// History: Date Author Comment
|
|
// 1/30/96 GregJen Created
|
|
//
|
|
//*************************************************************
|
|
BOOL
|
|
PrepForEnumRegistryTree(
|
|
HKEY hkSourceTree,
|
|
PREGKEYINFO pRegKeyInfo
|
|
)
|
|
{
|
|
LPTSTR pStringsBuffer;
|
|
LONG result;
|
|
|
|
result = RegQueryInfoKey(hkSourceTree,
|
|
NULL,
|
|
NULL,
|
|
0,
|
|
&pRegKeyInfo->SubKeyCount,
|
|
&pRegKeyInfo->MaxSubKeyLen,
|
|
NULL,
|
|
&pRegKeyInfo->ValueCount,
|
|
&pRegKeyInfo->MaxValueNameLen,
|
|
&pRegKeyInfo->MaxValueLen,
|
|
&pRegKeyInfo->SDLen,
|
|
NULL);
|
|
|
|
if ( result != ERROR_SUCCESS )
|
|
return FALSE;
|
|
|
|
// allocate a block of memory to use for enumerating subkeys and values
|
|
pStringsBuffer = (LPTSTR) LocalAlloc( LPTR,
|
|
(pRegKeyInfo->MaxSubKeyLen +
|
|
pRegKeyInfo->MaxValueNameLen +
|
|
pRegKeyInfo->MaxValueLen + 3)
|
|
* sizeof( TCHAR ) );
|
|
if ( !pStringsBuffer )
|
|
return FALSE;
|
|
|
|
pRegKeyInfo->pSubKeyName = pStringsBuffer;
|
|
pRegKeyInfo->pValueName = pStringsBuffer + pRegKeyInfo->MaxSubKeyLen + 1;
|
|
pRegKeyInfo->pValue = pRegKeyInfo->pValueName +
|
|
pRegKeyInfo->MaxValueNameLen + 1;
|
|
|
|
}
|
|
|
|
//*************************************************************
|
|
//
|
|
// CleanupAfterEnumRegistryTree()
|
|
//
|
|
// Purpose: duplicate a source bunch of keys into the destination.
|
|
//
|
|
// Parameters: hkSourceTree - source registry tree
|
|
// pRegKeyInfo - info block for use doing enumeration
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
// History: Date Author Comment
|
|
// 1/30/96 GregJen Created
|
|
//
|
|
//*************************************************************
|
|
void
|
|
CleanupAfterEnumRegistryTree(
|
|
HKEY hkSourceTree,
|
|
PREGKEYINFO pRegKeyInfo)
|
|
{
|
|
LocalFree( pRegKeyInfo->pSubKeyName );
|
|
}
|
|
BOOL
|
|
DeleteRegistrySubtree (
|
|
HKEY hkTree )
|
|
{
|
|
HKEY hkCurrentSourceKey;
|
|
DWORD idx;
|
|
DWORD NameLen;
|
|
LONG result = ERROR_SUCCESS;
|
|
REGKEYINFO RegKeyInfo;
|
|
BOOL Success = FALSE;
|
|
|
|
if ( !PrepForEnumRegistryTree( hkTree, &RegKeyInfo ) )
|
|
return FALSE; // nothing to clean up here yet
|
|
|
|
// enumerate all the source subkeys
|
|
// for each: if dest subkey is older than source, delete it
|
|
// if dest subkey does not exist (or was deleted) clone the subkey
|
|
//
|
|
// Clone all the subkeys
|
|
//
|
|
for ( idx = 0;
|
|
(result == ERROR_SUCCESS) &&
|
|
( result != ERROR_MORE_DATA ) &&
|
|
( idx < RegKeyInfo.SubKeyCount );
|
|
idx++ ) {
|
|
NameLen = RegKeyInfo.MaxSubKeyLen + sizeof( TCHAR );
|
|
result = RegEnumKeyEx( hkTree,
|
|
idx,
|
|
RegKeyInfo.pSubKeyName,
|
|
&NameLen,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL );
|
|
|
|
if ( ( result != ERROR_SUCCESS ) && ( result != ERROR_MORE_DATA ) )
|
|
goto cleanup;
|
|
|
|
// TBD: open the subkey in the source tree AS CurrentSourceKey
|
|
result = RegOpenKeyEx(hkTree,
|
|
RegKeyInfo.pSubKeyName,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hkCurrentSourceKey);
|
|
|
|
DeleteRegistrySubtree( hkCurrentSourceKey );
|
|
|
|
RegCloseKey( hkCurrentSourceKey );
|
|
|
|
RegDeleteKey( hkTree, RegKeyInfo.pSubKeyName );
|
|
|
|
result = ERROR_SUCCESS;
|
|
}
|
|
|
|
cleanup:
|
|
CleanupAfterEnumRegistryTree( hkTree, &RegKeyInfo );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
//*************************************************************
|
|
//
|
|
// CloneRegistryValues()
|
|
//
|
|
// Purpose: copy the values from under the source key to the dest key
|
|
//
|
|
// Parameters: SourceTree - source registry tree
|
|
// DestinationTree - destintation registry tree
|
|
// RegKeyInfo - handy information from the RegEnumKeyEx call.
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
// History: Date Author Comment
|
|
// 1/14/96 GregJen Created
|
|
//
|
|
//*************************************************************
|
|
BOOL
|
|
CloneRegistryValues(
|
|
HKEY hkSourceTree,
|
|
HKEY hkDestinationTree,
|
|
REGKEYINFO RegKeyInfo )
|
|
{
|
|
LONG result = ERROR_SUCCESS;
|
|
DWORD idx;
|
|
DWORD ValueLen;
|
|
DWORD ValueType;
|
|
DWORD DataLen;
|
|
|
|
for ( idx = 0;
|
|
(result == ERROR_SUCCESS) &&
|
|
( result != ERROR_MORE_DATA ) &&
|
|
( idx < RegKeyInfo.ValueCount );
|
|
idx++ )
|
|
{
|
|
DataLen = RegKeyInfo.MaxValueLen + sizeof( TCHAR );
|
|
ValueLen = RegKeyInfo.MaxValueNameLen + sizeof( TCHAR );
|
|
|
|
result = RegEnumValue( hkSourceTree,
|
|
idx,
|
|
RegKeyInfo.pValueName,
|
|
&ValueLen,
|
|
NULL,
|
|
&ValueType,
|
|
(BYTE*) RegKeyInfo.pValue,
|
|
&DataLen);
|
|
|
|
// TBD: check errors
|
|
|
|
// now add the value to the destination key
|
|
|
|
result = RegSetValueEx( hkDestinationTree,
|
|
RegKeyInfo.pValueName,
|
|
0,
|
|
ValueType,
|
|
(BYTE*) RegKeyInfo.pValue,
|
|
DataLen );
|
|
// TBD: check errors
|
|
}
|
|
return TRUE;
|
|
}
|
|
//*************************************************************
|
|
//
|
|
// CloneRegistryTree()
|
|
//
|
|
// Purpose: duplicate a source bunch of keys into the destination.
|
|
//
|
|
// Parameters: SourceTree - source registry tree
|
|
// DestinationTree - destintation registry tree
|
|
// lpSubKeyName - if present this is a subkey name that
|
|
// corresponds to the SourceTree HKEY.
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
// History: Date Author Comment
|
|
// 1/14/96 GregJen Created
|
|
//
|
|
//*************************************************************
|
|
BOOL
|
|
CloneRegistryTree(
|
|
HKEY hkSourceTree,
|
|
HKEY hkDestinationTree,
|
|
LPTSTR lpDestTreeName )
|
|
{
|
|
HKEY hkCurrentSourceKey;
|
|
DWORD idx;
|
|
DWORD NameLen;
|
|
LONG result = ERROR_SUCCESS;
|
|
REGKEYINFO RegKeyInfo;
|
|
BOOL Success = FALSE;
|
|
|
|
if ( !PrepForEnumRegistryTree( hkSourceTree, &RegKeyInfo ) )
|
|
return FALSE; // nothing to clean up here yet
|
|
|
|
if ( lpDestTreeName ) {
|
|
HKEY hkNewKey;
|
|
DWORD dwSDLen = RegKeyInfo.SDLen;
|
|
DWORD dwDisp;
|
|
SECURITY_INFORMATION SI = DACL_SECURITY_INFORMATION; // for now...
|
|
PSECURITY_DESCRIPTOR pSD = (PSECURITY_DESCRIPTOR)
|
|
LocalAlloc( LPTR, dwSDLen );
|
|
// TBD: check for NULL;
|
|
|
|
// Get the registry security information from the old key
|
|
result = RegGetKeySecurity( hkSourceTree,
|
|
SI,
|
|
pSD,
|
|
&dwSDLen);
|
|
// TBD: check for errors, free pSD
|
|
// create a key with the given name, and registry info
|
|
result = RegCreateKeyEx( hkDestinationTree,
|
|
lpDestTreeName,
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
pSD,
|
|
&hkNewKey,
|
|
&dwDisp );
|
|
// TBD: check for errors, free pSD
|
|
|
|
// TBD: and update the hkDestinationTree variable to point to it
|
|
|
|
hkDestinationTree = hkNewKey;
|
|
LocalFree( pSD );
|
|
}
|
|
|
|
//
|
|
// clone the values
|
|
//
|
|
|
|
if ( ! CloneRegistryValues( hkSourceTree, hkDestinationTree, RegKeyInfo ) )
|
|
goto cleanup;
|
|
|
|
//
|
|
// Clone all the subkeys
|
|
//
|
|
for ( idx = 0;
|
|
(result == ERROR_SUCCESS) &&
|
|
( result != ERROR_MORE_DATA ) &&
|
|
( idx < RegKeyInfo.SubKeyCount );
|
|
idx++ ) {
|
|
NameLen = RegKeyInfo.MaxSubKeyLen + sizeof( TCHAR );
|
|
result = RegEnumKeyEx( hkSourceTree,
|
|
idx,
|
|
RegKeyInfo.pSubKeyName,
|
|
&NameLen,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL );
|
|
|
|
if ( ( result != ERROR_SUCCESS ) && ( result != ERROR_MORE_DATA ) )
|
|
goto cleanup;
|
|
|
|
// TBD: open the subkey in the source tree AS CurrentSourceKey
|
|
result = RegOpenKeyEx(hkSourceTree,
|
|
RegKeyInfo.pSubKeyName,
|
|
0,
|
|
KEY_READ,
|
|
&hkCurrentSourceKey);
|
|
|
|
//
|
|
// recurse passing the subkey name
|
|
//
|
|
CloneRegistryTree( hkCurrentSourceKey,
|
|
hkDestinationTree,
|
|
RegKeyInfo.pSubKeyName );
|
|
|
|
//
|
|
// close our open key
|
|
//
|
|
|
|
RegCloseKey( hkCurrentSourceKey );
|
|
}
|
|
|
|
Success = TRUE;
|
|
|
|
cleanup:
|
|
if ( lpDestTreeName )
|
|
{
|
|
RegCloseKey( hkDestinationTree );
|
|
}
|
|
|
|
CleanupAfterEnumRegistryTree( hkSourceTree, &RegKeyInfo );
|
|
|
|
return Success;
|
|
}
|
|
|
|
// TBD: dummy prototypes for now
|
|
// dummy testing code
|
|
void TestCloneHive( )
|
|
{
|
|
// for testing
|
|
HKEY hkSource;
|
|
HKEY hkDestination;
|
|
LONG result;
|
|
|
|
result =
|
|
RegOpenKeyEx(HKEY_CLASSES_ROOT,
|
|
TEXT(".pps"),
|
|
0,
|
|
KEY_READ,
|
|
&hkSource);
|
|
result =
|
|
RegOpenKeyEx(HKEY_CLASSES_ROOT,
|
|
TEXT(".paa"),
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hkDestination);
|
|
CloneRegistryTree(hkSource, hkDestination, NULL);
|
|
}
|
|
|
|
void SaveChangesToUser( )
|
|
{
|
|
}
|
|
|
|
void SaveChangesToCommon( )
|
|
{
|
|
}
|
|
|
|
BOOL
|
|
AddSharedValuesToSubkeys( HKEY hkShared, LPTSTR pszSubtree )
|
|
{
|
|
HKEY hkSourceKey;
|
|
HKEY hkCurrentSourceKey;
|
|
DWORD idx;
|
|
DWORD NameLen;
|
|
LONG result = ERROR_SUCCESS;
|
|
REGKEYINFO RegKeyInfo;
|
|
BOOL Success = FALSE;
|
|
|
|
|
|
// for every subkey, set "Shared" value
|
|
result = RegOpenKeyEx( hkShared,
|
|
pszSubtree,
|
|
0,
|
|
KEY_READ,
|
|
&hkSourceKey );
|
|
|
|
// TBD: if no subtree in source, skip ahead to next special subtree
|
|
if ( result == ERROR_FILE_NOT_FOUND )
|
|
return TRUE;
|
|
|
|
if ( !PrepForEnumRegistryTree( hkSourceKey, &RegKeyInfo ) )
|
|
goto cleanup2;
|
|
|
|
// enumerate all the source subkeys
|
|
// for each: if dest subkey is older than source, delete it
|
|
// if dest subkey does not exist (or was deleted) clone the subkey
|
|
//
|
|
// Clone all the subkeys
|
|
//
|
|
for ( idx = 0;
|
|
(result == ERROR_SUCCESS) &&
|
|
( result != ERROR_MORE_DATA ) &&
|
|
( idx < RegKeyInfo.SubKeyCount );
|
|
idx++ )
|
|
{
|
|
NameLen = RegKeyInfo.MaxSubKeyLen + sizeof( TCHAR );
|
|
result = RegEnumKeyEx( hkSourceKey,
|
|
idx,
|
|
RegKeyInfo.pSubKeyName,
|
|
&NameLen,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL );
|
|
|
|
if ( ( result != ERROR_SUCCESS ) && ( result != ERROR_MORE_DATA ) )
|
|
goto cleanup;
|
|
|
|
result = RegOpenKeyEx( hkSourceKey,
|
|
RegKeyInfo.pSubKeyName,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hkCurrentSourceKey );
|
|
|
|
// check for errors
|
|
|
|
result = RegSetValueEx( hkCurrentSourceKey,
|
|
L"Shared",
|
|
0,
|
|
REG_SZ,
|
|
(LPBYTE) L"Y",
|
|
sizeof( L"Y" ) );
|
|
|
|
RegCloseKey( hkCurrentSourceKey );
|
|
|
|
}
|
|
|
|
Success = TRUE;
|
|
|
|
cleanup:
|
|
CleanupAfterEnumRegistryTree( hkSourceKey, &RegKeyInfo );
|
|
|
|
cleanup2:
|
|
RegCloseKey( hkSourceKey );
|
|
|
|
return Success;
|
|
|
|
}
|
|
|
|
BOOL
|
|
AddSharedValues( HKEY hkShared )
|
|
{
|
|
// for each of the special subtrees, add "Shared" values
|
|
int idx;
|
|
|
|
// now, for each of the special top-level keys, process the level below them
|
|
// these keys are: CLSID, Interface, TypeLib, Licenses, FileType
|
|
|
|
for ( idx = 0; idx < MAX_SPECIAL_SUBTREE; idx++ )
|
|
{
|
|
AddSharedValuesToSubkeys( hkShared, SpecialSubtrees[idx] );
|
|
}
|
|
|
|
// now do all the top level keys (file extensions and progids)
|
|
AddSharedValuesToSubkeys( hkShared, NULL );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// MergeUserClasses()
|
|
//
|
|
// Purpose: Merges the user's class information with the
|
|
// common class information.
|
|
//
|
|
// Parameters: UserClassStore - Per-user class information
|
|
// CommonClassStore - Machine-wide class information
|
|
// MergedClassStore - Destination for merged information.
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
// Comments: UserClassStore may be a null HKEY, implying
|
|
// just copy the information from the common
|
|
// portion into the merged portion
|
|
//
|
|
// History: Date Author Comment
|
|
// 1/14/96 GregJen Created
|
|
//
|
|
//*************************************************************
|
|
BOOL
|
|
MergeRegistrySubKeys (
|
|
HKEY hkSourceTree,
|
|
HKEY hkDestTree )
|
|
{
|
|
HKEY hkCurrentSourceKey;
|
|
HKEY hkCurrentDestKey;
|
|
DWORD idx;
|
|
DWORD NameLen;
|
|
LONG result = ERROR_SUCCESS;
|
|
REGKEYINFO RegKeyInfo;
|
|
BOOL Success = FALSE;
|
|
FILETIME SourceFileTime;
|
|
FILETIME DestFileTime;
|
|
LONG cmp;
|
|
|
|
if ( !PrepForEnumRegistryTree( hkSourceTree, &RegKeyInfo ) )
|
|
return FALSE; // nothing to clean up here yet
|
|
|
|
// enumerate all the source subkeys
|
|
// for each: if dest subkey is older than source, delete it
|
|
// if dest subkey does not exist (or was deleted) clone the subkey
|
|
//
|
|
// Clone all the subkeys
|
|
//
|
|
for ( idx = 0;
|
|
(result == ERROR_SUCCESS) &&
|
|
( result != ERROR_MORE_DATA ) &&
|
|
( idx < RegKeyInfo.SubKeyCount );
|
|
idx++ ) {
|
|
NameLen = RegKeyInfo.MaxSubKeyLen + sizeof( TCHAR );
|
|
result = RegEnumKeyEx( hkSourceTree,
|
|
idx,
|
|
RegKeyInfo.pSubKeyName,
|
|
&NameLen,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&SourceFileTime );
|
|
|
|
if ( ( result != ERROR_SUCCESS ) && ( result != ERROR_MORE_DATA ) )
|
|
goto cleanup;
|
|
|
|
// TBD: open the subkey in the source tree AS CurrentSourceKey
|
|
result = RegOpenKeyEx(hkSourceTree,
|
|
RegKeyInfo.pSubKeyName,
|
|
0,
|
|
KEY_READ,
|
|
&hkCurrentSourceKey);
|
|
|
|
|
|
result = RegOpenKeyEx(hkDestTree,
|
|
RegKeyInfo.pSubKeyName,
|
|
0,
|
|
KEY_READ,
|
|
&hkCurrentDestKey);
|
|
|
|
// if current dest key does not exist,
|
|
if ( result == ERROR_FILE_NOT_FOUND )
|
|
{
|
|
//
|
|
// recurse passing the subkey name
|
|
//
|
|
CloneRegistryTree( hkCurrentSourceKey,
|
|
hkDestTree,
|
|
RegKeyInfo.pSubKeyName );
|
|
}
|
|
// if current dest key is older than current source key, delete dest
|
|
// then recreate new
|
|
else
|
|
{
|
|
RegQueryInfoKey( hkCurrentDestKey,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&DestFileTime );
|
|
|
|
|
|
cmp = CompareFileTime( &SourceFileTime, &DestFileTime );
|
|
if ( cmp > 0 )
|
|
{
|
|
// delete dest
|
|
//
|
|
DeleteRegistrySubtree( hkCurrentDestKey );
|
|
|
|
//
|
|
// recurse passing the subkey name
|
|
//
|
|
CloneRegistryTree( hkCurrentSourceKey,
|
|
hkDestTree,
|
|
RegKeyInfo.pSubKeyName );
|
|
}
|
|
}
|
|
|
|
//
|
|
// close our open key
|
|
//
|
|
|
|
RegCloseKey( hkCurrentSourceKey );
|
|
|
|
result = ERROR_SUCCESS;
|
|
}
|
|
|
|
cleanup:
|
|
CleanupAfterEnumRegistryTree( hkSourceTree, &RegKeyInfo );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
|
|
|
|
|
|
//*************************************************************
|
|
//
|
|
// MergeUserClasses()
|
|
//
|
|
// Purpose: Merges the user's class information with the
|
|
// common class information.
|
|
//
|
|
// Parameters: UserClassStore - Per-user class information
|
|
// CommonClassStore - Machine-wide class information
|
|
// MergedClassStore - Destination for merged information.
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
// Comments: UserClassStore may be a null HKEY, implying
|
|
// just copy the information from the common
|
|
// portion into the merged portion
|
|
//
|
|
// History: Date Author Comment
|
|
// 1/14/96 GregJen Created
|
|
//
|
|
//*************************************************************
|
|
BOOL
|
|
MergeRegistrySubtree (
|
|
HKEY hkSourceParent,
|
|
HKEY hkDestParent,
|
|
LPTSTR pszSubtree )
|
|
{
|
|
HKEY hkCurrentSourceKey;
|
|
HKEY hkCurrentDestKey;
|
|
LONG result;
|
|
DWORD dummy = 0;
|
|
|
|
// open the special subtree in the source tree
|
|
result = RegOpenKeyEx( hkSourceParent,
|
|
pszSubtree,
|
|
0,
|
|
KEY_READ,
|
|
&hkCurrentSourceKey );
|
|
|
|
// TBD: if no subtree in source, skip ahead to next special subtree
|
|
if ( result == ERROR_FILE_NOT_FOUND )
|
|
return TRUE;
|
|
|
|
result = RegOpenKeyEx( hkDestParent,
|
|
pszSubtree,
|
|
0,
|
|
KEY_ALL_ACCESS,
|
|
&hkCurrentDestKey );
|
|
// TBD: if no such subtree in dest, do CloneRegistry etc
|
|
if ( result == ERROR_FILE_NOT_FOUND )
|
|
{
|
|
//
|
|
// recurse passing the subkey name
|
|
//
|
|
CloneRegistryTree( hkCurrentSourceKey,
|
|
hkDestParent,
|
|
pszSubtree );
|
|
}
|
|
// TBD:if timestamp on source is newer than timestamp on dest,
|
|
// delete dest and recreate??
|
|
|
|
MergeRegistrySubKeys( hkCurrentSourceKey,
|
|
hkCurrentDestKey );
|
|
|
|
// make sure the timestamp on the special trees is updated
|
|
result = RegSetValueEx( hkCurrentDestKey,
|
|
TEXT("Updated"),
|
|
0,
|
|
REG_DWORD,
|
|
(BYTE*) &dummy,
|
|
sizeof( DWORD ) );
|
|
|
|
|
|
// close special subtrees
|
|
RegCloseKey( hkCurrentSourceKey );
|
|
RegCloseKey( hkCurrentDestKey );
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
long
|
|
CompareRegistryTimes(
|
|
HKEY hkLHS,
|
|
HKEY hkRHS )
|
|
{
|
|
FILETIME LHSTime;
|
|
FILETIME RHSTime;
|
|
|
|
RegQueryInfoKey( hkLHS,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&LHSTime );
|
|
|
|
RegQueryInfoKey( hkRHS,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
NULL,
|
|
&RHSTime );
|
|
|
|
return CompareFileTime( &LHSTime, &RHSTime );
|
|
}
|
|
|
|
//*************************************************************
|
|
//
|
|
// MergeUserClasses()
|
|
//
|
|
// Purpose: Merges the user's class information with the
|
|
// common class information.
|
|
//
|
|
// Parameters: UserClassStore - Per-user class information
|
|
// CommonClassStore - Machine-wide class information
|
|
// MergedClassStore - Destination for merged information.
|
|
//
|
|
// Return: TRUE if successful
|
|
// FALSE if an error occurs
|
|
//
|
|
// Comments: UserClassStore may be a null HKEY, implying
|
|
// just copy the information from the common
|
|
// portion into the merged portion
|
|
//
|
|
// History: Date Author Comment
|
|
// 1/14/96 GregJen Created
|
|
//
|
|
//*************************************************************
|
|
BOOL
|
|
MergeUserClasses(
|
|
HKEY UserClassStore,
|
|
HKEY CommonClassStore,
|
|
HKEY MergedClassStore,
|
|
BOOL ForceNew )
|
|
{
|
|
BOOL fNotCorrectUser = FALSE;
|
|
HKEY hkOverridingSubtree = CommonClassStore;
|
|
HKEY hkMergingSubtree = UserClassStore;
|
|
int idx;
|
|
|
|
//TBD: check time stamps on source and destination
|
|
// if same user, and timestamps are in sync, do nothing
|
|
|
|
// if destination does not belong to the current user, then
|
|
// delete everything under it
|
|
|
|
if ( fNotCorrectUser ) {
|
|
DeleteRegistrySubtree( MergedClassStore );
|
|
}
|
|
|
|
|
|
if ( !ForceNew &&
|
|
( CompareRegistryTimes( MergedClassStore, CommonClassStore ) > 0 ) &&
|
|
( CompareRegistryTimes( MergedClassStore, UserClassStore ) > 0 ) )
|
|
{
|
|
return TRUE;
|
|
}
|
|
|
|
// TBD: copy everything from the overriding store into the
|
|
// destination store
|
|
// At this moment, the common store overrides the user store;
|
|
// this will eventually reverse.
|
|
|
|
CloneRegistryTree( hkOverridingSubtree, MergedClassStore, NULL );
|
|
|
|
// now, for each of the special top-level keys, process the level below them
|
|
// these keys are: CLSID, Interface, TypeLib, Licenses, FileType
|
|
|
|
for ( idx = 0; idx < MAX_SPECIAL_SUBTREE; idx++ )
|
|
{
|
|
MergeRegistrySubtree( hkMergingSubtree,
|
|
MergedClassStore,
|
|
SpecialSubtrees[idx] );
|
|
}
|
|
|
|
// now do all the top level keys (file extensions and progids)
|
|
// TBD: MergeRegistrySubtree( UserClassStore, MergedClassStore );
|
|
MergeRegistrySubtree( hkMergingSubtree,
|
|
MergedClassStore,
|
|
NULL );
|
|
|
|
return TRUE;
|
|
}
|
|
// dummy testing code
|
|
void TestMergeHives( )
|
|
{
|
|
// for testing
|
|
HKEY hkUser;
|
|
HKEY hkMachine;
|
|
HKEY hkMerged;
|
|
LONG result;
|
|
DWORD dwCreated;
|
|
|
|
result =
|
|
RegOpenKeyEx(HKEY_CLASSES_ROOT,
|
|
TEXT(".111111\\PerUser"),
|
|
0,
|
|
KEY_READ,
|
|
&hkUser);
|
|
result =
|
|
RegOpenKeyEx(HKEY_CLASSES_ROOT,
|
|
TEXT(".111111\\MachineClasses"),
|
|
0,
|
|
KEY_READ,
|
|
&hkMachine);
|
|
// note: eventually, this would be created with the
|
|
// same security as the per-user part.
|
|
|
|
// if the per-user part is missing, just copy the machine to per-user
|
|
//
|
|
result =
|
|
RegCreateKeyEx(HKEY_CLASSES_ROOT,
|
|
TEXT(".111111\\MergedClasses"),
|
|
0,
|
|
NULL,
|
|
REG_OPTION_NON_VOLATILE,
|
|
KEY_ALL_ACCESS,
|
|
NULL,
|
|
&hkMerged,
|
|
&dwCreated);
|
|
MergeUserClasses(hkUser, hkMachine, hkMerged, TRUE);
|
|
}
|
|
|
|
|