/*++

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;
    MERGE_RESULT result = MERGE_NOP;

    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)) {
                        result = MERGE_ERROR;
                        __leave;
                    }

                }

            }
        } __finally {

            if (p) {
                FreePathString (p);
            }

            if (dataNt) {
                MemFree (g_hHeap, 0, dataNt);
            }

            if (data9x) {
                MemFree (g_hHeap, 0, data9x);
            }
        }

    }

    return result;
}


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 (StringIMatchTcharCount (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;
}