/*++ Copyright (c) 1999 Microsoft Corporation Module Name: iniact.c Abstract: This module contains the implementation of the engine and actions on INI files. To add a new INI action, just add it to wkstamig.inf or usermig.inf, add it to INI_ACTIONS macro list and implement a function with the same name having FNINIACT prototype. Author: Ovidiu Temereanca (ovidiut) 07-May-1999 Environment: GUI mode Setup. Revision History: 07-May-1999 ovidiut Creation and initial implementation. --*/ // // includes // #include "pch.h" #include "migmainp.h" #ifdef DEBUG #define DBG_INIACT "IniAct" #endif // // GUID Format: {%08lX-%04X-%04X-%02X%02X-%02X%02X%02X%02X%02X%02X} // we care about the exact length of this string // #define GUIDSTR_LEN (1 + 8 + 1 + 4 + 1 + 4 + 1 + 4 + 1 + 12 + 1) #define DASH_INDEXES 1+8, 1+8+1+4, 1+8+1+4+1+4, 1+8+1+4+1+4+1+4 // // Add a macro here with an INI Action function name and implement it. // Make sure wkstamig.inf or usermig.inf use the same function name in [INI Files Actions]. // See FNINIACT definition for the function prototype // #define INI_ACTIONS \ DEFMAC (MigrateDesktopIniSCI) \ DEFMAC (MigrateDesktopIniESFV) \ // // Private prototypes // // // description of rule's settings // typedef struct { // // INI file specification, as appears in INF files (Field 1) // PCTSTR IniSpec; // // Section specified in INF (Field 2) // PCTSTR Section; // // Key specified in INF (Field 3) // PCTSTR Key; // // Data specified in INF (Field 4) // PCTSTR Data; // // Function-dependent strings defined in INF; // all strings from section named in Field 5 // the strings are double-zero terminated // GROWBUFFER Settings; } RULEATTRIBS, *PRULEATTRIBS; // // description of an INI file (original, actual, NT location) // typedef struct { // // original (Win9x) INI file location // PCTSTR OrigIniPath; // // actual INI file location (it was copied to a temp location) // PCTSTR ActualLocation; // // NT file location; it may be different than Win9x location // PCTSTR NtIniPath; } INIFILE, *PINIFILE; // // the prototype of an INI file processing function // typedef BOOL (FNINIACT) ( IN PRULEATTRIBS RuleAttribs, IN PINIFILE IniFile ); typedef FNINIACT* PFNINIACT; // // description of an INI action (there is a list of actions) // typedef struct _INIACT { // // it's a list of actions // struct _INIACT* Next; // // processing function name (Key field in INF) // PCTSTR FnName; // // a pointer to the processing function // PFNINIACT FnIniAct; // // the attributes of this rule as defined in INF + context // RULEATTRIBS RuleAttribs; } INIACT, *PINIACT; // // this serves as a map from function name to function pointer // typedef struct { PCTSTR FnName; PFNINIACT Fn; } INIACTMAP, *PINIACTMAP; // // global data // // // memory pool used by IniActions // static POOLHANDLE g_IniActPool = NULL; // // the list of rules // static PINIACT g_IniActHead = NULL, g_IniActTail = NULL; // // function declarations // #define DEFMAC(Name) FNINIACT Name; INI_ACTIONS #undef DEFMAC // // map function name -> function pointer // #define DEFMAC(Name) TEXT(#Name), Name, static INIACTMAP g_IniActionsMapping[] = { INI_ACTIONS NULL, NULL }; #undef DEFMAC BOOL pLookupRuleFn ( IN OUT PINIACT IniAct ) /*++ Routine Description: pLookupRuleFn tries to find the function specified in IniAct->FnName and put the pointer in IniAct->FnIniAct. It will look in the global map g_IniActionsMapping. Arguments: IniAct - Specifies the function name and receives the function pointer. Return Value: TRUE if the function was found, FALSE otherwise --*/ { INT i; for (i = 0; g_IniActionsMapping[i].FnName; i++) { if (StringMatch (g_IniActionsMapping[i].FnName, IniAct->FnName)) { IniAct->FnIniAct = g_IniActionsMapping[i].Fn; return TRUE; } } return FALSE; } PCTSTR pGetNextMultiSzString ( IN PCTSTR Str ) /*++ Routine Description: pGetNextMultiSzString skips over the string specified to get to the next string, assumed to be in contiguous memory. Arguments: Str - Specifies the string to skip over Return Value: A pointer to the caracter following the string (starting of the next one). --*/ { return (PCTSTR) (((PBYTE)Str) + SizeOfString (Str)); } VOID pGetRuleSectionSettings ( IN OUT PINIACT IniAct, IN HINF Inf, IN PCTSTR Section ) /*++ Routine Description: pGetRuleSectionSettings reads all settings from specified Inf file and specified section and appends them to IniAct->RuleAttribs.Settings Arguments: IniAct - Receives the strings read Inf - Specifies the source INF file Section - Specifies the section containing the strings Return Value: none --*/ { INFCONTEXT ctx; TCHAR field[MEMDB_MAX]; if (SetupFindFirstLine (Inf, Section, NULL, &ctx)) { do { if (SetupGetStringField (&ctx, 0, field, MEMDB_MAX, NULL)) { MultiSzAppend (&IniAct->RuleAttribs.Settings, field); } } while (SetupFindNextLine (&ctx, &ctx)); } } BOOL pGetIniActData ( IN OUT PINFCONTEXT ctx, OUT PINIACT IniAct ) /*++ Routine Description: pGetIniActData reads all rule settings from the specified INF context and puts them in IniAct Arguments: ctx - Specifies the INF context containing the attributes of this rule; receives new context data IniAct - Receives the data read Return Value: TRUE if attributes read are valid and they make up a valid rule --*/ { TCHAR field[MEMDB_MAX]; TCHAR FileSpec[MAX_PATH]; if (!(SetupGetStringField (ctx, 0, field, MEMDB_MAX, NULL) && field[0])) { DEBUGMSG (( DBG_ASSERT, "pGetIniActData: couldn't get function name in Wkstamig.inf" )); MYASSERT (FALSE); return FALSE; } IniAct->FnName = DuplicateText (field); // // lookup handling function // if (!pLookupRuleFn (IniAct)) { DEBUGMSG (( DBG_ASSERT, "pGetIniActData: couldn't find implementation of function [%s] in Wkstamig.inf", IniAct->FnName )); MYASSERT (FALSE); return FALSE; } if (!(SetupGetStringField (ctx, 1, field, MEMDB_MAX, NULL) && field[0])) { DEBUGMSG (( DBG_ASSERT, "pGetIniActData: couldn't get INI file spec in Wkstamig.inf" )); MYASSERT (FALSE); return FALSE; } // // expand env vars first // if (ExpandEnvironmentStrings (field, FileSpec, MAX_PATH) <= MAX_PATH) { // // there shouldn't be any % left // if (_tcschr (FileSpec, TEXT('%'))) { DEBUGMSG (( DBG_ASSERT, "pGetIniActData: invalid INI file spec in Wkstamig.inf" )); MYASSERT (FALSE); return FALSE; } } else { DEBUGMSG (( DBG_ASSERT, "pGetIniActData: INI file spec too long in Wkstamig.inf" )); MYASSERT (FALSE); return FALSE; } IniAct->RuleAttribs.IniSpec = DuplicateText (FileSpec); // // rest of fields are optional // if (SetupGetStringField (ctx, 2, field, MEMDB_MAX, NULL) && field[0]) { IniAct->RuleAttribs.Section = DuplicateText (field); } if (SetupGetStringField (ctx, 3, field, MEMDB_MAX, NULL) && field[0]) { IniAct->RuleAttribs.Key = DuplicateText (field); } if (SetupGetStringField (ctx, 4, field, MEMDB_MAX, NULL) && field[0]) { IniAct->RuleAttribs.Data = DuplicateText (field); } if (SetupGetStringField (ctx, 5, field, MEMDB_MAX, NULL) && field[0]) { // // this is actually a section name in the same INF file // read its contents and make a multisz string with them // pGetRuleSectionSettings (IniAct, ctx->Inf, field); } return TRUE; } VOID pCleanUpIniAction ( IN OUT PINIACT IniAct ) /*++ Routine Description: pCleanUpIniAction frees all resources associated with the given IniAct Arguments: IniAct - Specifies the action to be "emptied"; all resources are freed Return Value: none --*/ { FreeText (IniAct->FnName); IniAct->FnName = NULL; FreeText (IniAct->RuleAttribs.IniSpec); FreeText (IniAct->RuleAttribs.Section); FreeText (IniAct->RuleAttribs.Key); FreeText (IniAct->RuleAttribs.Data); FreeGrowBuffer (&IniAct->RuleAttribs.Settings); ZeroMemory (&IniAct->RuleAttribs, sizeof (IniAct->RuleAttribs)); } BOOL pCreateIniActions ( IN INIACT_CONTEXT Context ) /*++ Routine Description: pCreateIniActions will create a list of rules read from an INF depending on the Context Arguments: Context - Specifies the context in which the function is called Return Value: TRUE if the list (defined by the globals g_IniActHead and g_IniActTail) is not empty --*/ { INFCONTEXT InfContext; PINIACT IniAct; PCTSTR Section; if (g_WkstaMigInf == INVALID_HANDLE_VALUE) { DEBUGMSG ((DBG_ERROR, "Ini Actions: wkstamig.inf is not loaded")); return FALSE; } if (Context == INIACT_WKS_FIRST) { Section = S_INIFILES_ACTIONS_FIRST; } else { Section = S_INIFILES_ACTIONS_LAST; } if (SetupFindFirstLine (g_WkstaMigInf, Section, NULL, &InfContext)) { do { IniAct = PoolMemGetMemory (g_IniActPool, sizeof (*IniAct)); ZeroMemory (IniAct, sizeof (*IniAct)); if (pGetIniActData (&InfContext, IniAct)) { // // add it to the list // if (g_IniActTail) { g_IniActTail->Next = IniAct; g_IniActTail = IniAct; } else { g_IniActHead = g_IniActTail = IniAct; } } else { pCleanUpIniAction (IniAct); PoolMemReleaseMemory (g_IniActPool, IniAct); } } while (SetupFindNextLine (&InfContext, &InfContext)); } return g_IniActHead != NULL; } VOID pFreeIniActions ( VOID ) /*++ Routine Description: pFreeIniActions destroys all rules in the global list (see g_IniActHead and g_IniActTail) Arguments: none Return Value: none --*/ { PINIACT NextRule; while (g_IniActHead) { NextRule = g_IniActHead->Next; pCleanUpIniAction (g_IniActHead); PoolMemReleaseMemory (g_IniActPool, g_IniActHead); g_IniActHead = NextRule; } g_IniActTail = NULL; } BOOL pEnumFirstIniAction ( OUT PINIACT* IniAct ) /*++ Routine Description: pEnumFirstIniAction enumerates the first rule in the global list and puts a pointer to it in IniAct Arguments: IniAct - Receives the first INI rule; NULL if none Return Value: TRUE if there is at least a rule, FALSE if list is empty --*/ { *IniAct = g_IniActHead; return *IniAct != NULL; } BOOL pEnumNextIniAction ( IN OUT PINIACT* IniAct ) /*++ Routine Description: pEnumNextIniAction enumerates the next action after IniAct in the global list and puts a pointer to it in the same IniAct Arguments: IniAct - Specifies a pointer to an INI rule; will receive a pointer to the next rule; receives NULL if last rule Return Value: TRUE if there is a rule following (*IniAct is a valid pointer), FALSE if not --*/ { if (*IniAct) { *IniAct = (*IniAct)->Next; } return *IniAct != NULL; } PTSTR pGetAllKeys ( IN PCTSTR IniFilePath, IN PCTSTR Section ) /*++ Routine Description: pGetAllKeys reads all keys or sections from the specified INI file and returns a pointer to allocated memory that contains all keys in the specified section. If section is NULL, a list of all sections is retrived instead. Arguments: IniFilePath - Specifies the INI file Section - Specifies the section containg the keys; if NULL, sections are retrieved instead of keys Return Value: A pointer to a multisz containing all keys or sections; caller must free the memory --*/ { PTSTR Keys = NULL; DWORD Size = 64 * sizeof (TCHAR); DWORD chars; MYASSERT (IniFilePath); do { if (Keys) { PoolMemReleaseMemory (g_IniActPool, Keys); } Size *= 2; Keys = PoolMemGetMemory (g_IniActPool, Size); chars = GetPrivateProfileString ( Section, NULL, TEXT(""), Keys, Size, IniFilePath ); } while (chars == Size - 2); return Keys; } PTSTR pGetKeyValue ( IN PCTSTR IniFilePath, IN PCTSTR Section, IN PCTSTR Key ) /*++ Routine Description: pGetKeyValue reads the value associated with the given key, section, INI file and returns a pointer to allocated memory that contains this value as a string. Both section and Key must not be NULL. Arguments: IniFilePath - Specifies the INI file Section - Specifies the section Key - Specifies the key Return Value: A pointer to a string containing the value; caller must free the memory --*/ { PTSTR Value = NULL; DWORD Size = 64 * sizeof (TCHAR); DWORD chars; MYASSERT (IniFilePath); MYASSERT (Section); MYASSERT (Key); do { if (Value) { PoolMemReleaseMemory (g_IniActPool, Value); } Size *= 2; Value = PoolMemGetMemory (g_IniActPool, Size); chars = GetPrivateProfileString ( Section, Key, TEXT(""), Value, Size, IniFilePath ); } while (chars == Size - 1); return Value; } BOOL pIsFileActionRule ( IN PINIACT IniAct, IN PINIFILE IniFile ) /*++ Routine Description: pIsFileActionRule determines if the specified rule applies to the whole INI file Arguments: IniAct - Specifies the INI action IniFile - Specifies the INI file Return Value: TRUE if the rule applies to the whole INI file, FALSE if not --*/ { MYASSERT (IniAct); return !IniAct->RuleAttribs.Section && !IniAct->RuleAttribs.Key; } BOOL pDoFileAction ( IN PINIACT IniAct, IN PINIFILE IniFile ) /*++ Routine Description: pDoFileAction applies the specified rule to the whole INI file Arguments: IniAct - Specifies the INI action IniFile - Specifies the INI file Return Value: the result returned by the INI action processing function on this INI file --*/ { GROWBUFFER GbKeys = GROWBUF_INIT; PTSTR Sections, Keys; PCTSTR Section, Key; BOOL Result; MYASSERT (IniAct && IniAct->FnIniAct && !IniAct->RuleAttribs.Section && !IniAct->RuleAttribs.Key); Sections = pGetAllKeys (IniFile->ActualLocation, NULL); IniAct->RuleAttribs.Section = Sections; for (Section = Sections; *Section; Section = pGetNextMultiSzString (Section)) { Keys = pGetAllKeys (IniFile->ActualLocation, Section); for (Key = Keys; *Key; Key = pGetNextMultiSzString (Key)) { MultiSzAppend (&GbKeys, Key); } PoolMemReleaseMemory (g_IniActPool, Keys); } // // end with another zero (here are 2 TCHAR zeroes...) // GrowBufAppendDword (&GbKeys, 0); IniAct->RuleAttribs.Key = (PCTSTR)GbKeys.Buf; Result = (*IniAct->FnIniAct)(&IniAct->RuleAttribs, IniFile); IniAct->RuleAttribs.Key = NULL; IniAct->RuleAttribs.Section = NULL; FreeGrowBuffer (&GbKeys); PoolMemReleaseMemory (g_IniActPool, Sections); return Result; } BOOL pIsSectionActionRule( IN PINIACT IniAct, IN PINIFILE IniFile ) /*++ Routine Description: pIsSectionActionRule determines if the specified rule applies to a section of the INI file Arguments: IniAct - Specifies the INI action IniFile - Specifies the INI file Return Value: TRUE if the rule applies to a section of the INI file, FALSE if not --*/ { MYASSERT (IniAct); return IniAct->RuleAttribs.Section && !IniAct->RuleAttribs.Key; } BOOL pDoSectionAction ( IN PINIACT IniAct, IN PINIFILE IniFile ) /*++ Routine Description: pDoSectionAction applies the specified rule to a section of the INI file Arguments: IniAct - Specifies the INI action IniFile - Specifies the INI file Return Value: the result returned by the INI action processing function --*/ { PTSTR Keys; BOOL Result; MYASSERT (IniAct && IniAct->FnIniAct && IniAct->RuleAttribs.Section && !IniAct->RuleAttribs.Key); Keys = pGetAllKeys (IniFile->ActualLocation, IniAct->RuleAttribs.Section); IniAct->RuleAttribs.Key = Keys; Result = (*IniAct->FnIniAct)(&IniAct->RuleAttribs, IniFile); IniAct->RuleAttribs.Key = NULL; PoolMemReleaseMemory (g_IniActPool, Keys); return Result; } BOOL pDoKeyAction ( IN PINIACT IniAct, IN PINIFILE IniFile ) /*++ Routine Description: pDoKeyAction applies the specified rule to a key of the INI file Arguments: IniAct - Specifies the INI action IniFile - Specifies the INI file Return Value: the result returned by the INI action processing function --*/ { MYASSERT (IniAct && IniAct->FnIniAct && IniAct->RuleAttribs.Key); return (*IniAct->FnIniAct)(&IniAct->RuleAttribs, IniFile); } BOOL pDoIniAction ( IN PINIFILE IniFile ) /*++ Routine Description: This is the actual worker routine called by pDoIniActions for each INI file to be migrated. Arguments: IniFile - Specifies the INI file Return Value: TRUE if INI migration was successful for this file, FALSE otherwise --*/ { PINIACT IniAct; BOOL Result = TRUE; BOOL b; // // check INI file against all rules; if a rule applies, do it // if (pEnumFirstIniAction (&IniAct)) { do { if (!IsPatternMatch (IniAct->RuleAttribs.IniSpec, IniFile->OrigIniPath)) { continue; } // // do the action; check for file actions first // if (pIsFileActionRule (IniAct, IniFile)) { b = pDoFileAction (IniAct, IniFile); } else { // // check section actions next // if (pIsSectionActionRule (IniAct, IniFile)) { // // do it for each section in the current file // b = pDoSectionAction (IniAct, IniFile); } else { // // do key actions last // b = pDoKeyAction (IniAct, IniFile); } } DEBUGMSG_IF (( !b, DBG_INIACT, "pDoIniActions: function [%s] failed on file [%s]", IniAct->FnName, IniFile->OrigIniPath )); Result &= b; } while (pEnumNextIniAction (&IniAct)); } return Result; } BOOL pDoIniActions ( IN INIACT_CONTEXT Context ) /*++ Routine Description: This is the actual worker routine called by DoIniActions. It may be called in different contexts. Arguments: Context - Specifies the context in which the function is called Return Value: TRUE if INI files migration was successful, FALSE otherwise --*/ { MEMDB_ENUM e; INIFILE IniFile; PCTSTR OrigIniPath; PCTSTR ActualLocation; PCTSTR NtIniPath; PCTSTR MemDbCategory; // // get all rules first // if (pCreateIniActions (Context)) { // // enum all candidates files from corresponding memdb category // if (Context == INIACT_WKS_FIRST) { MemDbCategory = MEMDB_CATEGORY_INIACT_FIRST; } else { MemDbCategory = MEMDB_CATEGORY_INIACT_LAST; } if (MemDbGetValueEx (&e, MemDbCategory, NULL, NULL)) { do { OrigIniPath = e.szName; ActualLocation = GetTemporaryLocationForFile (OrigIniPath); if (!ActualLocation) { DEBUGMSG (( DBG_ERROR, "Couldn't find temp location for INIACT key: %s\\%s", MemDbCategory, e.szName )); continue; } NtIniPath = GetPathStringOnNt (OrigIniPath); // // fill in the members of IniFile // IniFile.OrigIniPath = OrigIniPath; IniFile.ActualLocation = ActualLocation; IniFile.NtIniPath = NtIniPath; if (!pDoIniAction (&IniFile)) { DEBUGMSG (( DBG_INIACT, "Some errors occured during migration of INI file [%s] -> [%s]", OrigIniPath, NtIniPath )); } // // now convert the INI file (fix paths etc) // // ConvertIniFile (NtIniPath); FreePathString (NtIniPath); FreePathString (ActualLocation); ZeroMemory (&IniFile, sizeof (IniFile)); } while (MemDbEnumNextValue (&e)); } pFreeIniActions (); } return TRUE; } BOOL DoIniActions ( IN INIACT_CONTEXT Context ) /*++ Routine Description: This is the main routine called to perform INI files migration. It may be called several times, specifying the context. Arguments: Context - Specifies the context in which the function is called Return Value: TRUE if INI files migration was successful in that context, FALSE otherwise --*/ { BOOL b; g_IniActPool = PoolMemInitNamedPool ("IniAct"); if (!g_IniActPool) { return FALSE; } b = FALSE; __try { b = pDoIniActions (Context); } __finally { PoolMemDestroyPool (g_IniActPool); g_IniActPool = NULL; } return b; } BOOL pIsValidGuidStr ( IN PCTSTR GuidStr ) /*++ Routine Description: Determines if a GUID represented as a string has a valid representation (braces included). Arguments: GuidStr - Specifies the GUID to check; it must contain the surrounding braces Return Value: TRUE if the specified GUID is valid, or FALSE if it is not. --*/ { DWORD GuidIdx, DashIdx; BYTE DashIndexes[4] = { DASH_INDEXES }; TCHAR ch; MYASSERT (GuidStr); if (_tcslen (GuidStr) != GUIDSTR_LEN || GuidStr[0] != TEXT('{') || GuidStr[GUIDSTR_LEN - 1] != TEXT('}')) { return FALSE; } for (GuidIdx = 1, DashIdx = 0; GuidIdx < GUIDSTR_LEN - 1; GuidIdx++) { // // check all digits and dashes positions // ch = GuidStr[GuidIdx]; if (DashIdx < 4 && (BYTE)GuidIdx == DashIndexes[DashIdx]) { if (ch != TEXT('-')) { return FALSE; } DashIdx++; } else { if (ch < TEXT('0') || ch > TEXT('9')) { if (!(ch >= TEXT('A') && ch <= TEXT('F') || ch >= TEXT('a') && ch <= TEXT('f'))) { return FALSE; } } } } return TRUE; } BOOL pIsGuidSuppressed ( PCTSTR GuidStr ) /*++ Routine Description: Determines if a GUID is suppressed or not. Arguments: GuidStr - Specifies the GUID to look up, which must be valid and must contain the surrounding braces Return Value: TRUE if the specified GUID is suppressed, or FALSE if it is not. --*/ { TCHAR Node[MEMDB_MAX]; MemDbBuildKey ( Node, MEMDB_CATEGORY_GUIDS, NULL, NULL, GuidStr ); return MemDbGetValue (Node, NULL); } BOOL pIsValidShellExtClsid ( IN PCTSTR GuidStr ) /*++ Routine Description: pIsValidShellExtClsid determines if a GUID is a valid shell extension Arguments: GuidStr - Specifies the GUID to look up, which must be valid and must contain the surrounding braces Return Value: TRUE if the specified GUID is a valid shell ext, or FALSE if it is not. --*/ { #if 0 HKEY Key; LONG rc; #endif // // check if the GUID is a known bad guid // if (pIsGuidSuppressed (GuidStr)) { return FALSE; } return TRUE; // // I removed the registry check because it is not always accurate; // some GUIDS may work without being listed in S_SHELLEXT_APPROVED keys // as it's the case with the default GUID {5984FFE0-28D4-11CF-AE66-08002B2E1262} // #if 0 rc = TrackedRegOpenKeyEx ( HKEY_LOCAL_MACHINE, S_SHELLEXT_APPROVED, 0, KEY_QUERY_VALUE, &Key ); if (rc == ERROR_SUCCESS) { rc = RegQueryValueEx (Key, GuidStr, NULL, NULL, NULL, NULL); CloseRegKey (Key); } if (rc == ERROR_SUCCESS) { return TRUE; } return FALSE; #endif } BOOL pFindStrInMultiSzStrI ( IN PCTSTR Str, IN PCTSTR MultiSz ) /*++ Routine Description: pFindStrInMultiSzStrI looks for Str in a list of multi-sz; the search is case-insensitive Arguments: Str - Specifies the string to look for MultiSz - Specifies the list to be searched Return Value: TRUE if the string was found in the list, or FALSE if not. --*/ { PCTSTR p; for (p = MultiSz; *p; p = pGetNextMultiSzString (p)) { if (StringIMatch (p, Str)) { return TRUE; } } return FALSE; } BOOL pMigrateSection ( IN PCTSTR Section, IN PRULEATTRIBS RuleAttribs, IN PINIFILE IniFile ) /*++ Routine Description: pMigrateSection migrates a whole section of the INI file. Arguments: Section - Specifies section name RuleAttribs - Specifies the rule attributes which govern the migration IniFile - Specifies the INI file Return Value: TRUE if the section was transferred successfully, or FALSE if not. --*/ { PTSTR Keys; PTSTR Value; PCTSTR Key; BOOL b = TRUE; Keys = pGetAllKeys (IniFile->ActualLocation, Section); if (*Keys) { // // there are keys to transfer; first remove the entire section that will be replaced // WritePrivateProfileString ( Section, NULL, NULL, IniFile->NtIniPath ); } for (Key = Keys; *Key; Key = pGetNextMultiSzString (Key)) { Value = pGetKeyValue (IniFile->ActualLocation, Section, Key); b &= WritePrivateProfileString ( Section, Key, Value, IniFile->NtIniPath ); PoolMemReleaseMemory (g_IniActPool, Value); } PoolMemReleaseMemory (g_IniActPool, Keys); return b; } BOOL MigrateDesktopIniSCI ( IN PRULEATTRIBS RuleAttribs, IN PINIFILE IniFile ) /*++ Routine Description: MigrateDesktopIniSCI migrates desktop.ini settings in section [.ShellClassInfo]. It reads all keys and associated values within the section and writes them back to the NT version of this file. The "settings" multisz in this case represents a list of keys that must be synchronized; if no Win9x key exists, the corresponding NT key must be deleted; if the Win9x key exists, its value is copied Arguments: RuleAttribs - Specifies the rule attributes which govern the migration IniFile - Specifies the INI file Return Value: TRUE if the section was transferred successfully, or FALSE if an error occured. --*/ { PCTSTR Key, SKey, NewValue; BOOL Found; BOOL Result, b; PTSTR Win9xValue, NtValue; TCHAR Dummy[2]; DEBUGMSG (( DBG_INIACT, "Processing: %s -> %s [%s]", IniFile->OrigIniPath, IniFile->NtIniPath, RuleAttribs->Section )); Result = TRUE; // // RuleAttribs->Settings points in this case to a list of keys that // must be synchronized; if no Win9x key exists, the corresponding // NT key must be deleted; if Win9x key exists, its value is copied // for (SKey = (PCTSTR)RuleAttribs->Settings.Buf; *SKey; SKey = pGetNextMultiSzString (SKey) ) { Found = FALSE; for (Key = RuleAttribs->Key; *Key; Key = pGetNextMultiSzString (Key)) { if (StringIMatch (SKey, Key)) { Found = TRUE; break; } } if (!Found) { // // remove NT key if there is one // if (GetPrivateProfileString ( RuleAttribs->Section, SKey, TEXT(""), Dummy, 2, IniFile->NtIniPath )) { if (!WritePrivateProfileString ( RuleAttribs->Section, SKey, NULL, IniFile->NtIniPath )) { Result = FALSE; DEBUGMSG ((DBG_INIACT, "Couldn't remove NT key [%s]", SKey)); } ELSE_DEBUGMSG ((DBG_INIACT, "Removed NT key [%s]", SKey)); } } } for (Key = RuleAttribs->Key; *Key; Key = pGetNextMultiSzString (Key)) { // // for each key on Win9x, update NT value; // check for suppressed GUIDs // Win9xValue = pGetKeyValue (IniFile->ActualLocation, RuleAttribs->Section, Key); NewValue = Win9xValue; if (pIsValidGuidStr (NewValue) && pIsGuidSuppressed (NewValue)) { // // remove the key // NewValue = NULL; } NtValue = pGetKeyValue (IniFile->NtIniPath, RuleAttribs->Section, Key); if (!NewValue && *NtValue || !StringMatch (NewValue, NtValue)) { b = WritePrivateProfileString ( RuleAttribs->Section, Key, NewValue, IniFile->NtIniPath ); if (b) { DEBUGMSG (( DBG_INIACT, "Replaced key [%s] NT value [%s] with 9x value [%s]", Key, NtValue, Win9xValue)); } else { Result = FALSE; DEBUGMSG (( DBG_INIACT, "Failed to replace key [%s] NT value [%s] with 9x value [%s]", Key, NtValue, Win9xValue)); } } PoolMemReleaseMemory (g_IniActPool, Win9xValue); PoolMemReleaseMemory (g_IniActPool, NtValue); } return Result; } BOOL MigrateDesktopIniESFV ( IN PRULEATTRIBS RuleAttribs, IN PINIFILE IniFile ) /*++ Routine Description: MigrateDesktopIniESFV migrates desktop.ini settings in section [ExtShellFolderViews]. It reads all keys and associated values within the section and writes them back to the NT version of this file. The "settings" multisz is not interpreted in this case. Arguments: RuleAttribs - Specifies the rule attributes which govern the migration IniFile - Specifies the INI file Return Value: TRUE if the section was transferred successfully, or FALSE if an error occured. --*/ { PCTSTR ViewID; BOOL b, Result; DWORD chars; PTSTR Win9xValue; TCHAR DefaultViewID[GUIDSTR_LEN + 2]; BOOL ReplaceDefViewID = FALSE; PTSTR NtValue; #ifdef DEBUG TCHAR NtViewID[GUIDSTR_LEN + 2]; #endif Result = TRUE; DEBUGMSG (( DBG_INIACT, "Processing: %s -> %s [%s]", IniFile->OrigIniPath, IniFile->NtIniPath, RuleAttribs->Section )); // // get the default view id // chars = GetPrivateProfileString ( RuleAttribs->Section, S_DEFAULT, TEXT(""), DefaultViewID, GUIDSTR_LEN + 2, IniFile->ActualLocation ); if (*DefaultViewID && chars != GUIDSTR_LEN || !pIsValidShellExtClsid (DefaultViewID)) { // // invalid view id // DEBUGMSG (( DBG_INIACT, "Invalid Default ViewID [%s]; will not be processed", DefaultViewID )); *DefaultViewID = 0; } for (ViewID = RuleAttribs->Key; *ViewID; ViewID = pGetNextMultiSzString (ViewID)) { // // except for Default={ViewID}, // all the other lines in this section should have the format {ViewID}=value // for each {ViewID} there is a section with the same name // keeping other keys (attributes of that shell view) // if (StringIMatch (ViewID, S_DEFAULT)) { continue; } if (pIsValidGuidStr (ViewID) && pIsValidShellExtClsid (ViewID)) { // // transfer the whole GUID section, if it's not one that shouldn't be migrated // a list of GUIDS that shouldn't be migrated is in RuleAttribs->Settings // if (!pFindStrInMultiSzStrI (ViewID, (PCTSTR)RuleAttribs->Settings.Buf)) { b = pMigrateSection (ViewID, RuleAttribs, IniFile); if (b) { DEBUGMSG ((DBG_INIACT, "Successfully migrated section [%s]", ViewID)); if (*DefaultViewID && !StringIMatch (ViewID, DefaultViewID)) { ReplaceDefViewID = TRUE; } // // set {ViewID}=value in NT desktop.ini // NtValue = pGetKeyValue (IniFile->NtIniPath, RuleAttribs->Section, ViewID); Win9xValue = pGetKeyValue ( IniFile->ActualLocation, RuleAttribs->Section, ViewID ); if (!StringIMatch (NtValue, Win9xValue)) { b = WritePrivateProfileString ( RuleAttribs->Section, ViewID, Win9xValue, IniFile->NtIniPath ); DEBUGMSG_IF (( b, DBG_INIACT, "Replaced key [%s] NT value [%s] with 9x value [%s]", ViewID, NtValue, Win9xValue)); } else { b = TRUE; } PoolMemReleaseMemory (g_IniActPool, Win9xValue); PoolMemReleaseMemory (g_IniActPool, NtValue); } ELSE_DEBUGMSG ((DBG_INIACT, "Section [%s] was not migrated successfully", ViewID)); // // update global result // Result &= b; } } ELSE_DEBUGMSG ((DBG_INIACT, "Invalid ShellExtViewID: [%s]; will not be processed", ViewID)); } if (ReplaceDefViewID) { // // replace NT default view with Win9x default view // #ifdef DEBUG GetPrivateProfileString ( RuleAttribs->Section, S_DEFAULT, TEXT(""), NtViewID, GUIDSTR_LEN + 2, IniFile->NtIniPath ); #endif b = WritePrivateProfileString ( RuleAttribs->Section, S_DEFAULT, DefaultViewID, IniFile->NtIniPath ); DEBUGMSG_IF (( b, DBG_INIACT, "Replaced default NT ViewID [%s] with Default Win9x ViewID [%s]", NtViewID, DefaultViewID)); Result &= b; } return Result; }