windows-nt/Source/XPSP1/NT/base/ntsetup/win95upg/w95upgnt/merge/hkcr.c
2020-09-26 16:20:57 +08:00

2055 lines
51 KiB
C

/*++
Copyright (c) 1998 Microsoft Corporation
Module Name:
hkcr.c
Abstract:
Implements routines that merge various HKCR settings. Macro expansion
list defines a list of merge routines that are called for a particular
key. A context ID and a set of flags allow control over when a merge
routine is called.
The flag set controls the type of enumeration the merge routine wants
to be notified with. It can be any of three values:
MERGE_FLAG_KEY - Called for the root key itself
MERGE_FLAG_VALUE - Called for each value in the root key
MERGE_FLAG_SUBKEY - Called for each subkey in the root key
Recursion of MergeRegistryNode is used to copy parts of the tree.
The context ID is defined in merge.h, and is expandable. It is used
to specify a context when making recursive calls.
The order of processing of the merge routines is specified in the
macro expansion list. The default behavior if no routine chooses to
handle a key is to copy without overwrite.
Author:
Jim Schmidt (jimschm) 24-Mar-1998
Revision History:
jimschm 23-Sep-1998 Updated for new flag bit size
jimschm 27-Apr-1998 Added DefaultIcon preservation
--*/
#include "pch.h"
#include "mergep.h"
extern DWORD g_ProgressBarCounter;
#define MERGE_FLAG_KEY 0x0001
#define MERGE_FLAG_SUBKEY 0x0002
#define MERGE_FLAG_VALUE 0x0004
#define MERGE_ALL_FLAGS 0xFFFFFFFF
#define MERGE_FLAG_SUBKEYS_AND_VALUES (MERGE_FLAG_SUBKEY|MERGE_FLAG_VALUE)
#define MERGE_FLAG_ALL (MERGE_FLAG_KEY|MERGE_FLAG_SUBKEY|MERGE_FLAG_VALUE)
#define MERGE_FLAG_ALL_KEYS (MERGE_FLAG_KEY|MERGE_FLAG_SUBKEY)
#define MULTI_CONTEXT ANY_CONTEXT
/*++
Macro Expansion List Description:
HKCR_FUNCTION_LIST lists functions that are called to process HKCR
registry data. The functions are called in the order specified by
the macro expansion list, and when none of the functions process
the data, the last filter, pLastMergeRoutine, performs a copy-no-
overwrite merge.
Processing occurs in the following stages:
1. Functions are called for the key itself
2. Functions are called for each of the key's values
3. Functions are called for each of the key's subkeys
Line Syntax:
DEFMAC(FilterFn, ContextId, Flags)
Arguments:
FilterFn - Specifies the name of the function. This function is
automatically prototyped as:
MERGE_RESULT
FilterFn (
PMERGE_STATE State
);
The filter function uses the State structure to determine
the context surrounding the registry data (i.e., where is
it, what its parent is, what kind of data, etc.). The
function returns one of the following:
MERGE_LEAVE - Processing of the current key is terminated.
MERGE_BREAK - Processing breaks out of the loop. If the
loop is a value enumeration, then processing
continues with the values. If the loop is
a subkey enumeration, processing ends for
the key.
MERGE_CONTINUE - Processing continues to the next item in
the enumeration.
MERGE_NOP - The function did not process the data
MERGE_ERROR - An error occurred processing the data. The
error stops processing of HKCR.
ContextId - Specifies the context the function is called in. Specify
ANY_CONTEXT or MULTI_CONTEXT to always be called. The
ContextId is specified by the caller of MergeRegistryNode.
NOTE: If MULTI_CONTEXT is used, the function must examine
the ContextId member of State and return MERGE_NOP
if the context is not correct.
Always place ANY_CONTEXT and MULTI_CONTEXT definitions
at the end of the HKCR_FUNCTION_LIST definition.
Flags - Specifies one or more of the following:
MERGE_FLAG_KEY - Called for the key, before enumeration
MERGE_FLAG_SUBKEY - Called for each subkey of the key
MERGE_FLAG_VALUE - Called for each value of the key
or a combined macro:
MERGE_FLAG_SUBKEYS_AND_VALUES - Called for each subkey and each value
MERGE_FLAG_ALL - Called for the key, then each subkey
and each value
Variables Generated From List:
g_MergeRoutines
--*/
#define HKCR_FUNCTION_LIST \
DEFMAC(pDetectRootKeyType, ROOT_BASE, MERGE_FLAG_SUBKEY) \
DEFMAC(pFileExtensionMerge, ROOT_BASE, MERGE_FLAG_SUBKEY) \
DEFMAC(pCopyClassId, CLSID_BASE, MERGE_FLAG_SUBKEY) \
DEFMAC(pCopyClassIdWorker, CLSID_COPY, MERGE_FLAG_ALL_KEYS) \
DEFMAC(pInstanceSpecialCase, CLSID_INSTANCE_COPY, MERGE_FLAG_ALL_KEYS) \
DEFMAC(pCopyTypeLibOrInterface, MULTI_CONTEXT, MERGE_FLAG_SUBKEY) \
DEFMAC(pCopyTypeLibVersion, TYPELIB_VERSION_COPY, MERGE_FLAG_SUBKEY) \
DEFMAC(pDefaultIconExtraction, COPY_DEFAULT_ICON, MERGE_FLAG_ALL) \
DEFMAC(pCopyDefaultValue, COPY_DEFAULT_VALUE, MERGE_FLAG_KEY) \
DEFMAC(pKeyCopyMerge, KEY_COPY, MERGE_FLAG_VALUE) \
\
DEFMAC(pDetectDefaultIconKey, ANY_CONTEXT, MERGE_FLAG_SUBKEY) \
DEFMAC(pEnsureShellDefaultValue, TREE_COPY_NO_OVERWRITE, MERGE_FLAG_SUBKEY) \
DEFMAC(pTreeCopyMerge, MULTI_CONTEXT, MERGE_FLAG_ALL) \
\
DEFMAC(pLastMergeRoutine, MULTI_CONTEXT, MERGE_FLAG_SUBKEY)
//
// Simplification macros
//
#define CopyAllValues(state) MergeValuesOfKey(state,KEY_COPY)
#define CopyAllSubKeyValues(state) MergeValuesOfSubKey(state,KEY_COPY)
#define CopyEntireSubKey(state) MergeSubKeyNode(state,TREE_COPY)
#define CopyEntireSubKeyNoOverwrite(state) MergeSubKeyNode(state,TREE_COPY_NO_OVERWRITE)
//
// Define macro expansion list types
//
typedef struct {
HKEY Key95;
HKEY KeyNt;
HKEY SubKey95;
HKEY SubKeyNt;
BOOL CloseKey95;
BOOL CloseKeyNt;
PCTSTR SubKeyName;
PCTSTR FullKeyName;
PCTSTR FullSubKeyName;
PCTSTR ValueName;
PBYTE ValueData;
DWORD ValueDataSize;
DWORD ValueDataType;
MERGE_CONTEXT Context;
DWORD MergeFlag;
BOOL LockValue;
} MERGE_STATE, *PMERGE_STATE;
typedef enum {
MERGE_LEAVE,
MERGE_BREAK,
MERGE_CONTINUE,
MERGE_NOP,
MERGE_ERROR
} MERGE_RESULT;
typedef MERGE_RESULT (MERGE_ROUTINE_PROTOTYPE) (PMERGE_STATE State);
typedef MERGE_ROUTINE_PROTOTYPE * MERGE_ROUTINE;
typedef struct {
MERGE_ROUTINE fn;
MERGE_CONTEXT Context;
DWORD Flags;
} MERGE_ROUTINE_ATTRIBS, *PMERGE_ROUTINE_ATTRIBS;
//
// Declare function prototypes
//
#define DEFMAC(fn,id,flags) MERGE_ROUTINE_PROTOTYPE fn;
HKCR_FUNCTION_LIST
#undef DEFMAC
//
// Create g_MergeRoutines array
//
#define DEFMAC(fn,id,flags) {fn,id,flags},
MERGE_ROUTINE_ATTRIBS g_MergeRoutines[] = {
HKCR_FUNCTION_LIST /* , */
{NULL, 0, 0}
};
#undef DEFMAC
//
// Local prototypes
//
MERGE_RESULT
pCallMergeRoutines (
IN OUT PMERGE_STATE State,
IN DWORD MergeFlag
);
BOOL
pMakeSureNtKeyExists (
IN PMERGE_STATE State
);
BOOL
pMakeSureNtSubKeyExists (
IN PMERGE_STATE State
);
//
// Globals
//
PBYTE g_MergeBuf;
UINT g_MergeBufUseCount;
//
// Implementation
//
BOOL
MergeRegistryNodeEx (
IN PCTSTR RootKey,
IN HKEY RootKey95, OPTIONAL
IN HKEY RootKeyNt, OPTIONAL
IN MERGE_CONTEXT Context,
IN DWORD RestrictionFlags
)
/*++
Routine Description:
MergeRegistryNode calls functions for the specified RootKey, all of its
values and all of its subkeys. The merge functions can at runtime decide
how to process a key, and typically call this function recursively. All
registry data is passed through the merge data filter, and keys or values
marked as suppressed are not processed.
Arguments:
RootKey - Specifies the root key string, starting with either HKLM or HKR.
RootKey95 - Specifies the 95-side root key; reduces the number of key open
calls
RootKeyNt - Specifies the NT-side root key; reduces the number of key open
calls
Context - Specifies a root ID constant that corresponds to RootKey. This
constant is used by the merge routines to determine the
processing context.
RestrictionFlags - Specifies MERGE_FLAG mask to restrict processing. The
caller can therefore limit the enumerations and processing
to only values, only subkeys, only the key, or a combination
of the three.
Return Value:
TRUE if processing was successful, or FALSE if one of the merge functiosn
returned an error.
--*/
{
REGKEY_ENUM ek;
REGVALUE_ENUM ev;
MERGE_STATE State;
MERGE_RESULT Result = MERGE_NOP;
ZeroMemory (&State, sizeof (MERGE_STATE));
//
// Do not process if key is suppressed
//
if (Is95RegKeyTreeSuppressed (RootKey)) {
return TRUE;
}
//
// Init State
//
ZeroMemory (&State, sizeof (State));
State.Context = Context;
//
// If the NT registry key is suppressed, then we want to do a tree copy, regardless
// of wether there is an NT value or not.
//
if (Context == TREE_COPY_NO_OVERWRITE && IsNtRegKeyTreeSuppressed (RootKey)) {
DEBUGMSG ((DBG_VERBOSE, "The NT Value for %s will be overwritten because it is marked for suppression.", RootKey));
State.Context = TREE_COPY;
}
if (RootKey95) {
State.Key95 = RootKey95;
} else {
State.Key95 = OpenRegKeyStr95 (RootKey);
State.CloseKey95 = (State.Key95 != NULL);
}
if (!State.Key95) {
DEBUGMSG ((DBG_VERBOSE, "Root %s does not exist", RootKey));
return TRUE;
}
//
// Progress bar update
//
g_ProgressBarCounter++;
if (g_ProgressBarCounter >= REGMERGE_TICK_THRESHOLD) {
g_ProgressBarCounter = 0;
TickProgressBar();
}
__try {
g_MergeBufUseCount++;
if (g_MergeBufUseCount == MAX_REGISTRY_KEY) {
DEBUGMSG ((DBG_WHOOPS, "Recursive merge depth indicates a loop problem, aborting!"));
__leave;
}
if (RootKeyNt) {
State.KeyNt = RootKeyNt;
} else {
State.KeyNt = OpenRegKeyStr (RootKey);
State.CloseKeyNt = (State.KeyNt != NULL);
}
State.FullKeyName = RootKey;
//
// Key processing
//
if (!Is95RegKeySuppressed (RootKey)) {
//
// Loop through the key functions for the root key
//
if (RestrictionFlags & MERGE_FLAG_KEY) {
Result = pCallMergeRoutines (&State, MERGE_FLAG_KEY);
if (Result == MERGE_ERROR || Result == MERGE_LEAVE) {
__leave;
}
}
//
// Loop through the values, skipping those that are suppressed
//
if ((RestrictionFlags & MERGE_FLAG_VALUE) &&
EnumFirstRegValue95 (&ev, State.Key95)
) {
do {
if (Is95RegObjectSuppressed (State.FullKeyName, ev.ValueName)) {
continue;
}
State.ValueName = ev.ValueName;
State.ValueDataType = ev.Type;
State.ValueDataSize = ev.DataSize;
//
// Loop through the value functions
//
Result = pCallMergeRoutines (&State, MERGE_FLAG_VALUE);
if (Result == MERGE_ERROR ||
Result == MERGE_LEAVE ||
Result == MERGE_BREAK
) {
break;
}
} while (EnumNextRegValue95 (&ev));
if (Result == MERGE_ERROR || Result == MERGE_LEAVE) {
__leave;
}
}
State.ValueName = NULL;
State.ValueDataType = 0;
State.ValueDataSize = 0;
}
//
// Subkey processing
//
if ((RestrictionFlags & MERGE_FLAG_SUBKEY) &&
EnumFirstRegKey95 (&ek, State.Key95)
) {
do {
//
// Prepare State, skip key if it is suppressed
//
State.SubKeyName = ek.SubKeyName;
State.FullSubKeyName = JoinPaths (RootKey, ek.SubKeyName);
if (Is95RegKeyTreeSuppressed (State.FullSubKeyName)) {
FreePathString (State.FullSubKeyName);
continue;
}
State.SubKey95 = OpenRegKey95 (ek.KeyHandle, ek.SubKeyName);
if (State.KeyNt) {
State.SubKeyNt = OpenRegKey (State.KeyNt, ek.SubKeyName);
} else {
State.SubKeyNt = NULL;
}
//
// Loop through the subkey functions
//
Result = pCallMergeRoutines (&State, MERGE_FLAG_SUBKEY);
//
// Clean up
//
FreePathString (State.FullSubKeyName);
if (State.SubKeyNt) {
CloseRegKey (State.SubKeyNt);
}
if (State.SubKey95) {
CloseRegKey95 (State.SubKey95);
}
if (Result == MERGE_ERROR ||
Result == MERGE_LEAVE ||
Result == MERGE_BREAK
) {
break;
}
} while (EnumNextRegKey95 (&ek));
if (Result == MERGE_ERROR || Result == MERGE_LEAVE) {
__leave;
}
}
}
__finally {
PushError();
g_MergeBufUseCount--;
if (!g_MergeBufUseCount && g_MergeBuf) {
ReuseFree (g_hHeap, g_MergeBuf);
g_MergeBuf = NULL;
}
if (State.CloseKey95) {
CloseRegKey95 (State.Key95);
}
if (State.CloseKeyNt) {
CloseRegKey (State.KeyNt);
}
PopError();
}
return Result != MERGE_ERROR;
}
BOOL
MergeRegistryNode (
IN PCTSTR RootKey,
IN MERGE_CONTEXT Context
)
{
return MergeRegistryNodeEx (RootKey, NULL, NULL, Context, MERGE_ALL_FLAGS);
}
BOOL
MergeKeyNode (
IN PMERGE_STATE State,
IN MERGE_CONTEXT Context
)
{
return MergeRegistryNodeEx (
State->FullKeyName,
State->Key95,
State->KeyNt,
Context,
MERGE_ALL_FLAGS
);
}
BOOL
MergeSubKeyNode (
IN PMERGE_STATE State,
IN MERGE_CONTEXT Context
)
{
return MergeRegistryNodeEx (
State->FullSubKeyName,
State->SubKey95,
State->SubKeyNt,
Context,
MERGE_ALL_FLAGS
);
}
BOOL
MergeValuesOfKey (
IN PMERGE_STATE State,
IN MERGE_CONTEXT Context
)
{
if (!pMakeSureNtKeyExists (State)) {
DEBUGMSG ((DBG_ERROR, "Can't create %s to merge values", State->FullKeyName));
return TRUE; // eat error
}
return MergeRegistryNodeEx (
State->FullKeyName,
State->Key95,
State->KeyNt,
Context,
MERGE_FLAG_VALUE
);
}
BOOL
MergeValuesOfSubKey (
IN PMERGE_STATE State,
IN MERGE_CONTEXT Context
)
{
if (!pMakeSureNtSubKeyExists (State)) {
DEBUGMSG ((DBG_ERROR, "Can't create %s to merge values", State->FullSubKeyName));
return TRUE; // eat error
}
return MergeRegistryNodeEx (
State->FullSubKeyName,
State->SubKey95,
State->SubKeyNt,
Context,
MERGE_FLAG_VALUE
);
}
MERGE_RESULT
pCallMergeRoutines (
IN OUT PMERGE_STATE State,
IN DWORD MergeFlag
)
{
INT i;
MERGE_RESULT Result = MERGE_NOP;
State->MergeFlag = MergeFlag;
for (i = 0 ; g_MergeRoutines[i].fn ; i++) {
if (g_MergeRoutines[i].Context != State->Context &&
g_MergeRoutines[i].Context != ANY_CONTEXT
) {
continue;
}
if (g_MergeRoutines[i].Flags & MergeFlag) {
Result = g_MergeRoutines[i].fn (State);
if (Result != MERGE_NOP) {
break;
}
}
}
return Result;
}
BOOL
pFillStateWithValue (
IN OUT PMERGE_STATE State
)
/*++
Routine Description:
pFillStateWithValue queries the Win95 registry for the value specified in
the inbound State struct. Upon return, the ValueData member of State is
set to the global buffer g_MergeBuf. The value is passed through the merge
data filter in datafilt.c.
The caller must make a copy of the data if two or more values are to be
processed at the same time.
Arguments:
State - Specifies the registry key name and value, along with a key handle.
Receives the value data.
Return Value:
TRUE if the value was read, or FALSE if an error occurred.
--*/
{
LONG rc;
DWORD Size;
if (!State->ValueName) {
DEBUGMSG ((DBG_WHOOPS, "pFillStateWithValue: No value name"));
return FALSE;
}
if (State->LockValue) {
return TRUE;
}
//
// Do not process if value is suppressed
//
if (Is95RegObjectSuppressed (State->FullKeyName, State->ValueName)) {
return TRUE;
}
//
// Get data from registry
//
rc = Win95RegQueryValueEx (
State->Key95,
State->ValueName,
NULL,
&State->ValueDataType,
NULL,
&State->ValueDataSize
);
if (rc != ERROR_SUCCESS) {
SetLastError (rc);
LOG ((
LOG_ERROR,
"Win95Reg query size of %s [%s] failed",
State->FullKeyName,
State->ValueName
));
return FALSE;
}
Size = State->ValueDataSize;
g_MergeBuf = (PBYTE) ReuseAlloc (g_hHeap, g_MergeBuf, Size);
if (!g_MergeBuf) {
DEBUGMSG ((DBG_ERROR, "pFillStateWithValue: ReuseAlloc returned NULL"));
return FALSE;
}
rc = Win95RegQueryValueEx (
State->Key95,
State->ValueName,
NULL,
NULL,
g_MergeBuf,
&Size
);
if (rc != ERROR_SUCCESS) {
SetLastError (rc);
LOG ((
LOG_ERROR,
"Win95Reg query for %s [%s] failed",
State->FullKeyName,
State->ValueName
));
return FALSE;
}
//
// Convert data if necessary; g_MergeBuf is a ReUse buffer, so it's
// address may change upon return.
//
State->ValueData = FilterRegValue (
g_MergeBuf,
Size,
State->ValueDataType,
State->FullKeyName,
&State->ValueDataSize
);
if (State->ValueData) {
if (g_MergeBuf != State->ValueData) {
g_MergeBuf = State->ValueData;
}
}
return State->ValueData != NULL;
}
BOOL
pMakeSureNtKeyExists (
IN PMERGE_STATE State
)
{
if (!State->KeyNt) {
State->KeyNt = CreateRegKeyStr (State->FullKeyName);
State->CloseKeyNt = (State->KeyNt != NULL);
}
return State->KeyNt != NULL;
}
BOOL
pMakeSureNtSubKeyExists (
IN PMERGE_STATE State
)
{
if (!State->SubKeyNt) {
State->SubKeyNt = CreateRegKeyStr (State->FullSubKeyName);
}
return State->SubKeyNt != NULL;
}
BOOL
pCopy95ValueToNt (
IN OUT PMERGE_STATE State
)
{
LONG rc = ERROR_SUCCESS;
if (pFillStateWithValue (State)) {
pMakeSureNtKeyExists (State);
rc = RegSetValueEx (
State->KeyNt,
State->ValueName,
0,
State->ValueDataType,
State->ValueData,
State->ValueDataSize
);
if (rc != ERROR_SUCCESS) {
SetLastError (rc);
LOG ((
LOG_ERROR,
"Copy Win9x Value To Nt failed to set %s [%s]",
State->FullKeyName,
State->ValueName
));
}
}
return rc = ERROR_SUCCESS;
}
MERGE_RESULT
pFileExtensionMerge (
IN PMERGE_STATE State
)
{
BOOL CloseNTKey = FALSE;
BOOL Close95Key = FALSE;
PCTSTR Value9x, ValueNt;
TCHAR key [MEMDB_MAX];
DWORD value;
MYASSERT (State->FullKeyName);
MYASSERT (State->FullSubKeyName);
MYASSERT (State->SubKeyName);
MYASSERT (State->SubKey95);
MYASSERT (State->MergeFlag == MERGE_FLAG_SUBKEY);
MYASSERT (State->Context == ROOT_BASE);
//
// Sub key name must have a dot
//
if (_tcsnextc (State->SubKeyName) != TEXT('.')) {
return MERGE_NOP;
}
//
// We look now to see if NT comes with this file extension.
// If it does and the progID referenced by 9x file extension has
// a loss of default command functionality, we let NT overwrite
// the ProgId reference. We do this by suppressing the default
// value of this file extension.
//
if (!State->SubKey95) {
State->SubKey95 = OpenRegKeyStr95 (State->FullSubKeyName);
Close95Key = (State->SubKey95 != NULL);
}
if (!State->SubKeyNt) {
State->SubKeyNt = OpenRegKeyStr (State->FullSubKeyName);
CloseNTKey = (State->SubKeyNt != NULL);
}
if (State->SubKey95 && State->SubKeyNt) {
//
// Let's see if we the NT default value is different than 9x one.
//
Value9x = GetRegValueString95 (State->SubKey95, TEXT(""));
ValueNt = GetRegValueString (State->SubKeyNt, TEXT(""));
if ((ValueNt && !Value9x) ||
(!ValueNt && Value9x) ||
(ValueNt && Value9x && (!StringIMatch (Value9x, ValueNt)))
) {
MemDbBuildKey (key, MEMDB_CATEGORY_PROGIDS, Value9x?Value9x:State->SubKeyName, NULL, NULL);
if (MemDbGetValue (key, &value) &&
(value == PROGID_LOSTDEFAULT)
) {
//
// Now it's the time to suppress the default value for this file extension
//
MemDbBuildKey (key, MEMDB_CATEGORY_HKLM, TEXT("SOFTWARE\\Classes"), State->SubKeyName, NULL);
if (!Suppress95RegSetting(key, TEXT(""))) {
DEBUGMSG((DBG_WARNING,"Could not suppress %s\\[] registry setting.", key));
}
}
}
if (Value9x) {
MemFree (g_hHeap, 0, Value9x);
Value9x = NULL;
}
if (ValueNt) {
MemFree (g_hHeap, 0, ValueNt);
ValueNt = NULL;
}
}
if (Close95Key) {
CloseRegKey95 (State->SubKey95);
State->SubKey95 = NULL;
}
if (CloseNTKey) {
CloseRegKey (State->SubKeyNt);
State->SubKeyNt = NULL;
}
//
// We copy all the extensions blindly
//
if (!CopyEntireSubKey (State)) {
return MERGE_ERROR;
}
//
// Return MERGE_CONTINUE so processing continues to next subkey
//
return MERGE_CONTINUE;
}
MERGE_RESULT
pDetectDefaultIconKey (
IN PMERGE_STATE State
)
{
MYASSERT (State->FullKeyName);
MYASSERT (State->FullSubKeyName);
MYASSERT (State->SubKeyName);
MYASSERT (State->SubKey95);
MYASSERT (State->MergeFlag == MERGE_FLAG_SUBKEY);
if (StringIMatch (State->SubKeyName, TEXT("DefaultIcon"))) {
if (!MergeSubKeyNode (State, COPY_DEFAULT_ICON)) {
return MERGE_ERROR;
}
return MERGE_CONTINUE;
}
return MERGE_NOP;
}
MERGE_RESULT
pEnsureShellDefaultValue (
IN PMERGE_STATE State
)
{
PTSTR dataNt = NULL;
PTSTR data9x = NULL;
PTSTR p = NULL;
MYASSERT (State->FullKeyName);
MYASSERT (State->MergeFlag == MERGE_FLAG_SUBKEY);
if (StringIMatch (State->SubKeyName, TEXT("Shell"))) {
//
// Get default values for both sides. We'll need this
// to determine wether to do the merge or not.
//
pMakeSureNtSubKeyExists (State);
data9x = (PTSTR) GetRegValueData95 (State->SubKey95, S_EMPTY);
dataNt = (PTSTR) GetRegValueData (State->SubKeyNt, S_EMPTY);
__try {
if (data9x && *data9x && (!dataNt || !*dataNt)) {
//
// If we get here, we know there is some value set
// in the win9x registry and no value set in the
// nt registry.
//
p = JoinPaths (State->FullSubKeyName, data9x);
if (!Is95RegKeyTreeSuppressed (p)) {
if (!MergeSubKeyNode (State, COPY_DEFAULT_VALUE)) {
return MERGE_ERROR;
}
}
}
} __finally {
if (p) {
FreePathString (p);
}
if (dataNt) {
MemFree (g_hHeap, 0, dataNt);
}
if (data9x) {
MemFree (g_hHeap, 0, data9x);
}
}
}
return MERGE_NOP;
}
MERGE_RESULT
pDefaultIconExtraction (
IN PMERGE_STATE State
)
{
PCTSTR Data;
TCHAR iconCmdLine[MAX_CMDLINE];
PCTSTR LongPath = NULL;
PCTSTR p;
INT IconIndex;
TCHAR IconIndexStr[32];
TCHAR Node[MEMDB_MAX];
DWORD Offset;
DWORD Seq;
LONG rc;
BOOL Copied = FALSE;
PCTSTR updatedPath;
INT newSeq;
MYASSERT (State->Context == COPY_DEFAULT_ICON);
if (State->MergeFlag == MERGE_FLAG_KEY) {
return MERGE_BREAK;
}
if (State->MergeFlag == MERGE_FLAG_SUBKEY) {
//
// Copy subkey (which is garbage)
//
if (!CopyEntireSubKey (State)) {
return MERGE_ERROR;
}
return MERGE_CONTINUE;
}
//
// Get the default command line
//
if (State->ValueDataSize > MAX_CMDLINE - sizeof (TCHAR)) {
LOG ((LOG_ERROR, "Data too large in %s [%s]", State->FullKeyName, State->ValueName));
return MERGE_CONTINUE;
}
Data = (PCTSTR) GetRegValueString95 (State->Key95, State->ValueName);
if (Data) {
//
// Determine if command line has saved icon
//
ExtractArgZeroEx (Data, iconCmdLine, TEXT(","), TRUE);
p = (PCTSTR) ((PBYTE) Data + ByteCount (iconCmdLine));
while (*p == TEXT(' ')) {
p++;
}
if (*p == TEXT(',')) {
IconIndex = _ttoi (_tcsinc (p));
LongPath = GetSourceFileLongName (iconCmdLine);
wsprintf (IconIndexStr, TEXT("%i"), IconIndex);
//
// Test for a moved icon. If there is a moved icon, use it,
// otherwise test for an extracted icon. If there is an
// extracted icon, use it. Otherwise, leave DefaultIcon alone.
//
iconCmdLine[0] = 0;
MemDbBuildKey (Node, MEMDB_CATEGORY_ICONS_MOVED, LongPath, IconIndexStr, NULL);
if (MemDbGetValueAndFlags (Node, &Offset, &Seq)) {
//
// icon moved to a new binary
//
if (IconIndex < 0) {
newSeq = -((INT) Seq);
} else {
newSeq = (INT) Seq;
}
MemDbBuildKeyFromOffset (Offset, Node, 1, NULL);
updatedPath = GetPathStringOnNt (Node);
wsprintf (iconCmdLine, TEXT("%s,%i"), updatedPath, newSeq);
FreePathString (updatedPath);
} else {
Offset = INVALID_OFFSET;
MemDbBuildKey (Node, MEMDB_CATEGORY_ICONS, LongPath, IconIndexStr, NULL);
if (MemDbGetValueAndFlags (Node, NULL, &Seq)) {
//
// icon was extracted
//
wsprintf (iconCmdLine, TEXT("%s,%i"), g_IconBin, Seq);
}
}
if (iconCmdLine[0]) {
//
// DefaultIcon has changed; write change now (full REG_SZ
// value is in iconCmdLine)
//
if (!pMakeSureNtKeyExists (State)) {
LOG ((
LOG_ERROR,
"Unable to open %s",
State->FullKeyName
));
} else {
rc = RegSetValueEx (
State->KeyNt,
State->ValueName,
0,
REG_SZ,
(PBYTE) iconCmdLine,
SizeOfString (iconCmdLine)
);
if (rc != ERROR_SUCCESS) {
SetLastError (rc);
LOG ((
LOG_ERROR,
"Default Icon Extraction failed to set path for %s",
State->FullKeyName
));
} else {
Copied = TRUE;
DEBUGMSG_IF ((
Offset == INVALID_OFFSET,
DBG_VERBOSE,
"DefaultIcon preserved for %s [%s]",
State->FullKeyName,
State->ValueName
));
DEBUGMSG_IF ((
Offset != INVALID_OFFSET,
DBG_VERBOSE,
"DefaultIcon moved to new OS icon for %s [%s]",
State->FullKeyName,
State->ValueName
));
}
}
}
FreePathString (LongPath);
}
MemFree (g_hHeap, 0, Data);
}
if (!Copied) {
pCopy95ValueToNt (State);
}
return MERGE_CONTINUE;
}
MERGE_RESULT
pTreeCopyMerge (
IN PMERGE_STATE State
)
{
REGVALUE_ENUM ev;
if (State->Context != TREE_COPY && State->Context != TREE_COPY_NO_OVERWRITE) {
return MERGE_NOP;
}
switch (State->MergeFlag) {
case MERGE_FLAG_KEY:
if (State->Context == TREE_COPY) {
if (!pMakeSureNtKeyExists (State)) {
LOG ((
LOG_ERROR,
"Unable to create %s",
State->FullKeyName
));
}
} else {
//
// If no values in Win9x key, then it is OK to create the
// value-less key now. Otherwise, wait until MERGE_FLAG_VALUE
// processing.
//
if (!EnumFirstRegValue95 (&ev, State->Key95)) {
if (!pMakeSureNtKeyExists (State)) {
LOG ((
LOG_ERROR,
"Unable to create %s",
State->FullKeyName
));
}
}
}
break;
case MERGE_FLAG_VALUE:
//
// Copy values unconditionally, unless no overwrite is specified and
// NT key exists.
//
if (State->Context == TREE_COPY_NO_OVERWRITE && State->KeyNt) {
return MERGE_BREAK;
}
if (!MergeKeyNode (State, KEY_COPY)) {
return MERGE_ERROR;
}
return MERGE_BREAK; // MERGE_BREAK breaks out of value enumeration and enters
// subkey enumeration
case MERGE_FLAG_SUBKEY:
//
// Continue copy recursively
//
if (!MergeSubKeyNode (State, State->Context)) {
return MERGE_ERROR;
}
break;
}
return MERGE_CONTINUE;
}
MERGE_RESULT
pKeyCopyMerge (
IN OUT PMERGE_STATE State
)
{
MYASSERT (State->FullKeyName);
MYASSERT (!State->FullSubKeyName);
MYASSERT (!State->SubKeyName);
MYASSERT (!State->SubKey95);
MYASSERT (State->ValueName);
MYASSERT (State->MergeFlag == MERGE_FLAG_VALUE);
MYASSERT (State->Context == KEY_COPY);
pCopy95ValueToNt (State);
return MERGE_CONTINUE;
}
MERGE_RESULT
pCopyDefaultValue (
IN PMERGE_STATE State
)
{
PCSTR value1;
PCSTR value2;
PCWSTR value3;
MYASSERT (State->FullKeyName);
MYASSERT (!State->FullSubKeyName);
MYASSERT (!State->SubKeyName);
MYASSERT (!State->SubKey95);
MYASSERT (!State->ValueName);
MYASSERT (State->MergeFlag == MERGE_FLAG_KEY);
MYASSERT (State->Context == COPY_DEFAULT_VALUE);
State->ValueName = S_EMPTY;
#ifdef DEBUG
//
// Obtain NT value, if it exists, then compare against Win9x value. If
// different, dump debug output.
//
{
PBYTE Data95, DataNt;
Data95 = GetRegValueData95 (State->Key95, S_EMPTY);
DataNt = GetRegValueData (State->KeyNt, S_EMPTY);
if (Data95 && DataNt) {
__try {
if (memcmp (Data95, DataNt, ByteCount ((PTSTR) Data95))) {
DEBUGMSG ((
DBG_VERBOSE,
"Default value of %s changed from %s to %s",
State->FullKeyName,
DataNt,
Data95
));
}
}
__except (1) {
}
}
if (Data95) {
MemFree (g_hHeap, 0, Data95);
}
if (DataNt) {
MemFree (g_hHeap, 0, DataNt);
}
}
#endif
//
// now let's get the value and convert it if necessary
//
if (pFillStateWithValue (State)) {
if ((OurGetACP() == 932) &&
((State->ValueDataType == REG_SZ) ||
(State->ValueDataType == REG_EXPAND_SZ)
)) {
//
// apply the Katakana filter
//
value1 = ConvertWtoA ((PCWSTR) State->ValueData);
value2 = ConvertSBtoDB (NULL, value1, NULL);
value3 = ConvertAtoW (value2);
g_MergeBuf = (PBYTE) ReuseAlloc (g_hHeap, g_MergeBuf, SizeOfStringW (value3));
StringCopyW ((PWSTR)g_MergeBuf, value3);
State->ValueData = g_MergeBuf;
FreeConvertedStr (value3);
FreePathStringA (value2);
FreeConvertedStr (value1);
}
State->LockValue = TRUE;
pCopy95ValueToNt (State);
State->LockValue = FALSE;
}
State->ValueName = NULL;
return MERGE_LEAVE;
}
MERGE_RESULT
pDetectRootKeyType (
IN PMERGE_STATE State
)
/*++
Routine Description:
pDetectRootKeyType identifies CLSID in the root of HKCR, and when found, the CLSID
subkey is processed with the CLSID_BASE context.
Arguments:
State - Specifies the enumeration state.
Return Value:
MERGE_ERROR - An error occurred
MERGE_CONTINUE - The subkey was processed
MERGE_NOP - The subkey was not processed
--*/
{
MYASSERT (State->FullKeyName);
MYASSERT (State->FullSubKeyName);
MYASSERT (State->SubKeyName);
MYASSERT (State->SubKey95);
MYASSERT (State->MergeFlag == MERGE_FLAG_SUBKEY);
MYASSERT (State->Context == ROOT_BASE);
if (StringIMatch (State->SubKeyName, TEXT("CLSID"))) {
//
// This is the CLSID key; copy with CLSID_BASE
//
if (!MergeSubKeyNode (State, CLSID_BASE)) {
return MERGE_ERROR;
}
//
// Copy the values (usually there are none)
//
if (!MergeRegistryNodeEx (
State->FullKeyName,
State->Key95,
State->KeyNt,
KEY_COPY,
MERGE_FLAG_VALUE
)) {
return MERGE_ERROR;
}
return MERGE_CONTINUE;
} else if (StringIMatch (State->SubKeyName, TEXT("TYPELIB"))) {
//
// Copy the TypeLib subkey (its values and all of its subkeys)
//
if (!MergeSubKeyNode (State, TYPELIB_BASE) ||
!CopyAllSubKeyValues (State)
) {
return MERGE_ERROR;
}
return MERGE_CONTINUE;
} else if (StringIMatch (State->SubKeyName, TEXT("Interface"))) {
//
// Copy the Interface, then copy the values (usually none)
//
if (!MergeSubKeyNode (State, INTERFACE_BASE)) {
return MERGE_ERROR;
}
if (!CopyAllSubKeyValues (State)) {
return MERGE_ERROR;
}
return MERGE_CONTINUE;
}
return MERGE_NOP;
}
BOOL
pIsNtClsIdOverwritable (
IN HKEY Key
)
{
REGKEY_ENUM e;
BOOL Overwritable = TRUE;
HKEY InstanceSubKey;
//
// Enumerate the subkeys. If there is a subkey that has a binary
// implementation, then do not overwrite the key.
//
if (!Key) {
return TRUE;
}
if (EnumFirstRegKey (&e, Key)) {
do {
if (StringIMatchCharCount (e.SubKeyName, TEXT("Inproc"), 6) ||
StringIMatch (e.SubKeyName, TEXT("LocalServer")) ||
StringIMatch (e.SubKeyName, TEXT("LocalServer32")) ||
StringIMatch (e.SubKeyName, TEXT("ProxyStubClsid32"))
) {
Overwritable = FALSE;
break;
}
if (StringIMatch (e.SubKeyName, TEXT("Instance"))) {
break;
}
} while (EnumNextRegKey (&e));
}
if (!Overwritable) {
//
// if we think a key is not overwritable, we have
// to test for a subkey Instance. If it exists, then we
// consider the key overwritable. This is for ActiveMovie,
// and unfortunately it has the potential of breaking anyone
// in NT who puts an Instance key in their CLSID. Since
// ActiveMovie plug-ins use this key, we're stuck.
//
// Fortunately, nobody in NT does this currently.
//
InstanceSubKey = OpenRegKey (Key, TEXT("Instance"));
if (InstanceSubKey) {
CloseRegKey (InstanceSubKey);
Overwritable = TRUE;
}
return Overwritable;
}
return TRUE;
}
MERGE_RESULT
pCopyClassId (
IN PMERGE_STATE State
)
/*++
Routine Description:
pCopyClassId performs a copy of an HKCR\CLSID\* key. It copies the entire
key to NT if NT does not provide an equivalent setting. In all cases, the
friendly name is copied to NT, because it may have been modified on Win9x.
However, we don't copy the default value when we are talking about a suppressed
GUID and NT does not install this GUID.
This function is called for all subkeys of CLSID. The subkey name is either
a GUID or garbage.
Arguments:
State - Specifies the enumeration state, which is always a subkey of
HKCR\CLSID in this case.
Return Value:
MERGE_CONTINUE - Key was processed
MERGE_ERROR - An error occurred
--*/
{
TCHAR Node[MEMDB_MAX];
TCHAR DefaultIconKey[MAX_REGISTRY_KEY];
HKEY DefaultIcon95;
BOOL Copied = FALSE;
PTSTR defaultValue = NULL;
MYASSERT (State->FullKeyName);
MYASSERT (State->FullSubKeyName);
MYASSERT (State->SubKeyName);
MYASSERT (State->SubKey95);
MYASSERT (State->MergeFlag == MERGE_FLAG_SUBKEY);
MYASSERT (State->Context == CLSID_BASE);
//
// Skip if the GUID is suppressed
//
MemDbBuildKey (Node, MEMDB_CATEGORY_GUIDS, State->SubKeyName, NULL, NULL);
if (!MemDbGetValue (Node, NULL)) {
//
// Copy entire Win9x setting if GUID does not exist on NT.
// If GUID exists on NT, do not touch it.
//
if (pIsNtClsIdOverwritable (State->SubKeyNt)) {
Copied = TRUE;
if (!pMakeSureNtSubKeyExists (State)) {
LOG ((LOG_ERROR, "Can't create %s", State->FullSubKeyName));
} else {
//
// Copy the specific CLSID key from 95 to NT
//
if (!MergeSubKeyNode (State, CLSID_COPY)) {
return MERGE_ERROR;
}
}
}
ELSE_DEBUGMSG ((DBG_VERBOSE, "CLSID %s is not overwritable", State->SubKeyName));
}
if (!Copied) {
if (State->SubKeyNt) {
defaultValue = (PTSTR) GetRegValueData95 (State->SubKey95, S_EMPTY);
if (defaultValue && *defaultValue) {
//
// If ClsId is suppressed but NT installs the GUID, we want to copy
// the default value and the default icon for GUID.
//
// (This is the class friendly name.)
//
if (!MergeRegistryNodeEx (
State->FullSubKeyName,
State->SubKey95,
State->SubKeyNt,
COPY_DEFAULT_VALUE,
MERGE_FLAG_KEY
)) {
return MERGE_ERROR;
}
}
if (defaultValue) {
MemFree (g_hHeap, 0, defaultValue);
}
StringCopy (DefaultIconKey, State->FullSubKeyName);
StringCopy (AppendWack (DefaultIconKey), TEXT("DefaultIcon"));
DefaultIcon95 = OpenRegKeyStr95 (DefaultIconKey);
if (DefaultIcon95) {
CloseRegKey95 (DefaultIcon95);
if (!MergeRegistryNode (DefaultIconKey, COPY_DEFAULT_ICON)) {
return MERGE_ERROR;
}
}
}
}
return MERGE_CONTINUE;
}
MERGE_RESULT
pCopyClassIdWorker (
IN PMERGE_STATE State
)
/*++
Routine Description:
pCopyClassIdWorker handles one CLSID entry. It processses all the values
of the entry, and all subkeys. This routine looks for the special cases of
CLSID. If none are found, the entire key is copied (unless NT provides the
key). If a special case is found, the root is changed for the special
case.
The key is HKCR\CLSID\<guid>.
The subkey is HKCR\CLSID\<guid>\<subkey>
We have already determined that <guid> is not suppressed.
Arguments:
State - Specifies the enumeration state, which is the subkey of HKCR\CLSID,
or the subkey of HKCR\CLSID\<guid>.
Return Value:
MERGE_CONTINUE - Key was processed
MERGE_ERROR - An error occurred
--*/
{
MYASSERT (State->FullKeyName);
MYASSERT (State->FullSubKeyName || State->MergeFlag == MERGE_FLAG_KEY);
MYASSERT (State->SubKeyName || State->MergeFlag == MERGE_FLAG_KEY);
MYASSERT (State->SubKey95 || State->MergeFlag == MERGE_FLAG_KEY);
MYASSERT (State->Context == CLSID_COPY);
switch (State->MergeFlag) {
case MERGE_FLAG_KEY:
//
// For MERGE_FLAG_KEY, copy all the values
//
CopyAllValues (State);
break;
case MERGE_FLAG_SUBKEY:
//
// For MERGE_FLAG_SUBKEY, copy unless it needs a special-case merge
//
if (StringIMatch (State->SubKeyName, TEXT("Instance"))) {
//
// The subkey is Instance, perform a special-case merge
//
if (!MergeSubKeyNode (State, CLSID_INSTANCE_COPY)) {
return MERGE_ERROR;
}
} else {
//
// Copy the key unconditionally
//
if (!CopyEntireSubKey (State)) {
return MERGE_ERROR;
}
}
break;
default:
DEBUGMSG ((DBG_WHOOPS, "Wasteful call to pCopyClassIdWorker"));
break;
}
return MERGE_CONTINUE;
}
MERGE_RESULT
pInstanceSpecialCase (
IN PMERGE_STATE State
)
/*++
Routine Description:
pInstanceSpecialCase handles the Instance subkey of arbitrary GUIDs. This
is used by ActiveMovie to track third-party plug-ins. This routine
examines the format of Instance (specific to ActiveMove), and copies only
parts of the key that are compatible with NT and not
replaced.
The Key refers to HKCR\CLSID\<guid>\Instance.
The SubKey refers to HKCR\CLSID\<guid>\Instance\<sequencer>
We have already determined that <guid> is not suppressed.
Arguments:
State - Specifies the HKCR state.
Return Value:
MERGE_CONTINUE - Key was processed
MERGE_ERROR - An error occurred
--*/
{
TCHAR Guid[MAX_GUID];
TCHAR Node[MEMDB_MAX];
LONG rc;
DWORD Size;
MYASSERT (State->FullKeyName);
MYASSERT (State->FullSubKeyName || State->MergeFlag == MERGE_FLAG_KEY);
MYASSERT (State->SubKeyName || State->MergeFlag == MERGE_FLAG_KEY);
MYASSERT (State->SubKey95 || State->MergeFlag == MERGE_FLAG_KEY);
MYASSERT (State->Context == CLSID_INSTANCE_COPY);
switch (State->MergeFlag) {
case MERGE_FLAG_KEY:
//
// Copy all values (normally there are none)
//
CopyAllValues (State);
break;
case MERGE_FLAG_SUBKEY:
//
// The subkey is a random enumerator (usually a GUID -- but it is not defined
// to be so). Look at the CLSID value of the subkey, then check the GUID
// (the value data) against the suppress list.
//
//
// Was that random enumerator installed by NT? If so, ignore the Win9x
// setting.
//
if (State->SubKeyNt) {
break;
}
//
// Get GUID and see if it is suppressed
//
Size = sizeof (Guid);
rc = Win95RegQueryValueEx (
State->SubKey95,
TEXT("CLSID"),
NULL,
NULL,
(PBYTE) Guid,
&Size
);
if (rc != ERROR_SUCCESS) {
//
// No CLSID value; copy entire subkey unaltered
//
if (!CopyEntireSubKey (State)) {
return MERGE_ERROR;
}
}
MemDbBuildKey (Node, MEMDB_CATEGORY_GUIDS, Guid, NULL, NULL);
if (!MemDbGetValue (Node, NULL)) {
//
// GUID is not suppressed; copy entire subkey unaltered
//
if (!CopyEntireSubKey (State)) {
return MERGE_ERROR;
}
}
ELSE_DEBUGMSG ((DBG_VERBOSE, "Suppressing ActiveMovie Instance GUID: %s", Guid));
break;
default:
DEBUGMSG ((DBG_WHOOPS, "Wasteful call to pInstanceSpecialCase"));
break;
}
return MERGE_CONTINUE;
}
MERGE_RESULT
pCopyTypeLibOrInterface (
IN PMERGE_STATE State
)
/*++
Routine Description:
pCopyTypeLibOrInterface copies the COM type registration and COM interfaces.
Arguments:
State - Specifies the enumeration state, which is always a subkey of
HKCR\TypeLib in this case.
Return Value:
MERGE_CONTINUE - Key was processed
MERGE_ERROR - An error occurred
--*/
{
TCHAR Node[MEMDB_MAX];
MYASSERT (State->FullKeyName);
MYASSERT (State->FullSubKeyName);
MYASSERT (State->SubKeyName);
MYASSERT (State->SubKey95);
MYASSERT (State->MergeFlag == MERGE_FLAG_SUBKEY);
if (State->Context != TYPELIB_BASE &&
State->Context != INTERFACE_BASE
) {
return MERGE_NOP;
}
//
// Skip if the GUID is suppressed
//
MemDbBuildKey (Node, MEMDB_CATEGORY_GUIDS, State->SubKeyName, NULL, NULL);
if (!MemDbGetValue (Node, NULL)) {
if (State->Context == TYPELIB_BASE) {
//
// If this is a typelib entry, use additional typelib logic
//
if (!MergeSubKeyNode (State, TYPELIB_VERSION_COPY) ||
!CopyAllSubKeyValues (State)
) {
return MERGE_ERROR;
}
} else {
//
// For the Interface entries, copy entire Win9x setting if
// GUID does not exist on NT. If GUID exists on NT, do not
// touch it.
//
if (!State->SubKeyNt) {
if (!CopyEntireSubKey (State)) {
return MERGE_ERROR;
}
}
}
}
return MERGE_CONTINUE;
}
MERGE_RESULT
pCopyTypeLibVersion (
IN PMERGE_STATE State
)
/*++
Routine Description:
pCopyTypeLibVersion copies the type registration for a specific
interface version. It only copies if the particular version
is not installed by NT.
This function is called only for subkeys within a TypeLib\{GUID}
key.
Arguments:
State - Specifies the enumeration state, which is always a subkey of
HKCR\TypeLib in this case.
Return Value:
MERGE_CONTINUE - Key was processed
MERGE_ERROR - An error occurred
--*/
{
MYASSERT (State->FullKeyName);
MYASSERT (State->MergeFlag == MERGE_FLAG_SUBKEY);
MYASSERT (State->Context == TYPELIB_VERSION_COPY);
MYASSERT (State->FullSubKeyName);
MYASSERT (State->SubKeyName);
MYASSERT (State->SubKey95);
//
// Skip if the sub key exists in NT, copy the entire thing otherwise
//
if (!State->SubKeyNt) {
if (!CopyEntireSubKey (State)) {
return MERGE_ERROR;
}
}
return MERGE_CONTINUE;
}
MERGE_RESULT
pLastMergeRoutine (
IN PMERGE_STATE State
)
/*++
Routine Description:
pLastMergeRoutine performs a default copy with no overwrite for all keys
left unhandled. This routine first verifies the context is a base context
(such as ROOT_BASE or CLSID_BASE), and if so, MergeRegistryNode is called
recursively to perform the merge.
Arguments:
State - Specifies the enumeration state
Return Value:
MERGE_NOP - The subkey was not processed
MERGE_ERROR - An error occurred
MERGE_CONTINUE - The key was merged
--*/
{
TCHAR DefaultIconKey[MAX_REGISTRY_KEY];
HKEY DefaultIcon95;
MYASSERT (State->FullKeyName);
MYASSERT (State->FullSubKeyName);
MYASSERT (State->SubKeyName);
MYASSERT (State->SubKey95);
MYASSERT (State->MergeFlag == MERGE_FLAG_SUBKEY);
//
// Process only base contexts
//
if (State->Context != ROOT_BASE &&
State->Context != CLSID_BASE
) {
return MERGE_NOP;
}
//
// If we got here, nobody wants to handle the current key,
// so let's copy it without overwriting NT keys.
//
if (!CopyEntireSubKeyNoOverwrite (State)) {
return MERGE_ERROR;
}
//
// Special case: If ROOT_BASE, and subkey has a DefaultIcon subkey,
// run the DefaultIcon processing.
//
if (State->Context == ROOT_BASE) {
StringCopy (DefaultIconKey, State->FullSubKeyName);
StringCopy (AppendWack (DefaultIconKey), TEXT("DefaultIcon"));
DefaultIcon95 = OpenRegKeyStr95 (DefaultIconKey);
if (DefaultIcon95) {
CloseRegKey95 (DefaultIcon95);
if (!MergeRegistryNode (DefaultIconKey, COPY_DEFAULT_ICON)) {
return MERGE_ERROR;
}
}
}
return MERGE_CONTINUE;
}