NT4/private/ole32/olecnfg/treemgmt.c
2020-09-30 17:12:29 +02:00

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);
}