/*++ 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\. The subkey is HKCR\CLSID\\ We have already determined that is not suppressed. Arguments: State - Specifies the enumeration state, which is the subkey of HKCR\CLSID, or the subkey of HKCR\CLSID\. 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\\Instance. The SubKey refers to HKCR\CLSID\\Instance\ We have already determined that 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; }