/* REGUTILS.C Resident Code Segment // Tweak: make non-resident? Routines for reading and writing to the system registry and our .THM files. GatherThemeToFile() ApplyThemeFile() Frosting: Master Theme Selector for Windows '95 Copyright (c) 1994-1999 Microsoft Corporation. All rights reserved. */ // --------------------------------------------- // Brief file history: // Alpha: // Beta: // Bug fixes // --------- // // --------------------------------------------- // // Critique 2/1/95 jdk at dvw // // The design intends to have everything work transparently from // the KEYS.H file. The tables there specify all of the fields // in the registry that we want to save to a theme file and to set // from a theme file, along with flags for how to treat each field, // etc. It works in nice abstract loops that just save/set what // you tell it. You can add and change elements that you care to // have in the theme by adjusting the KEYS.H file, without touching // your code. Clean. // // Unfortunately, once you've set everything in the registry, you // still need to hand-code how you make many of the elements take // effect in the system in the current user session. This involves // wildly divergent APIs/parameters. This blows the abstraction noted // above. Everytime you change something in the KEYS.H file, you have // to hand-code changes here, too. // // I've isolated the non-abstract, item-specific code in HandPumpSystem() // below. Looking back on it now, there is some redundancy here. If // we started by doing everything by hand, then we could do the registry // and system settings together. Now you may end up reading, writing, // and later rereading the same string -- with attendant Reg open/closes // as well. #include "windows.h" #include "frost.h" #include "global.h" #include "keys.h" // only this files refers to list of keys #include "shlobj.h" // for SHChangeNotify() and flag #include "loadimag.h" #include "Bkgd.h" #include "adutil.h" #include "schedule.h" #include "mmsystem.h" // Stuff in bkgd.c extern void GetPlusBitmapName(LPTSTR szPlus); extern HBITMAP LoadWallpaper(LPTSTR szWallpaper, LPTSTR szTheme, BOOL fPreview); // Externs in NC.C extern BOOL FAR GatherIconMetricsByHand(); extern BOOL FAR GatherNonClientMetricsByHand(); extern VOID FAR SetIconMetricsByHand(BOOL, BOOL); extern VOID FAR SetNonClientMetricsByHand(BOOL, BOOL); // Local Routines BOOL GatherSubkeys(HKEY, FROST_SUBKEY *, int, LPTSTR); BOOL DatumRegisterToFile(HKEY, FROST_VALUE, LPTSTR, LPTSTR); BOOL FAR WriteBytesToFile(LPTSTR, LPTSTR, BYTE *, int, LPTSTR); BOOL ApplySubkeys(HKEY, FROST_SUBKEY *, int, LPTSTR, BOOL); BOOL DatumFileToRegister(HKEY, FROST_VALUE, LPTSTR, LPTSTR, BOOL); BOOL WriteBytesToRegister(HKEY, LPTSTR, int, LPTSTR); int FAR WriteBytesToBuffer(LPTSTR); BOOL IsTrashFull(); BOOL ApplyCurrentTrash(BOOL, LPTSTR); BOOL HandPumpSystem(); BOOL GatherSysColorsByHand(LPTSTR); BOOL SetSysColorsByHand(); BOOL GatherWallpaperBitsByHand(LPTSTR); VOID AbstractPath(LPTSTR, int); BOOL GatherICONS(LPCTSTR); BOOL ApplyWebView(LPCTSTR); BOOL GatherWebView(LPCTSTR); BOOL ExtractWVResource(LPCTSTR, LPCTSTR); VOID ExpandSZ(LPTSTR); extern TCHAR szCursors[]; #define DEF_SCREENSAVETIMEOUT 15 * 60 // default time to screen saver in seconds // // Local Globals // TCHAR pValue[MAX_VALUELEN+1]; // multi-use buffer: char, hex string, etc. BOOL bReadOK, bWroteOK; // Save: read from reg/sys, write to file // Apply: not implemented since ignoring results anyway // strings for grody screen saver case TCHAR szSS_Section[] = TEXT("boot"); TCHAR szSS_Key[] = TEXT("SCRNSAVE.EXE"); TCHAR szSS_File[] = TEXT("SYSTEM.INI"); // this worked in disp cpl code with no path.... TCHAR szCP_Clr[] = TEXT("Control Panel\\Colors"); TCHAR szCP_Appearance[] = TEXT("Control Panel\\Appearance"); TCHAR szCP_SoundSchemes[] = TEXT("AppEvents\\Schemes"); extern TCHAR szCP_DT[]; TCHAR szSS_Active[] = TEXT("ScreenSaveActive"); TCHAR szCurrent[] = TEXT("Current"); extern TCHAR szTileWP[]; TCHAR szWPStyle[] = TEXT("WallpaperStyle"); // // For DoSysColors() and GetThemeColors() and others // // Important that these two arrays are the same length, and that they // are kept in the same order together during any change. // // SYNCHRONIZATION ALERT! -- Keep INDEX_* defines in FROST.H in // sync with this array. Keep NUM_COLORS define in FAKEWIN.H // in sync with this array. TCHAR *pRegColors[] = { TEXT("ActiveTitle"), TEXT("Background"), TEXT("Hilight"), TEXT("HilightText"), TEXT("TitleText"), TEXT("Window"), TEXT("WindowText"), TEXT("Scrollbar"), TEXT("InactiveTitle"), TEXT("Menu"), TEXT("WindowFrame"), TEXT("MenuText"), TEXT("ActiveBorder"), TEXT("InactiveBorder"), TEXT("AppWorkspace"), TEXT("ButtonFace"), TEXT("ButtonShadow"), TEXT("GrayText"), TEXT("ButtonText"), TEXT("InactiveTitleText"), TEXT("ButtonHilight"), TEXT("ButtonDkShadow"), TEXT("ButtonLight"), TEXT("InfoText"), TEXT("InfoWindow"), // These next two are bogus -- just here to pad the array. They // should be something like the "ButtonAlternateFace" (not sure // about that one) and "HotTrackingColor". TEXT("GradientActiveTitle"), TEXT("GradientInactiveTitle"), // These next two are the real deal for the gradient title bars TEXT("GradientActiveTitle"), TEXT("GradientInactiveTitle") }; int iSysColorIndices[] = { COLOR_ACTIVECAPTION, COLOR_DESKTOP, COLOR_HIGHLIGHT, COLOR_HIGHLIGHTTEXT, COLOR_CAPTIONTEXT, COLOR_WINDOW, COLOR_WINDOWTEXT, COLOR_SCROLLBAR, COLOR_INACTIVECAPTION, COLOR_MENU, COLOR_WINDOWFRAME, COLOR_MENUTEXT, COLOR_ACTIVEBORDER, COLOR_INACTIVEBORDER, COLOR_APPWORKSPACE, COLOR_3DFACE, COLOR_3DSHADOW, COLOR_GRAYTEXT, COLOR_BTNTEXT, COLOR_INACTIVECAPTIONTEXT, COLOR_3DHILIGHT, COLOR_3DDKSHADOW, COLOR_3DLIGHT, COLOR_INFOTEXT, COLOR_INFOBK, // These next two are bogus -- just here to pad the array. They // should be something like the "COLOR_3DFACE" (not sure about // that one) and "COLOR_HOTLIGHT". COLOR_GRADIENTACTIVECAPTION, COLOR_GRADIENTINACTIVECAPTION, // These next two are the real deal for the gradient title bars COLOR_GRADIENTACTIVECAPTION, COLOR_GRADIENTINACTIVECAPTION }; __inline int _atoi(char *sz) { int i=0; while (*sz && *sz >= '0' && *sz <= '9') i = i*10 + *sz++ -'0'; return i; } // // GetRegString // void GetRegString(HKEY hkey, LPCTSTR szKey, LPCTSTR szValue, LPCTSTR szDefault, LPTSTR szBuffer, UINT cbBuffer) { if (szDefault) lstrcpy(szBuffer, szDefault); else szBuffer[0] = 0; if (RegOpenKey(hkey, szKey, &hkey) == 0) { RegQueryValueEx(hkey, szValue, NULL, NULL, (LPBYTE)szBuffer, &cbBuffer); RegCloseKey(hkey); } } // // GetRegInt // int GetRegInt(HKEY hkey, LPCTSTR szKey, LPCTSTR szValue, int def) { TCHAR ach[40]; #ifdef UNICODE CHAR szTempA[40]; #endif GetRegString(hkey, szKey, szValue, NULL, ach, sizeof(ach)); if (ach[0]) #ifdef UNICODE // Need to do conversion to ANSI for _atoi to work. { wcstombs(szTempA, (wchar_t *)ach, sizeof(szTempA)); return _atoi(szTempA); } #else // !UNICODE return _atoi(ach); #endif else return def; } // // GatherThemeToFile // // This is one of the workhorse routines of the package. // For the whole list of theme items that we care about, go check the // cur Windows system settings in the registry. Copy those settings to // a new file with the given full pathname. // // Oh, and umh then: for the two sets of things we do by hand -- // rather than by reading (and later writing) directly from (to) // the registry -- go off and do the special case code for them. // That is, Icon metrics and Nonclient metrics. // // Uses: global szCurDir to get theme directory // resets szCurThemeFile if successful writing to file // // Returns: BOOL success writing to file // BOOL FAR GatherThemeToFile(LPTSTR lpszFullPath) { int imaxkey; BOOL bRet, bOK = TRUE; // // init global error flags bReadOK = bWroteOK = TRUE; // // first do the ICON subkeys // OLD Plus95 code for gathering icon information has been replaced // by GatherICONS() function below. //imaxkey = sizeof(fsRoot)/sizeof(FROST_SUBKEY); //bOK = GatherSubkeys(HKEY_CLASSES_ROOT, fsRoot, imaxkey, lpszFullPath); bOK = GatherICONS(lpszFullPath); // // then do the CURRENT_USER subkeys imaxkey = sizeof(fsCurUser)/sizeof(FROST_SUBKEY); bRet = GatherSubkeys(HKEY_CURRENT_USER, fsCurUser, imaxkey, lpszFullPath); bOK = bOK && bRet; // // Now do the special cases bRet = GatherIconMetricsByHand(lpszFullPath); bOK = bOK && bRet; bRet = GatherNonClientMetricsByHand(lpszFullPath); bOK = bOK && bRet; bRet = GatherSysColorsByHand(lpszFullPath); bOK = bOK && bRet; bRet = GatherWallpaperBitsByHand(lpszFullPath); bOK = bOK && bRet; // // then do the Screen Saver setting, off in its own world // get cur GetPrivateProfileString((LPTSTR)szSS_Section, (LPTSTR)szSS_Key, (LPTSTR)szNULL, (LPTSTR)pValue, MAX_VALUELEN, (LPTSTR)szSS_File); // abstract out variable path if appropriate AbstractPath((LPTSTR)pValue, MAX_VALUELEN); // and save it to the theme bRet = WritePrivateProfileString((LPTSTR)szSS_Section, (LPTSTR)szSS_Key, (LPTSTR)pValue, lpszFullPath); bOK = bOK && bRet; if (!bRet) bWroteOK = FALSE; // Collect the WebView settings -- don't think we really care // if this fails... GatherWebView(lpszFullPath); // // then write magic number for file verification bRet = WritePrivateProfileString((LPTSTR)szFrostSection, (LPTSTR)szMagic, (LPTSTR)szVerify, lpszFullPath); bOK = bOK && bRet; if (!bRet) bWroteOK = FALSE; // // cleanup Assert(bOK, TEXT("didn't gather theme to file successfully\n")); return (bOK); } // // GatherSubkeys // // OK, read this carefully. // // This routine takes a handle to a currently open Registry key. // Then it takes a pointer to an array of FROST_SUBKEYs that identifies // subkey name strings of the open key. Then for those subkey names // each FROST_SUBKEY also points to another array of value names. This // is the final leaf of the Registry scheme. With a key, a subkey and a // specific value name, you can get an actual value. The actual query and // writing to a file happens in the DatumRegisterToFile() routine below. // // So here's the scheme: // for each subkey // open the subkey to get a key handle // for each value of this subkey that we care about // pass all the info to DatumRegisterToFile() to write one value // // Returns: BOOL success writing to file // BOOL GatherSubkeys(HKEY hKeyRoot, FROST_SUBKEY *fsEnum, int iMaxKey, LPTSTR lpszFile) { HKEY hKey; // cur open key int ikey, ival; LONG lret; BOOL bRet, bOK = TRUE; // loop through each subkey for (ikey = 0; ikey < iMaxKey; ikey++) { // open this subkey lret = RegOpenKeyEx( hKeyRoot, (LPTSTR)fsEnum[ikey].szSubKey, (DWORD)0, KEY_QUERY_VALUE, (PHKEY)&hKey ); // check that you got a good key here if (lret != ERROR_SUCCESS) { Assert(FALSE, TEXT("problem on RegOpenKey (read) of subkey ")); Assert(FALSE, fsEnum[ikey].szSubKey); Assert(FALSE, TEXT("\n")); // OK, you couldn't open the key to even look at the values. // ***************************************************************** // based on the sketchy documentation we have for this Reg* and Error // stuff, we're guessing that you've ended up here because this // totally standard, Windows-defined subkey name just doesn't happen // to be defined for the current user. // ***************************************************************** // SO: just write empty strings to the THM file for each valuename // you have for this subkey. You have then faithfully recorded that // there was nothing there. // still successful so long as all the empty strings are written out // with the value names to the THM file. also do default string if // appropriate. // (null loop if default string only) for (ival = 0; ival < fsEnum[ikey].iNumVals; ival++) { bRet = WritePrivateProfileString( fsEnum[ikey].szSubKey, (LPTSTR)fsEnum[ikey].fvVals[ival].szValName, (LPTSTR)szNULL, lpszFile ); Assert(bRet, TEXT("couldn't write empty value string to THM file\n")); bOK = bOK && bRet; if (!bRet) bWroteOK = FALSE; } if (fsEnum[ikey].fValues != FV_LIST) { // either def or list+def bRet = WritePrivateProfileString( fsEnum[ikey].szSubKey, (LPTSTR)FROST_DEFSTR, (LPTSTR)szNULL, lpszFile ); Assert(bRet, TEXT("couldn't write empty default string to THM file\n")); bOK = bOK && bRet; if (!bRet) bWroteOK = FALSE; } continue; // Open (read) subkey problem EXIT } // treat depending on type of values for this subkey switch (fsEnum[ikey].fValues) { case FV_LIST: case FV_LISTPLUSDEFAULT: // loop through each value in the list for this subkey for (ival = 0; ival < fsEnum[ikey].iNumVals; ival++) { bRet = DatumRegisterToFile(hKey, fsEnum[ikey].fvVals[ival], lpszFile, (LPTSTR)(fsEnum[ikey].szSubKey) ); bOK = bOK && bRet; } // check if just list or list plus default if (FV_LIST == fsEnum[ikey].fValues) break; // normal EXIT // else fall through and do default, too case FV_DEFAULT: // // Default string: There are no "valuenames" to search for under // this key. Like the old INI file routines, it's just // // get default string: // this is a little messy to include here and get it right { // variable scope DWORD dwSize; DWORD dwType; LONG lret; BOOL bDefault = TRUE; // first do paranoid check of data size lret = RegQueryValueEx(hKey, (LPTSTR)szNULL, // null str to get default (LPDWORD)NULL, (LPDWORD)&dwType, (LPBYTE)NULL, (LPDWORD)&dwSize ); if (ERROR_SUCCESS == lret) { // saw something there // here's the size check before getting the data if (dwSize > (DWORD)(MAX_VALUELEN * sizeof(TCHAR))) { Assert(FALSE, TEXT("Humongous default entry string in registry...\n")); bDefault = FALSE; // can't read, so very bad news bReadOK = FALSE; } else { // size is acceptable // now really get the value lret = RegQueryValueEx(hKey, (LPTSTR)szNULL,// null str to get default (LPDWORD)NULL, (LPDWORD)&dwType, (LPBYTE)pValue, // getting actual def value (LPDWORD)&dwSize); // If the value is an EXPAND_SZ we need to expand it... if (REG_EXPAND_SZ == dwType) ExpandSZ(pValue); Assert(lret == ERROR_SUCCESS, TEXT("bad return on default entry string\n")); Assert(((dwType == (DWORD)REG_SZ) || (dwType == (DWORD)REG_EXPAND_SZ)), TEXT("unexpected default entry type\n")); if (ERROR_SUCCESS != lret) // couldn't read somehow, so use null string *pValue = 0; } } else // couldn't even find the default string, so use null string *pValue = 0; // be sure to remember if couldn't get a value as above bOK = bOK && bDefault; } // end variable scope // // OK, if this is a path/filename, see about xlating to relative path if (fsEnum[ikey].bDefRelPath) AbstractPath((LPTSTR)pValue, MAX_VALUELEN); // // Phew, finally:write single default value // bRet = WritePrivateProfileString((LPTSTR)(fsEnum[ikey].szSubKey), (LPTSTR)FROST_DEFSTR, (LPTSTR)pValue, lpszFile); Assert(bRet, TEXT("couldn't write default string to THM file.\n")); bOK = bOK && bRet; if (!bRet) bWroteOK = FALSE; break; default: Assert(FALSE, TEXT("Unlisted .fValues value in Gather!\n")); break; } // close this key RegCloseKey(hKey); } // CLEANUP Assert(bOK, TEXT("didn't GatherSubkeys well\n")); return (bOK); } // // DatumRegisterToFile // // This is the atomic operation: a single datum from the registry to the file. // The technique varies a little by type of datum. // // Returns: BOOL success writing to file // Note that sucess doesn't depend on reading value from registry; // could be a value that's not set. Only reason to fail is: value // was too big to read, or the write itself failed. // BOOL DatumRegisterToFile( HKEY hQueryKey, FROST_VALUE fvQuery, LPTSTR lpszFile, // full pathname for output file LPTSTR lpszKeyname ) { DWORD dwSize; DWORD dwType; LONG lret; BOOL bOK = TRUE; BOOL bSkipReadingWP = FALSE; // First off, if this is the Wallpaper and ActiveDesktop is on we // need to read the current wallpaper setting from the IActiveDesktop // interface instead of from the registry. bSkipReadingWP = FALSE; if ((lstrcmpi(fvQuery.szValName,TEXT("Wallpaper")) == 0) && IsActiveDesktopOn()) { if (GetADWallpaper(pValue)) { bSkipReadingWP = TRUE; dwType = REG_SZ; // Set dwType as if we read a SZ from the reg lret = ERROR_SUCCESS; } // Couldn't read the wallpaper from IActiveDesktop so go ahead and // try reading it from the registry. } // Else, if this is the Wallpaper Pattern and ActiveDesktop is on we // need to read the current Pattern setting from the IActiveDesktop // interface instead of from the registry. else if ((lstrcmpi(fvQuery.szValName,TEXT("Pattern")) == 0) && IsActiveDesktopOn()) { if (GetADWPPattern(pValue)) { bSkipReadingWP = TRUE; dwType = REG_SZ; // Set dwType as if we read a SZ from the reg lret = ERROR_SUCCESS; } // Couldn't read the Pattern from IActiveDesktop so go ahead and // try reading it from the registry. } if (!bSkipReadingWP) { // first do paranoid check of data size lret = RegQueryValueEx(hQueryKey, fvQuery.szValName, (LPDWORD)NULL, (LPDWORD)&dwType, (LPBYTE)NULL, (LPDWORD)&dwSize); if (ERROR_SUCCESS == lret) { // here's the size check before getting the data if (dwSize > (DWORD)(MAX_VALUELEN*sizeof(TCHAR))) { Assert(FALSE, TEXT("Humongous entry in registry...\n")); bReadOK = FALSE; return (FALSE); // incredibly unlikely mammoth entry EXIT } // // now really get the value // lret = RegQueryValueEx(hQueryKey, fvQuery.szValName, (LPDWORD)NULL, (LPDWORD)&dwType, (LPBYTE)pValue, (LPDWORD)&dwSize); // If EXPAND_SZ type we need to expand it if (REG_EXPAND_SZ == dwType) { ExpandSZ(pValue); dwType = REG_SZ; // Fudge this to make that assert happy } Assert(lret == ERROR_SUCCESS, TEXT("bad return on datum retrieval\n")); Assert(dwType == (DWORD)fvQuery.iValType, TEXT("unexpected datum type\n")); } } // // if you got something, go ahead and write it if (ERROR_SUCCESS == lret) { // switch on value type to get how to write it switch ((int)dwType) { case REG_SZ: case REG_EXPAND_SZ: // before writing, if this is a path/filename, // see about xlating to relative path // // even before that, see if it is a bitmap // and find out what compressed file it came from -- // that is if it's not an HTM/HTML wallpaper. if ((lstrcmpi(fvQuery.szValName, TEXT("Wallpaper")) == 0) && (lstrcmpi(FindExtension(pValue), TEXT(".htm")) != 0) && (lstrcmpi(FindExtension(pValue), TEXT(".html")) !=0)) { GetImageTitle(pValue, pValue, sizeof(pValue)); } if (fvQuery.bValRelPath) AbstractPath((LPTSTR)pValue, MAX_VALUELEN); bOK = WritePrivateProfileString(lpszKeyname, (LPTSTR)fvQuery.szValName, (LPTSTR)pValue, lpszFile); Assert(bOK, TEXT("couldn't write value string to THM file.\n")); if (!bOK) bWroteOK = FALSE; break; // // these two cases are both just treated as binary output case REG_DWORD: case REG_BINARY: bOK = WriteBytesToFile(lpszKeyname, (LPTSTR)fvQuery.szValName, (BYTE *)pValue, (int)dwSize, lpszFile); Assert(bOK, TEXT("couldn't write value bytes to THM file.\n")); if (!bOK) bWroteOK = FALSE; // pretty unitary write function break; default: Assert(FALSE, TEXT("unexpected REG_* data type read from registry\n")); break; } } // EITHER: couldn't query size OR couldn't retrieve value else { // ***************************************************************** // based on the sketchy documentation we have for this Reg* and Error // stuff, we're guessing that you've ended up here because this // totally legitimate, successfully opened key and this totally // standard, Windows-defined value name just doesn't happen to have // a value assigned to it for the current user. // ***************************************************************** // So: just write an empty string to the THM file. Still successful // so long as the key actually is written to the THM file. bOK = WritePrivateProfileString(lpszKeyname, (LPTSTR)fvQuery.szValName, (LPTSTR)szNULL, lpszFile); Assert(bOK, TEXT("couldn't write empty string to THM file.\n")); if (!bOK) bWroteOK = FALSE; } // cleanup Assert(bOK, TEXT("missed a datum from register to file\n")); return (bOK); } // // WriteBytesToFile // // Writes binary data out to the THM file. // Converts the data byte by byte to ASCII numbers, appends // them to one long string, writes string to profile. // // Returns: success of write to theme file // BOOL FAR WriteBytesToFile(LPTSTR lpszProfileSection, LPTSTR lpszProfileKey, BYTE *pData, int iBytes, LPTSTR lpszProfile) { HLOCAL hXlat; TCHAR *psz; BOOL bWrote = TRUE; int iter; #ifdef UNICODE char szNumberA[10]; // byte value converted to ANSI #endif // // inits // alloc and lock memory for translation hXlat = LocalAlloc(LPTR, 5*sizeof(TCHAR)*iBytes+2); if (!hXlat) { // couldn't create buffer!! NoMemMsg(STR_TO_SAVE); // post low mem message! return (FALSE); // bad news couldn't write EXIT } // // do the translation to a string psz = (TCHAR *)hXlat; // start at beginning of string buffer // loop through the bytes for (iter = 0; iter < iBytes; iter++) { // translate one byte into our string buffer #ifdef UNICODE // With UNICODE we need to use a temporary ANSI buffer // for the litoa conversion, then convert that string to // UNICODE when putting it into our main string buffer. litoa( (int)(pData[iter]), (LPSTR)szNumberA); mbstowcs((wchar_t *)psz, szNumberA, sizeof(szNumberA)); #else // !UNICODE litoa( (int)(pData[iter]), (LPSTR)psz); #endif // add a space lstrcat((LPTSTR)psz, TEXT(" ")); // bump pointer up to end of string for next byte psz = psz + lstrlen((LPTSTR)psz); } // // do the write to the THM file bWrote = WritePrivateProfileString(lpszProfileSection, lpszProfileKey, (LPTSTR)hXlat, lpszProfile); // // cleanup // free up memory allocated LocalFree(hXlat); return (bWrote); } // // ApplyThemeFile // // The inverse of GatherThemeToFile(), this routine takes a theme // file and sets system registry values from the file. It also then // calls individual APIs to make some of the settings take immediate // effect. // // Goes through the list of theme values and if the controlling checkbox // is checked, sets the value from the file to the registry. This is // a nice clean loop using the tables in KEYS.H to match checkboxes to // registry keys/valuenames. // // Then for each checkbox, do the current system settings by hand as // necessary. // // lpszFilename == full pathname BOOL FAR ApplyThemeFile(LPTSTR lpszFilename) { BOOL bRet, bOK = TRUE; int imaxkey; BOOL bFullTrash; extern TCHAR szPlus_CurTheme[]; // // first apply the ROOT subkeys to the registry: these are the icons // but first first go check cur registry to see if trash can full or empty bFullTrash = IsTrashFull(); // OK now apply the root subkeys -- this is where Win95 checks for // the icons //imaxkey = sizeof(fsRoot)/sizeof(FROST_SUBKEY); //bOK = ApplySubkeys(HKEY_CLASSES_ROOT, fsRoot, imaxkey, lpszFilename, // FALSE); // don't apply null theme entries for icons! // Now apply the Win98/current user icon keys imaxkey = sizeof(fsCUIcons)/sizeof(FROST_SUBKEY); bOK = ApplySubkeys(HKEY_CURRENT_USER, fsCUIcons, imaxkey, lpszFilename, FALSE); // don't apply null theme entries for icons! // have to keep setting cursor to wait because someone resets it WaitCursor(); // now apply the right current trash icon from theme bRet = ApplyCurrentTrash(bFullTrash, lpszFilename); bOK = bOK && bRet; // // then apply the CURRENT_USER subkeys to the registry imaxkey = sizeof(fsCurUser)/sizeof(FROST_SUBKEY); bRet = ApplySubkeys(HKEY_CURRENT_USER, fsCurUser, imaxkey, lpszFilename, TRUE); bOK = bOK && bRet; // have to keep setting cursor to wait because someone resets it WaitCursor(); ApplyWebView(lpszFilename); WaitCursor(); // just a random place to check this: Assert(NUM_CURSORS == (sizeof(fvCursors)/sizeof(FROST_VALUE)), TEXT("DANGER: mismatched number of cursors in fvCursors and NUM_CURSORS constant!\n")); // // now try to make everything apply to the system currently bRet = HandPumpSystem(); // *** DEBUG *** need specific error message here bOK = bOK && bRet; // // cleanup // save this theme file in the registry as the last one applied // if you messed with stored CB values for color problem filter if (bLowColorProblem && (fLowBPPFilter == APPLY_NONE)) SaveCheckboxes(); // then reset from the actual buttons Assert(bOK, TEXT("didn't apply theme successfully\n")); return (bOK); } // // ApplySubkeys // // This is parallel to the GatherSubkeys() function above, copying // values instead _from_ the theme file _to_ the Registry. It uses // the same loop structure, see the comments to GatherSubkeys(). // // The one change is that on applying values, we check first the // FC_* flag in the fValCheckbox or fDefCheckbox fields to identify // the controlling checkbox for that valuename. If the checkbox is // unchecked, the value is not set. (We already got the checkbox states // into bCBStates[] in ApplyThemeFile() above. // // Returns: success writing to Registry, should be always TRUE. // BOOL ApplySubkeys(HKEY hKeyRoot, FROST_SUBKEY *fsEnum, int iMaxKey, LPTSTR lpszFile, BOOL bApplyNull) { HKEY hKey; // cur open key int ikey, ival, iret; LONG lret; BOOL bRet, bOK = TRUE; TCHAR szNTReg[MAX_PATH]; // loop through each subkey for (ikey = 0; ikey < iMaxKey; ikey++) { // have to keep setting cursor to wait because someone resets it WaitCursor(); // If this is NT and the key is for the icons we need to touch // up the reg pathing if (IsPlatformNT() && (ikey < MAX_ICON) && ((lstrcmpi(fsEnum[ikey].szSubKey, fsCUIcons[ikey].szSubKey) == 0) || (lstrcmpi(fsEnum[ikey].szSubKey, fsRoot[ikey].szSubKey) == 0))) { lstrcpy(szNTReg, c_szSoftwareClassesFmt); lstrcat(szNTReg, fsRoot[ikey].szSubKey); // open this subkey lret = RegOpenKeyEx( hKeyRoot, (LPTSTR)szNTReg, (DWORD)0, KEY_SET_VALUE, (PHKEY)&hKey ); } else { // open this subkey lret = RegOpenKeyEx( hKeyRoot, (LPTSTR)fsEnum[ikey].szSubKey, (DWORD)0, KEY_SET_VALUE, (PHKEY)&hKey ); } // check that you got a good key here if (lret != ERROR_SUCCESS) { DWORD dwDisposition; Assert(FALSE, TEXT("problem on RegOpenKey (write) of subkey ")); Assert(FALSE, fsEnum[ikey].szSubKey); Assert(FALSE, TEXT("\n")); // OK, you couldn't even open the key !!! // ***************************************************************** // based on the sketchy documentation we have for this Reg* and Error // stuff, we're guessing that you've ended up here because this // totally standard, Windows-defined subkey name just doesn't happen // to be defined for the current user. // ***************************************************************** // SO: Just create this subkey for this user, and maybe it will get // used after you create and set it. // still successful so long as can create new subkey to write to if (IsPlatformNT() && (ikey < MAX_ICON) && ((lstrcmpi(fsEnum[ikey].szSubKey, fsCUIcons[ikey].szSubKey) == 0) || (lstrcmpi(fsEnum[ikey].szSubKey, fsRoot[ikey].szSubKey) == 0))) { lret = RegCreateKeyEx( hKeyRoot, (LPTSTR)szNTReg, (DWORD)0, (LPTSTR)szNULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, (LPSECURITY_ATTRIBUTES)NULL, (PHKEY)&hKey, (LPDWORD)&dwDisposition ); } else { lret = RegCreateKeyEx( hKeyRoot, (LPTSTR)fsEnum[ikey].szSubKey, (DWORD)0, (LPTSTR)szNULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, (LPSECURITY_ATTRIBUTES)NULL, (PHKEY)&hKey, (LPDWORD)&dwDisposition ); } if (lret != ERROR_SUCCESS) { Assert(FALSE, TEXT("problem even with RegCreateKeyEx (write) of subkey ")); Assert(FALSE, fsEnum[ikey].szSubKey); Assert(FALSE, TEXT("\n")); // we are not happy campers bOK = FALSE; // but we'll keep on truckin' continue; // bad subkey EXIT } } // treat depending on type of values for this subkey switch (fsEnum[ikey].fValues) { case FV_LIST: case FV_LISTPLUSDEFAULT: // loop through each value in the list for this subkey for (ival = 0; ival < fsEnum[ikey].iNumVals; ival++) { // if the checkbox that controls this value is checked if ( bCBStates[ fsEnum[ikey].fvVals[ival].fValCheckbox ] ) { // then read from theme file and write to registry bRet = DatumFileToRegister(hKey, fsEnum[ikey].fvVals[ival], lpszFile, (LPTSTR)(fsEnum[ikey].szSubKey), bApplyNull); bOK = bOK && bRet; } } // check if just list or list plus default if (FV_LIST == fsEnum[ikey].fValues) break; // normal EXIT // else fall through and do default, too case FV_DEFAULT: // // if this subkey's default value's checkbox is checked if (bCBStates[fsEnum[ikey].fDefCheckbox]) { // // Default string: There are no "valuenames" to set under // this key. Like the old INI file routines, it's just one value. // // // Get default value string iret = GetPrivateProfileString((LPTSTR)(fsEnum[ikey].szSubKey), (LPTSTR)FROST_DEFSTR, (LPTSTR)szNULL, (LPTSTR)pValue, MAX_VALUELEN, lpszFile); // no error case; legit null string value is indistinguishable from // error case... // If we're reading the ICON strings and we got a NULL return // then we should try using the "old" Win95 reg keys in case // this is an old .Theme file if ((!*pValue) && (ikey < MAX_ICON) && (lstrcmpi(fsEnum[ikey].szSubKey, fsCUIcons[ikey].szSubKey) == 0)) { iret = GetPrivateProfileString((LPTSTR)(fsRoot[ikey].szSubKey), (LPTSTR)FROST_DEFSTR, (LPTSTR)szNULL, (LPTSTR)pValue, MAX_VALUELEN, lpszFile); // PLUS98 bug 1042 // If this is the MyDocs icon and there is no setting for // it in the Theme file then we need to default to the // szMyDocsDefault icon. if ((MYDOC_INDEX == ikey) && (!*pValue)) { lstrcpy(pValue, MYDOC_DEFSTR); } } // if this value is a relative path filename string, // first see about making abstract path into current instance if (fsEnum[ikey].bDefRelPath) { InstantiatePath((LPTSTR)pValue, MAX_VALUELEN); // look for and confirm finding the file, with replacement if (ConfirmFile((LPTSTR)pValue, TRUE) == CF_NOTFOUND) { *pValue = 0; // if not found, just null out filename bOK = FALSE; // couldn't apply this file } } // // sometimes don't want to set a null string to the Registry if (*pValue || bApplyNull) { // either non-null or OK to set null // now set the value in the registry lret = RegSetValueEx(hKey, (LPTSTR)szNULL,// null str to set default value 0, (DWORD)REG_SZ, (LPBYTE)pValue, // getting actual def value (DWORD)(SZSIZEINBYTES(pValue))); Assert(lret == ERROR_SUCCESS, TEXT("couldn't write a default entry string!\n")); bOK = bOK && (lret == ERROR_SUCCESS); } } // end if controlling checkbox checked break; default: Assert(FALSE, TEXT("Unlisted .fValues value in Apply!\n")); break; } // close this key RegCloseKey(hKey); } // CLEANUP Assert(bOK, TEXT("didn't ApplySubkeys well\n")); return (bOK); } // // DatumFileToRegister // // Like DatumRegisterToFile(), this is an atomic operation; in this // case, move a single datum from theme file to the registry. // Here, too, technique differs slightly between strings and numbers. // // Returns: BOOL success writing to registry // BOOL DatumFileToRegister( HKEY hSetKey, FROST_VALUE fvSet, LPTSTR lpszFile, // full pathname for theme file LPTSTR lpszKeyname, BOOL bOKtoApplyNull) { LONG lret; int iret; BOOL bOK = TRUE; // // get the saved string from the theme file iret = GetPrivateProfileString(lpszKeyname, (LPTSTR)fvSet.szValName, (LPTSTR)szNULL, (LPTSTR)pValue, MAX_VALUELEN, lpszFile); // no error case: can't tell difference between legit null string and default // If we're reading the TRASH ICON strings and we got a NULL return // then we should try using the "old" Win95 reg keys in case // this is an old .Theme file if ((!*pValue) && (lstrcmpi(lpszKeyname, fsCUIcons[TRASH_INDEX].szSubKey) == 0)) { iret = GetPrivateProfileString((LPTSTR)(fsRoot[TRASH_INDEX].szSubKey), (LPTSTR)fvSet.szValName, (LPTSTR)szNULL, (LPTSTR)pValue, MAX_VALUELEN, lpszFile); } // not always OK to set null value to registry if (!bOKtoApplyNull && !(*pValue)) return(TRUE); // no work to do EXIT // switch on value type to get how to write it switch (fvSet.iValType) { case REG_SZ: // if this value is a relative path filename string, // first see about making abstract path into current instance if (fvSet.bValRelPath) { InstantiatePath((LPTSTR)pValue, MAX_VALUELEN); // look for and confirm finding the file, with replacement if (ConfirmFile((LPTSTR)pValue, TRUE) == CF_NOTFOUND) { *pValue = 0; // if not found, just null out filename bOK = FALSE; // couldn't apply this file! } } // If this is the Wallpaper setting and it's an .htm or .html // wallpaper we need to apply this via IActiveDesktop // if (lstrcmpi(fvSet.szValName, TEXT("Wallpaper")) == 0 && pValue && *pValue && (lstrcmpi(FindExtension(pValue),TEXT(".htm")) == 0 || lstrcmpi(FindExtension(pValue),TEXT(".html")) == 0)) { // First, clear the existing registry wallpaper setting. // Don't really care if this fails. lret = RegSetValueEx(hSetKey, fvSet.szValName, 0, (DWORD)REG_SZ, (LPBYTE)TEXT("\0"), (DWORD)sizeof(TCHAR)); // Now try applying the new wallpaper via ActiveDesktop SetWP. if (SetADWallpaper(pValue, TRUE /* Force AD on */)) { bOK = TRUE; *pValue = 0; bOKtoApplyNull = FALSE; // Don't want to set Wallpaper string to // NULL later on because it causes AD to // forget about the HTML wallpaper!! } else { // Setting the HTML wallpaper failed! bOK = FALSE; *pValue = 0; } } // // if we are applying a compressed image, lets decompress it first // // NOTE we can handle the out 'o disk case a little better // if (lstrcmpi(fvSet.szValName, TEXT("Wallpaper")) == 0 && pValue && *pValue && lstrcmpi(FindExtension(pValue),TEXT(".bmp")) != 0 && lstrcmpi(FindExtension(pValue),TEXT(".dib")) != 0 && lstrcmpi(FindExtension(pValue),TEXT(".rle")) != 0 ) { TCHAR plus_bmp[MAX_PATH]; if (g_hbmWall) { CacheDeleteBitmap(g_hbmWall); g_hbmWall = NULL; } g_hbmWall = LoadWallpaper(pValue, lpszFile, FALSE); Assert(g_hbmWall, TEXT("LoadWallpaper failed!\n")); if (g_hbmWall) { GetPlusBitmapName(plus_bmp); bOK = bOK && SaveImageToFile(g_hbmWall, plus_bmp, pValue); Assert(bOK, TEXT("unable to save wallpaper to plus!.bmp\n")); } else { bOK = FALSE; } if (bOK) lstrcpy(pValue, plus_bmp); else *pValue = 0; // if not found, just null out filename } // not always OK to set null value to registry if (!bOKtoApplyNull && !(*pValue)) return(TRUE); // no work to do EXIT // just write the string to the registry lret = RegSetValueEx(hSetKey, fvSet.szValName, 0, (DWORD)REG_SZ, (LPBYTE)pValue, (DWORD)SZSIZEINBYTES(pValue)); bOK = bOK && (lret == ERROR_SUCCESS); // One last thing -- if this is the Wallpaper and // ActiveDesktop is on we need to use the ActiveDesktop // interface to set the wallpaper. We do this because // we want this wallpaper setting in BOTH the registry // and the ActiveDesktop. If you set a BMP wallpaper // via the registry w/out also doing it via the AD // interface it's possible for the AD/Non-AD desktops // to be out of sync on their wallpaper. // // Note this is the case where the wallpaper is not html. // Html wallpapers are set via the ActiveDesktop interface // above. if ((lstrcmpi(fvSet.szValName, TEXT("Wallpaper")) == 0) && IsActiveDesktopOn()) { bOK = SetADWallpaper(pValue, FALSE); } Assert(bOK, TEXT("couldn't write a string value to registry!\n")); break; // // these two cases are both just treated as binary output case REG_DWORD: case REG_BINARY: bOK = WriteBytesToRegister(hSetKey, (LPTSTR)fvSet.szValName, fvSet.iValType, (LPTSTR)pValue); Assert(bOK, TEXT("couldn't write value bytes to registry.\n")); break; default: Assert(FALSE, TEXT("unexpected REG_* data type from our own tables!\n")); break; } // cleanup Assert(bOK, TEXT("missed a datum from register to file\n")); return (bOK); } // // WriteBytesToRegister // // Parallel to WriteBytesToFile() function. This function takes an ASCII // string of space-separated 0-255 numbers, translates them into byte // number values packed into an output buffer, and then assigns that // binary data to the given key/valuename in the registry -- as the data // type given. // // Note that lpByteStr points to the same pValue that we are using as // an output buffer. This depends on the numbers compressing as they // are translated from ASCII to binary. Uses an itermediary variable so // the first translation isn't messed up. // // ******************************************************************** // ASSUMPTIONS: You assume that noone has mucked with this theme file // manually! In particular, this function depends on no leading blank // and exactly one blank between each number in the string, and one // trailing blank at the end followed by a null terminator. // WHOOPS! BAD ASSUMPTION: trailing blank is stripped by Write/Get // PrivateProfileString() functions! So need to watch for end manually. // ******************************************************************** // // Uses: writes binary data to global pValue[] // // Returns: success of write to register BOOL WriteBytesToRegister(HKEY hKeySet, LPTSTR lpszValName, int iType, LPTSTR lpszByteStr) { BOOL bOK = TRUE; int iBytes; LONG lret; iBytes = WriteBytesToBuffer(lpszByteStr); // set binary data in register file with the right data type lret = RegSetValueEx(hKeySet, lpszValName, 0, (DWORD)iType, (LPBYTE)pValue, (DWORD)iBytes); bOK = (lret == ERROR_SUCCESS); Assert(bOK, TEXT("couldn't write a string value to registry!\n")); // // cleanup return (bOK); } // // Utility routine for above; takes ASCII string to binary in // global pValue[] buffer. // // Since the values this guy is manipulating is purely ASCII // numerics we should be able to get away with this char pointer // arithmetic. If they were not simple ASCII numerics I think // we could get into trouble with some DBCS chars // // Uses: writes binary data to global pValue[] // int FAR WriteBytesToBuffer(LPTSTR lpszInput) { LPTSTR lpszCur, lpszNext, lpszEnd; BYTE *pbCur; int iTemp, iBytes; #ifdef UNICODE CHAR szTempA[10]; #endif // // inits lpszNext = lpszInput; pbCur = (BYTE *)&pValue; iBytes = 0; lpszEnd = lpszInput + lstrlen(lpszInput); // points to null term // // translating loop while (*lpszNext && (lpszNext < lpszEnd)) { // // update str pointers // hold onto your starting place lpszCur = lpszNext; // advance pointer to next and null terminate cur while ((TEXT(' ') != *lpszNext) && *lpszNext) { lpszNext++; } *lpszNext = 0; lpszNext++; // on last number, this leaves lpszNext pointing past lpszEnd // translate this string-number into binary number and place in // output buffer. #ifdef UNICODE wcstombs(szTempA, lpszCur, sizeof(szTempA)); iTemp = latoi(szTempA); #else // !UNICODE iTemp = latoi(lpszCur); #endif *pbCur = (BYTE)iTemp; pbCur++; // incr byte loc in output buffer // keep track of your bytes iBytes++; } // // cleanup return (iBytes); } // // Trash functions // // The registry holds three icons for the trash: full, empty, and default. // In theory default is always one of the full or empty, and it is the current // state of the actual trash. // // These functions are to query the trash state: // // IsTrashFull returns BOOL on full state // // and to apply the correct-state trash icon from // the theme file to the default/cur: // // ApplyCurrentTrash // #define szCUTrashKey (fsCUIcons[TRASH_INDEX].szSubKey) #define szRTrashKey (fsRoot[TRASH_INDEX].szSubKey) #define szEmptyTrashVal TEXT("empty") #define szFullTrashVal TEXT("full") BOOL IsTrashFull() { TCHAR szEmpty[MAX_PATHLEN+1]; TCHAR szDefault[MAX_PATHLEN+1]; TCHAR szNTReg[MAX_PATH]; // Get the two strings // First try the CURRENT_USER branch // Then try the CLASSES_ROOT branch if (IsPlatformNT()) { lstrcpy(szNTReg, c_szSoftwareClassesFmt); lstrcat(szNTReg, szRTrashKey); szEmpty[0] = TEXT('\0'); HandGet(HKEY_CURRENT_USER, szNTReg, szEmptyTrashVal, (LPTSTR)szEmpty); if (!*szEmpty) { // Didn't get a valid string from CURRENT_USER so try CLASSES_ROOT HandGet(HKEY_CLASSES_ROOT, szRTrashKey, szEmptyTrashVal, (LPTSTR)szEmpty); } szDefault[0] = TEXT('\0'); HandGet(HKEY_CURRENT_USER, szNTReg, szNULL, (LPTSTR)szDefault); if (!*szDefault) { // Didn't get a valid string from CURRENT_USER so try CLASSES_ROOT HandGet(HKEY_CLASSES_ROOT, szRTrashKey, szNULL, (LPTSTR)szDefault); } } else // Not NT { szEmpty[0] = TEXT('\0'); HandGet(HKEY_CURRENT_USER, szCUTrashKey, szEmptyTrashVal, (LPTSTR)szEmpty); if (!*szEmpty) { // Didn't get a valid string from CURRENT_USER so try CLASSES_ROOT HandGet(HKEY_CLASSES_ROOT, szRTrashKey, szEmptyTrashVal, (LPTSTR)szEmpty); } szDefault[0] = TEXT('\0'); HandGet(HKEY_CURRENT_USER, szCUTrashKey, szNULL, (LPTSTR)szDefault); if (!*szDefault) { // Didn't get a valid string from CURRENT_USER so try CLASSES_ROOT HandGet(HKEY_CLASSES_ROOT, szRTrashKey, szNULL, (LPTSTR)szDefault); } } // have to keep setting cursor to wait because someone resets it WaitCursor(); // compare strings and return return(lstrcmpi((LPTSTR)szEmpty, (LPTSTR)szDefault)); // lstrcmpi rets 0 for equal strings; equal to empty means FALSE to full } BOOL ApplyCurrentTrash(BOOL bTrashFull, LPTSTR lpszFile) { LONG lret; HKEY hKey; // cur open key TCHAR szNTReg[MAX_PATH]; // // check first that we are even touching the icons if (!bCBStates[FC_ICONS]) return (TRUE); // no trash to apply EXIT // have to keep setting cursor to wait because someone resets it WaitCursor(); // // inits //lret = RegOpenKeyEx( HKEY_CLASSES_ROOT, (LPTSTR)szRTrashKey, // (DWORD)0, KEY_SET_VALUE, (PHKEY)&hKey ); if (IsPlatformNT()) { lstrcpy(szNTReg, c_szSoftwareClassesFmt); lstrcat(szNTReg, szRTrashKey); lret = RegOpenKeyEx( HKEY_CURRENT_USER, (LPTSTR)szNTReg, (DWORD)0, KEY_SET_VALUE, (PHKEY)&hKey ); } else // NOT NT { lret = RegOpenKeyEx( HKEY_CURRENT_USER, (LPTSTR)szCUTrashKey, (DWORD)0, KEY_SET_VALUE, (PHKEY)&hKey ); } if (lret != ERROR_SUCCESS) { Assert(FALSE, TEXT("problem on RegOpenKey CURRENT_USER in ApplyCurrentTrash\n")); return (FALSE); // nothing else to do EXIT } // have to keep setting cursor to wait because someone resets it WaitCursor(); // get the right trash icon file GetPrivateProfileString((LPTSTR)szCUTrashKey, (LPTSTR)(bTrashFull ? szFullTrashVal : szEmptyTrashVal), (LPTSTR)szNULL, (LPTSTR)pValue, MAX_VALUELEN, lpszFile); // If we didn't get the value it could be that we've got an old // Win95/Plus95 .Theme file. Let's try reading the Theme file // using the Win95 reg key: if (!*pValue) { GetPrivateProfileString((LPTSTR)szRTrashKey, (LPTSTR)(bTrashFull ? szFullTrashVal : szEmptyTrashVal), (LPTSTR)szNULL, (LPTSTR)pValue, MAX_VALUELEN, lpszFile); } InstantiatePath((LPTSTR)pValue, MAX_VALUELEN); // look for and confirm finding the file, with replacement if (ConfirmFile((LPTSTR)pValue, TRUE) == CF_NOTFOUND) { *pValue = 0; // if not found, just null out filename --> ret FALSE } if (!(*pValue)) { Assert(FALSE, TEXT("ApplyCurrentTrash came up with no file\n")); RegCloseKey(hKey); // mini-cleanup return (FALSE); // no usable icon file, so just EXIT and leave cur } // have to keep setting cursor to wait because someone resets it WaitCursor(); // Do the deed lret = RegSetValueEx(hKey, (LPTSTR)szNULL, // sets default value 0, (DWORD)REG_SZ, (LPBYTE)pValue, (DWORD)SZSIZEINBYTES(pValue)); // have to keep setting cursor to wait because someone resets it WaitCursor(); // cleanup and return RegCloseKey(hKey); Assert(lret == ERROR_SUCCESS, TEXT("bad return setting current trash\n")); return (ERROR_SUCCESS == lret); } // // HandPumpSystem // // OK. You've applied all of the theme file settings to the registry. // Congratulations. Now reboot! HA HA HA! No seriously, most of the // settings don't take effect in the current system just by writing // them to the registry. You have to make each system change happen // by hand. Hand pump it. // // So here we try to get things actually changing in front of the // astonished user's eyes. // Of course, assuming the checkbox to request it is checked. // // First, to be robustly certain that we are setting what is in the // registry, actually read the value from the registry. Then use the // random API for this element to set it. // // This is long and messy, the way it has to be. See design critique // note at head of file. // // NOTE that SetSysColors() in SetSysColorsByHand() sends a // WM_SYSCOLORCHANGE message. We also need to send a WM_SYSCOLORCHANGE // message if we change any of the metrics - since Excel and other apps // saw color/metric changes as unitary in 3.1 and would look for color. // The extra BOOLs watching color and sys changes are to avoid sending // two WM_SYSCOLORCHANGE messages. // // Uses: global bCBStates[] for checkbox states // global szCurThemeFile[], non-encapsulated uncool // but this is the grody routine anyway // BOOL HandPumpSystem() { BOOL bret, bOK = TRUE, bChangedSettings = FALSE; TCHAR szWinMetrics[] = TEXT("WindowMetrics"); BOOL fClearAppearance = FALSE; BOOL bSaverIsNull = FALSE; HKEY hKey; LONG lret; DWORD dwDisposition; int screenSaverTimeout = 0; // Screen saver timeout BOOL bSkipWP = FALSE; // ActiveDesktop/HTML WP so skip HP WP TCHAR szADWP[MAX_PATH]; // Current/ActiveDesktop wallpaper setting // FC_SCRSVR if (bCBStates[FC_SCRSVR]) { // have to keep setting cursor to wait because someone resets it WaitCursor(); // Take a peek at the current SS setting. If it's null we want to // force the timeout to 15 minutes iff current timeout is 1 minute // per Plus98 bug 1075. The reason for doing this is because Win98 // sets the timeout to 1 min by default -- so the first time a // SS is applied the timeout is very short. Too short in some // people's opinion. So we try to detect this one scenario and // make the timeout a little bit longer. GetPrivateProfileString((LPTSTR)szSS_Section, (LPTSTR)szSS_Key, (LPTSTR)szNULL, (LPTSTR)pValue, MAX_VALUELEN, (LPTSTR)szSS_File); if (!*pValue) bSaverIsNull = TRUE; // // This one is different: Screen Saver is saved in SYSTEM.INI. #ifdef GENTLER // get the current value from SYSTEM.INI GetPrivateProfileString((LPTSTR)szSS_Section, (LPTSTR)szSS_Key, (LPTSTR)szNULL, (LPTSTR)szTemp, MAX_VALUELEN, (LPTSTR)szSS_File); // now, with cur value as default, get value from theme file GetPrivateProfileString((LPTSTR)szSS_Section, (LPTSTR)szSS_Key, (LPTSTR)szTemp, // cur value is default (LPTSTR)pValue, MAX_VALUELEN, (LPTSTR)szCurThemeFile); #endif // get scr saver from theme GetPrivateProfileString((LPTSTR)szSS_Section, (LPTSTR)szSS_Key, (LPTSTR)szNULL, // NULL is default (LPTSTR)pValue, MAX_VALUELEN, (LPTSTR)szCurThemeFile); // next, translate path variable if necessary InstantiatePath((LPTSTR)pValue, MAX_VALUELEN); // look for and confirm finding the file, with replacement if (ConfirmFile((LPTSTR)pValue, TRUE) == CF_NOTFOUND) { *pValue = 0; // if not found, just null out filename bOK = FALSE; // couldn't apply this file! } else { // file's OK, so continue // now, MAKE SURE that you only write short filenames to old-fashioned system if (FilenameToShort((LPTSTR)pValue, (LPTSTR)szMsg)) lstrcpy(FileFromPath((LPTSTR)pValue), (LPTSTR)szMsg); } // and finally, apply value from theme file (or NULL if not in theme) WritePrivateProfileString((LPTSTR)szSS_Section, (LPTSTR)szSS_Key, (LPTSTR)pValue, (LPTSTR)szSS_File); // and make it live in the system SystemParametersInfo(SPI_SETSCREENSAVEACTIVE, // set depending on whether scr saver in theme // pValue still has scr saver name (*pValue ? 1 : 0), NULL, SPIF_SENDCHANGE | SPIF_UPDATEINIFILE); // Set screen saver timeout value if (!SystemParametersInfo(SPI_GETSCREENSAVETIMEOUT, 0, &screenSaverTimeout, 0)) { screenSaverTimeout = 0; } // Plus 98 bug 1075 -- if current screensaver setting is NULL and // the timeout is 1 minute, then force timeout to default (15 mins). if (bSaverIsNull && (60 == screenSaverTimeout)) screenSaverTimeout = 0; if (*pValue && !screenSaverTimeout) { // There must be a screen saver timeout value, otherwise the system // assumes that there is no screen saver selected. SystemParametersInfo(SPI_SETSCREENSAVETIMEOUT, DEF_SCREENSAVETIMEOUT, NULL, SPIF_SENDCHANGE | SPIF_UPDATEINIFILE); } } // have to keep setting cursor to wait because someone resets it WaitCursor(); // FC_SOUND // already in effect just by setting registry settings if (bCBStates[FC_SOUND]) { // but need to flush buffer and ensure new sounds used for next events sndPlaySound((LPTSTR)NULL, SND_ASYNC | SND_NODEFAULT); // // Clear the current pointer scheme string from the registry so that Mouse // cpl doesn't display a bogus name. Don't care if this fails. RegSetValue(HKEY_CURRENT_USER, (LPTSTR) szCP_SoundSchemes, REG_SZ, TEXT(".current"), 0); } // have to keep setting cursor to wait because someone resets it WaitCursor(); // FC_PTRS if (bCBStates[FC_PTRS]) { SystemParametersInfo( SPI_SETCURSORS, 0, 0, SPIF_SENDCHANGE); // // Clear the current pointer scheme string from the registry so that Mouse // cpl doesn't display a bogus name. Don't care if this fails. RegSetValue(HKEY_CURRENT_USER, (LPTSTR) szCP_Appearance, REG_SZ, szNULL, sizeof(TCHAR)); } // have to keep setting cursor to wait because someone resets it WaitCursor(); // FC_WALL // If ActiveDesktop is on and we're using an html wallpaper // we don't want to do this hand pump stuff for the various // wallpaper settings bSkipWP = FALSE; if (IsActiveDesktopOn()) { if (GetADWallpaper(szADWP)) { if (lstrcmpi(FindExtension(szADWP), TEXT(".htm")) == 0 || lstrcmpi(FindExtension(szADWP), TEXT(".html")) == 0 ) { bSkipWP = TRUE; } } } if ((bCBStates[FC_WALL]) && !bSkipWP) { // // TileWallpaper and WallpaperStyle done by hand here // lret = RegCreateKeyEx( HKEY_CURRENT_USER, (LPTSTR)szCP_DT, (DWORD)0, (LPTSTR)szNULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, (LPSECURITY_ATTRIBUTES)NULL, (PHKEY)&hKey, (LPDWORD)&dwDisposition ); if (lret != ERROR_SUCCESS) { Assert(FALSE, TEXT("problem on RegCreateKeyEx for CP Desktop in HandPump!\n")); bOK = FALSE; // we are not happy campers } if (ERROR_SUCCESS == lret) { // got an open key; set the two values // do TileWallpaper GetPrivateProfileString((LPTSTR)szCP_DT, (LPTSTR)szTileWP, (LPTSTR)szNULL, (LPTSTR)pValue, MAX_VALUELEN, (LPTSTR)szCurThemeFile); if (*pValue) { // if in theme, set; else leave reg alone! lret = RegSetValueEx(hKey, (LPTSTR)szTileWP, 0, (DWORD)REG_SZ, (LPBYTE)pValue, (DWORD)SZSIZEINBYTES(pValue)); Assert(lret == ERROR_SUCCESS, TEXT("bad return setting szTileWP in HandPump!\n")); if (ERROR_SUCCESS != lret) bOK = FALSE; } // do WallpaperStyle GetPrivateProfileString((LPTSTR)szCP_DT, (LPTSTR)szWPStyle, (LPTSTR)szNULL, (LPTSTR)pValue, MAX_VALUELEN, (LPTSTR)szCurThemeFile); if (*pValue) { // if in theme, set; else leave reg alone! lret = RegSetValueEx(hKey, (LPTSTR)szWPStyle, 0, (DWORD)REG_SZ, (LPBYTE)pValue, (DWORD)SZSIZEINBYTES(pValue)); Assert(lret == ERROR_SUCCESS, TEXT("bad return setting WPStyle in HandPump!\n")); if (ERROR_SUCCESS != lret) bOK = FALSE; } RegCloseKey(hKey); // mini-cleanup } // // Wallpaper and Pattern are set in reg in ApplySubkeys. // Just make them live here // // get the Wallpaper reset in system bret = HandGet(HKEY_CURRENT_USER, TEXT("Control Panel\\Desktop"), TEXT("Wallpaper"), (LPTSTR)pValue); SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, pValue, SPIF_SENDCHANGE); // get the Pattern reset in system bret = HandGet(HKEY_CURRENT_USER, TEXT("Control Panel\\Desktop"), TEXT("Pattern"), (LPTSTR)pValue); SystemParametersInfo(SPI_SETDESKPATTERN, 0, pValue, SPIF_SENDCHANGE); // PLUS! 98 Bug 896 -- when switching from an HTML wallpaper // to a BMP wallpaper the shell didn't update the WP if the // ICONS setting was not checked -- user had to press F5 to // refresh the desktop. This fixes that problem. SHChangeNotify(SHCNE_ASSOCCHANGED, 0, NULL, NULL); // the rest of the wallpaper items seem to be read from registry as needed // "TileWallpaper" // NIX: "WallpaperStyle" // "WallPaperOriginX" // "WallPaperOriginY" } // have to keep setting cursor to wait because someone resets it WaitCursor(); // FC_ICONS // already done // FC_ICONSIZE and FC_FONTS are NO LONGER intertwined // if (bCBStates[FC_FONTS] || bCBStates[FC_ICONSIZE]) { // for icons, this is just for the spacing; size already done // FC_FONTS if (bCBStates[FC_FONTS]) { // for fonts, this is the icon fonts SetIconMetricsByHand(FALSE, bCBStates[FC_FONTS]); fClearAppearance = TRUE; } // have to keep setting cursor to wait because someone resets it WaitCursor(); // FC_FONTS and FC_BORDERS are intertwined if (bCBStates[FC_FONTS] || bCBStates[FC_BORDERS]) { SetNonClientMetricsByHand(bCBStates[FC_FONTS], bCBStates[FC_BORDERS]); bChangedSettings = TRUE; fClearAppearance = TRUE; } // have to keep setting cursor to wait because someone resets it WaitCursor(); // FC_COLORS if (bCBStates[FC_COLORS]) { bret = SetSysColorsByHand(); // // THIS SENT A WM_SYSCOLORCHANGE MESSAGE // bOK = bOK && bret; fClearAppearance = TRUE; } else if (bChangedSettings) // may need to send color msg anyway // for Win3.1 app compatibility, need to say COLOR changed if any metrics changed PostMessage(HWND_BROADCAST, WM_SYSCOLORCHANGE, 0, 0); // Changed SendMessage to PostMessage to get around Plus! Setup bug // // cleanup // have to keep setting cursor to wait because someone resets it WaitCursor(); // let the world know you've mucked with it all if (bChangedSettings) SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETNONCLIENTMETRICS, (LPARAM)(LPTSTR)szWinMetrics); // have to keep setting cursor to wait because someone resets it WaitCursor(); // if (bCBStates[FC_ICONS] || bCBStates[FC_ICONSIZE]) { if (bCBStates[FC_ICONS]) { SHChangeNotify(SHCNE_ASSOCCHANGED, 0, NULL, NULL); // should do the trick! SendMessage(HWND_BROADCAST, WM_SETTINGCHANGE, SPI_SETICONMETRICS, (LPARAM)(LPTSTR)szWinMetrics); } if (fClearAppearance) { // // Clear the current appearance string from the registry so that Display cpl // doesn't display a bogus name. Don't care if this fails. if (RegOpenKeyEx(HKEY_CURRENT_USER, (LPTSTR)szCP_Appearance, (DWORD)0, KEY_SET_VALUE, (PHKEY)&hKey ) == ERROR_SUCCESS) { RegDeleteValue(hKey, szCurrent); RegCloseKey(hKey); } } return (bOK); } // // HandGet // // Just a little helper routine, gets an individual string value from the // registry and returns it to the caller. Takes care of registry headaches, // including a paranoid length check before getting the string. // // NOTE that this function thinks it's getting a string value. If it's // another kind, this function will do OK: but the caller may be surprised // if expecting a string. // // Returns: success of string retrieval // BOOL FAR HandGet(HKEY hKeyRoot, LPTSTR lpszSubKey, LPTSTR lpszValName, LPTSTR lpszRet) { LONG lret; HKEY hKey; // cur open key BOOL bOK = TRUE; DWORD dwSize; DWORD dwType; // // inits // get subkey lret = RegOpenKeyEx( hKeyRoot, lpszSubKey, (DWORD)0, KEY_QUERY_VALUE, (PHKEY)&hKey ); if (lret != ERROR_SUCCESS) { Assert(FALSE, TEXT("problem on RegOpenKey in HandGet\n")); return (FALSE); } // now do our paranoid check of data size lret = RegQueryValueEx(hKey, lpszValName, (LPDWORD)NULL, (LPDWORD)&dwType, (LPBYTE)NULL, // null for size info only (LPDWORD)&dwSize ); if (ERROR_SUCCESS == lret) { // saw something there // here's the size check before getting the data if (dwSize > (DWORD)(MAX_VALUELEN * sizeof(TCHAR))) { // if string too big Assert(FALSE, TEXT("Humongous registry string; can't HandGet...\n")); bOK = FALSE; // can't read, so very bad news bReadOK = FALSE; } else { // size is OK to continue // now really get the value lret = RegQueryValueEx(hKey, lpszValName, (LPDWORD)NULL, (LPDWORD)&dwType, (LPBYTE)lpszRet, // getting actual value (LPDWORD)&dwSize); // If this is an EXPAND_SZ we need to expand it... if (REG_EXPAND_SZ == dwType) ExpandSZ(lpszRet); Assert(lret == ERROR_SUCCESS, TEXT("bad return HandGet query\n")); Assert(((dwType == (DWORD)REG_SZ) || (dwType == (DWORD)REG_EXPAND_SZ)), TEXT("non-string type in HandGet!\n")); if (ERROR_SUCCESS != lret) bOK = FALSE; } } else bOK = FALSE; // // cleanup // close subkey RegCloseKey(hKey); return (bOK); } // // Gather/SetSysColorsByHand // // Colors would seem to be the prototypical setting, that we could just // read and write directly in the registry. But noooooooo..... // A. On initial install, somehow the color settings all remain blank. // B. Need to use SetSysColor() anyway to broadcast message. // // So on both read and write, need to use Get/SetSysColor. On write, still // also need to write directly to registry. // // // Uses GetSysColor() rather than reading directly from the Registry. // Writes to theme file. // BOOL GatherSysColorsByHand(LPTSTR lpszTheme) { int iColor; COLORREF crRGB; BOOL bRet, bOK = TRUE; BOOL bGrad = FALSE; // Are gradient titles enabled? // init bGrad SystemParametersInfo(SPI_GETGRADIENTCAPTIONS, 0, (LPVOID)&bGrad, 0); // // inits Assert ((sizeof(iSysColorIndices)/sizeof(int)) == (sizeof(pRegColors)/sizeof(TCHAR *)), TEXT("mismatched color arrays in GatherSysColorsByHand\n")); // // main process // go through each color in your array for (iColor = 0; iColor < (sizeof(pRegColors)/sizeof(TCHAR *)); iColor ++) { // If this is the Gradient Caption setting and the system does // not currently show gradient captions then don't write them out // to the theme file. // // bGrad == Are gradient captions currently enabled? // g_bGradient == Enough colors to show gradients? if (((COLOR_GRADIENTACTIVECAPTION == iSysColorIndices[iColor]) || (COLOR_GRADIENTINACTIVECAPTION == iSysColorIndices[iColor])) && (!(bGrad && g_bGradient))) continue; // get the system color crRGB = GetSysColor(iSysColorIndices[iColor]); // ASSUME THAT YOU NEVER GET A BOGUS VALUE FROM THIS FUNCTION!! // translate to a string ColorToRGBString((LPTSTR)szMsg, crRGB); // write to theme file bRet = WritePrivateProfileString((LPTSTR)szCP_Clr, (LPTSTR)(pRegColors[iColor]), (LPTSTR)szMsg, lpszTheme); if (!bRet) bOK = FALSE; } // cleanup if (!bOK) bWroteOK = FALSE; return (bOK); } // // Reads from the theme and writes to the Registry. // At the same time, does the grody SetSysColor thing: // creates an array to set up the call. Makes the call. // BOOL SetSysColorsByHand() { LONG lret; HKEY hKey; // cur open key BOOL bOK = TRUE; COLORREF crSet[(sizeof(iSysColorIndices)/sizeof(int))]; int iColor; DWORD dwDisposition; // // inits Assert ((sizeof(iSysColorIndices)/sizeof(int)) == (sizeof(pRegColors)/sizeof(TCHAR *)), TEXT("mismatched color arrays in SetSysColorsByHand\n")); // have to keep setting cursor to wait because someone resets it WaitCursor(); // Open the color key if it exists, otherwise create it. lret = RegCreateKeyEx( HKEY_CURRENT_USER, (LPTSTR)szCP_Clr, (DWORD)0, (LPTSTR)szNULL, REG_OPTION_NON_VOLATILE, KEY_SET_VALUE, NULL, (PHKEY)&hKey, (LPDWORD)&dwDisposition ); if (lret != ERROR_SUCCESS) { Assert(FALSE, TEXT("problem on RegOpenKey in SetSysColorsByHand\n")); return (FALSE); } // // main process // go through each color valuename in your array for (iColor = 0; iColor < (sizeof(pRegColors)/sizeof(TCHAR *)); iColor ++) { // get color string from theme file GetPrivateProfileString((LPTSTR)szCP_Clr, (LPTSTR)(pRegColors[iColor]), (LPTSTR)szNULL, (LPTSTR)pValue, MAX_VALUELEN, (LPTSTR)szCurThemeFile); // If this is one of the Gradient Title bar settings and the setting // doesn't exist in the Theme file, read the non-gradient title setting // instead. if ((iColor == INDEX_GRADIENTACTIVE) && !*pValue) { GetPrivateProfileString((LPTSTR)szCP_Clr, (LPTSTR)(pRegColors[INDEX_ACTIVE]), (LPTSTR)szNULL, (LPTSTR)pValue, MAX_VALUELEN, (LPTSTR)szCurThemeFile); } if ((iColor == INDEX_GRADIENTINACTIVE) && !*pValue) { GetPrivateProfileString((LPTSTR)szCP_Clr, (LPTSTR)(pRegColors[INDEX_INACTIVE]), (LPTSTR)szNULL, (LPTSTR)pValue, MAX_VALUELEN, (LPTSTR)szCurThemeFile); } if (!(*pValue)) { // if nothing in theme, use cur sys colors crSet[iColor] = GetSysColor(iSysColorIndices[iColor]); continue; // null color value CONTINUE } // set color to Registry lret = RegSetValueEx(hKey, (LPTSTR)(pRegColors[iColor]), 0, (DWORD)REG_SZ, (LPBYTE)pValue, (DWORD)SZSIZEINBYTES(pValue)); Assert(lret == ERROR_SUCCESS, TEXT("bad return SetSysColorsByHand query\n")); if (ERROR_SUCCESS != lret) bOK = FALSE; // OK, you've got a str version of a COLOR. // Translate string and add to COLORREF array. crSet[iColor] = RGBStringToColor((LPTSTR)pValue); } // // There. You've finally got an array of color RGB values. Apply liberally. SystemParametersInfo(SPI_SETGRADIENTCAPTIONS, 0, IntToPtr(g_bGradient), SPIF_UPDATEINIFILE); SetSysColors((sizeof(iSysColorIndices)/sizeof(int)), iSysColorIndices, crSet); // // Cleanup RegCloseKey(hKey); return (bOK); } // // RGB to String to RGB utilities. // COLORREF FAR RGBStringToColor(LPTSTR lpszRGB) { LPTSTR lpszCur, lpszNext; BYTE bRed, bGreen, bBlue; #ifdef UNICODE CHAR szTempA[10]; #endif // inits lpszNext = lpszRGB; // set up R for translation lpszCur = lpszNext; while ((TEXT(' ') != *lpszNext) && *lpszNext) { lpszNext++; } *lpszNext = 0; lpszNext++; // get Red #ifdef UNICODE wcstombs(szTempA, (wchar_t *)lpszCur, sizeof(szTempA)); bRed = (BYTE)latoi(szTempA); #else // !UNICODE bRed = (BYTE)latoi(lpszCur); #endif // set up G for translation lpszCur = lpszNext; while ((TEXT(' ') != *lpszNext) && *lpszNext) { lpszNext++; } *lpszNext = 0; lpszNext++; // get Green #ifdef UNICODE wcstombs(szTempA, (wchar_t *)lpszCur, sizeof(szTempA)); bGreen = (BYTE)latoi(szTempA); #else // !UNICODE bGreen = (BYTE)latoi(lpszCur); #endif // set up B for translation lpszCur = lpszNext; while ((TEXT(' ') != *lpszNext) && *lpszNext) { lpszNext++; } *lpszNext = 0; lpszNext++; // get Blue #ifdef UNICODE wcstombs(szTempA, (wchar_t *)lpszCur, sizeof(szTempA)); bBlue = (BYTE)latoi(szTempA); #else // !UNICODE bBlue = (BYTE)latoi(lpszCur); #endif // OK, now combine them all for the big finish.....! return(RGB(bRed, bGreen, bBlue)); } void FAR ColorToRGBString(LPTSTR lpszRet, COLORREF crColor) { int iTemp; TCHAR szTemp[12]; #ifdef UNICODE CHAR szTempA[10]; #endif // first do R value iTemp = (int) GetRValue(crColor); #ifdef UNICODE litoa(iTemp, szTempA); mbstowcs(lpszRet, szTempA, sizeof(szTempA)); #else // !UNICODE litoa(iTemp, lpszRet); #endif // add on G value lstrcat(lpszRet, TEXT(" ")); iTemp = (int) GetGValue(crColor); #ifdef UNICODE litoa(iTemp, szTempA); mbstowcs(szTemp, szTempA, sizeof(szTempA)); #else // !UNICODE litoa(iTemp, szTemp); #endif lstrcat(lpszRet, (LPTSTR)szTemp); // add on B value lstrcat(lpszRet, TEXT(" ")); iTemp = (int) GetBValue(crColor); #ifdef UNICODE litoa(iTemp, szTempA); mbstowcs(szTemp, szTempA, sizeof(szTempA)); #else // !UNICODE litoa(iTemp, szTemp); #endif lstrcat(lpszRet, (LPTSTR)szTemp); // OK, you're done now } BOOL GatherWallpaperBitsByHand(LPTSTR lpszTheme) { BOOL bret, bOK = TRUE; // save TileWallpaper bret = HandGet(HKEY_CURRENT_USER, (LPTSTR)szCP_DT, (LPTSTR)szTileWP,(LPTSTR)pValue); Assert(bret, TEXT("couldn't get WallpaperStyle from Registry!\n")); bOK = bOK && bret; bret = WritePrivateProfileString((LPTSTR)szCP_DT, (LPTSTR)szTileWP, // only store if got something, else null (LPTSTR)(bret ? pValue : szNULL), lpszTheme); bOK = bOK && bret; if (!bret) bWroteOK = FALSE; // save Wallpaper Style bret = HandGet(HKEY_CURRENT_USER, (LPTSTR)szCP_DT, (LPTSTR)szWPStyle, (LPTSTR)pValue); Assert(bret, TEXT("couldn't get WallpaperStyle from Registry!\n")); bOK = bOK && bret; bret = WritePrivateProfileString((LPTSTR)szCP_DT, (LPTSTR)szWPStyle, // only store if got something, else null (LPTSTR)(bret ? pValue : szNULL), lpszTheme); bOK = bOK && bret; if (!bret) bWroteOK = FALSE; return (bOK); } // // *Path // // These routines help to make themes transportable between computers. // The problem is that the registry keeps filenames for the various // theme elements and, of course, these are hard-coded paths that vary // from machine to machine. // // The way we work around this problem is by storing filenames in the // theme file as _relative_ paths: relative to the theme file directory // or the Windows directory. (Actually, these routines are set up to // be relative to any number of directories.) When saving a filename to // a theme, we check to see if any relative paths can be abstracted out. // When retrieving a filename from a theme, we take the abstract placeholder // and replace it with the current sessions instances. // these must parallel each other. abstract strs must start with % TCHAR *szAbstractDirs[] = {szThemeDir, szWinDir, szWinDir}; TCHAR *szAbstractStrs[] = {TEXT("%ThemeDir%"), TEXT("%WinDir%"), TEXT("%SystemRoot%")}; // AbstractPath (see header above) // // Takes actual full path/filename and takes out a leading substring // that matches any of the paths to abstract, if any. // // lpszPath both input and output; assumes it's huge // VOID AbstractPath(LPTSTR lpszPath, int imax) { int iter, iAbstrDirLen; TCHAR szTemp[MAX_PATHLEN+1]; // check easy out first if (!lpszPath[0]) return; // easy out, nothing to change EXIT // paranoid init szTemp[MAX_PATHLEN] = 0; // look for each of the path prefixes we care about in the given string for (iter = 0; iter < (sizeof(szAbstractDirs)/sizeof(TCHAR *)); iter++ ) { // inits iAbstrDirLen = lstrlen((LPTSTR)(szAbstractDirs[iter])); // get beginning of passed path string lstrcpyn((LPTSTR)szTemp, lpszPath, // ******************************** // LSTRCPYN issue: doc says that N specifies number of chars, not // including the terminating null char. Behavior seems to include // the null char, so I add one here. If the Win libs are modified // to match their doc, then this will become a bug. // ******************************** iAbstrDirLen + 1); // compare to path prefix to abstract if (!lstrcmpi((LPTSTR)szTemp, (LPTSTR)(szAbstractDirs[iter]))) { // // GOT A MATCH: now do the substitution lstrcpy((LPTSTR)szTemp, (LPTSTR)(szAbstractStrs[iter])); // start w/ abstract key lstrcat((LPTSTR)szTemp, (LPTSTR)(lpszPath + iAbstrDirLen)); // rest of path lstrcpy(lpszPath, (LPTSTR)szTemp); // copy the result to ret str // now get out return; // got yer result now EXIT } } // if you didn't get a match, then you're just returning the same path str } // InstantiatePath (see header above) // // Takes theme file's version of path/filename and looks for leading abstraction // string that matches any of the known abstractions, replacing it with // current system's equivalent if found. // // lpszPath both input and output; assumes it's huge // VOID FAR InstantiatePath(LPTSTR lpszPath, int imax) { int iter, iAbstrStrLen; TCHAR szTemp[MAX_PATHLEN+1]; // easy outs if ((TEXT('%') != lpszPath[0]) || !lpszPath[0]) return; // easy out, nothing to change EXIT // paranoid init szTemp[MAX_PATHLEN] = 0; // look for each of the possible abstraction prefixes in the given string for (iter = 0; iter < (sizeof(szAbstractStrs)/sizeof(TCHAR *)); iter++ ) { // inits iAbstrStrLen = lstrlen((LPTSTR)(szAbstractStrs[iter])); // get beginning of passed path string lstrcpyn((LPTSTR)szTemp, lpszPath, // ******************************** // LSTRCPYN issue: doc says that N specifies number of chars, not // including the terminating null char. Behavior seems to include // the null char, so I add one here. If the Win libs are modified // to match their doc, then this will become a bug. // ******************************** iAbstrStrLen + 1); // compare to this abstraction key string if (!lstrcmpi((LPTSTR)szTemp, (LPTSTR)(szAbstractStrs[iter]))) { // // GOT A MATCH: now do the substitution lstrcpy((LPTSTR)szTemp, (LPTSTR)(szAbstractDirs[iter])); // actual path prefix // Avoid the double backslash problem if (lpszPath[iAbstrStrLen] == TEXT('\\')) iAbstrStrLen++; lstrcat((LPTSTR)szTemp, (LPTSTR)(lpszPath + iAbstrStrLen)); // rest of path lstrcpy(lpszPath, (LPTSTR)szTemp); // copy the result to ret str // now get out return; // got yer result now EXIT } } // On NT there is one more abstraction we need to worry about but // we can't add it to the array of abstraction strings -- doing // so would cause us to write it out to Theme files which would // make the theme files not backward compatible. // // %SystemDrive% // if (IsPlatformNT()) { // inits iAbstrStrLen = lstrlen((LPTSTR)(TEXT("%SystemDrive%"))); // get beginning of passed path string lstrcpyn((LPTSTR)szTemp, lpszPath, // ******************************** // LSTRCPYN issue: doc says that N specifies number of chars, not // including the terminating null char. Behavior seems to include // the null char, so I add one here. If the Win libs are modified // to match their doc, then this will become a bug. // ******************************** iAbstrStrLen + 1); // compare to this abstraction key string if (!lstrcmpi((LPTSTR)szTemp, (LPTSTR)(TEXT("%SystemDrive%")))) { // // GOT A MATCH: now do the substitution szTemp[0] = szWinDir[0]; // drive letter 'C' szTemp[1] = szWinDir[1]; // colon ':' szTemp[2] = TEXT('\0'); // null lstrcat((LPTSTR)szTemp, (LPTSTR)(lpszPath + iAbstrStrLen)); // rest of path lstrcpy(lpszPath, (LPTSTR)szTemp); // copy the result to ret str // now get out return; // got yer result now EXIT } } // if you didn't get a match, then you're just returning the same path str } // // ConfirmFile // // This function does the "smart" file searching that's supposed to be // built into each resource file reference in applying themes. // // First see if the full pathname + file given actually exists. // If it does not, then try looking for the same filename (stripped from path) // in other standard directories, in this order: // Current Theme file directory // Theme switcher THEMES subdirectory // Windows directory // Windows/MEDIA directory // Windows/CURSORS directory // Windows/SYSTEM directory // // Input: LPTSTR lpszPath full pathname // BOOL bUpdate whether to alter the filename string with found file // Returns: int flag telling if and how file has been confirmed // CF_EXISTS pathname passed in was actual file // CF_FOUND file did not exist, but found same filename elsewhere // CF_NOTFOUND file did not exist, could not find elsewhere // int FAR ConfirmFile(LPTSTR lpszPath, BOOL bUpdate) { TCHAR szWork[MAX_PATHLEN+1]; TCHAR szTest[MAX_PATHLEN+1]; int iret = CF_NOTFOUND; // default value LPTSTR lpFile; LPTSTR lpNumber; HANDLE hTest; // special case easy return: if it's null, then trivially satisfied. if (!*lpszPath) return (CF_EXISTS); // NO WORK EXIT // // Inits // copy pathname to a work string for the function lstrcpy((LPTSTR)szWork, lpszPath); // input can be of the form foo.dll,13. need to strip off that comma,# // but hold onto it to put back at the end if we change the pathname lpNumber = FindChar(szWork, TEXT(',')); if (*lpNumber) { // if there is a comma lpFile = lpNumber; // temp lpNumber = CharNext(lpNumber);// hold onto number *lpFile = 0; } // // Do the checks // *** first check if the given file just exists as is hTest = CreateFile(szWork, GENERIC_READ, FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); if (hTest != INVALID_HANDLE_VALUE) { // success iret = CF_EXISTS; // assign ret value // don't need to worry about bUpdate: found with input string } // otherwise, let's go searching for the same filename in other dirs else { Assert(FALSE, TEXT("had to go looking for ")); Assert(FALSE, szWork); Assert(FALSE, TEXT("....\n")); // get ptr to the filename separated from the path lpFile = FileFromPath(szWork); // *** try the cur theme file dir lstrcpy((LPTSTR)szTest, (LPTSTR)szCurDir); lstrcat((LPTSTR)szTest, lpFile); hTest = CreateFile((LPTSTR)szTest, GENERIC_READ, FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); if (hTest != INVALID_HANDLE_VALUE) { // success iret = CF_FOUND; // assign ret value Assert(FALSE, TEXT(" OK, found it in cur theme file dir\n")); } // *** otherwise try the Theme switcher THEMES subdirectory else { lstrcpy((LPTSTR)szTest, (LPTSTR)szThemeDir); lstrcat((LPTSTR)szTest, lpFile); hTest = CreateFile((LPTSTR)szTest, GENERIC_READ, FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); if (hTest != INVALID_HANDLE_VALUE) { // success iret = CF_FOUND; // assign ret value Assert(FALSE, TEXT(" OK, found it in themes directory\n")); } // *** otherwise try the win dir else { lstrcpy((LPTSTR)szTest, (LPTSTR)szWinDir); lstrcat((LPTSTR)szTest, lpFile); hTest = CreateFile((LPTSTR)szTest, GENERIC_READ, FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); if (hTest != INVALID_HANDLE_VALUE) { // success iret = CF_FOUND; // assign ret value Assert(FALSE, TEXT(" OK, found it in windows directory\n")); } // *** otherwise try the win/media dir else { // can get this one directly from Registry HandGet(HKEY_LOCAL_MACHINE, TEXT("SOFTWARE\\Microsoft\\Windows\\CurrentVersion"), TEXT("MediaPath"), (LPTSTR)szTest); #ifdef THEYREMOVEREGSETTING lstrcpy((LPTSTR)szTest, (LPTSTR)szWinDir); lstrcat((LPTSTR)szTest, TEXT("Media\\")); #endif lstrcat((LPTSTR)szTest, TEXT("\\")); lstrcat((LPTSTR)szTest, lpFile); hTest = CreateFile((LPTSTR)szTest, GENERIC_READ, FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); if (hTest != INVALID_HANDLE_VALUE) { // success iret = CF_FOUND; // assign ret value Assert(FALSE, TEXT(" OK, found it in windows media directory\n")); } // *** otherwise try the win/cursors dir else { lstrcpy((LPTSTR)szTest, (LPTSTR)szWinDir); lstrcat((LPTSTR)szTest, TEXT("CURSORS\\")); lstrcat((LPTSTR)szTest, lpFile); hTest = CreateFile((LPTSTR)szTest, GENERIC_READ, FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); if (hTest != INVALID_HANDLE_VALUE) { // success iret = CF_FOUND; // assign ret value Assert(FALSE, TEXT(" OK, found it in windows cursors directory\n")); } // *** otherwise try the win/system dir else { lstrcpy((LPTSTR)szTest, (LPTSTR)szWinDir); lstrcat((LPTSTR)szTest, TEXT("SYSTEM\\")); lstrcat((LPTSTR)szTest, lpFile); hTest = CreateFile((LPTSTR)szTest, GENERIC_READ, FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); if (hTest != INVALID_HANDLE_VALUE) { // success iret = CF_FOUND; // assign ret value Assert(FALSE, TEXT(" OK, found it in windows system directory\n")); } // *** otherwise try the win/system32 dir else { lstrcpy((LPTSTR)szTest, (LPTSTR)szWinDir); lstrcat((LPTSTR)szTest, TEXT("SYSTEM32\\")); lstrcat((LPTSTR)szTest, lpFile); hTest = CreateFile((LPTSTR)szTest, GENERIC_READ, FILE_SHARE_READ, (LPSECURITY_ATTRIBUTES)NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, (HANDLE)NULL); if (hTest != INVALID_HANDLE_VALUE) { // success iret = CF_FOUND; // assign ret value Assert(FALSE, TEXT(" OK, found it in windows system32 directory\n")); } } } } } } } // if found anywhere other than orig, copy found path/str as requested if ((iret == CF_FOUND) && bUpdate) { lstrcpy(lpszPath, (LPTSTR)szTest); // if we stripped off a number, let's add it back on if (*lpNumber) { lstrcat(lpszPath, TEXT(",")); lstrcat(lpszPath, lpNumber); } } // endif found file by searching } // cleanup Assert(iret != CF_NOTFOUND, TEXT(" But never found it!\n")); if (iret != CF_NOTFOUND) CloseHandle(hTest); // close file if opened return (iret); } #ifdef ACTUALLY_DOING_THIS // // SetCheckboxesFromThemeFile // // After a new theme is selected from the listbox, you have to update // the checkboxes in the main dlg window: // uncheck and disable those that are not used in the theme // check and enable those that are used in the theme // // Some of the checkboxes will always be enabled. // Some of the checkboxes require a check of a sequence of settings // (e.g. the sound events) and enabling iff any of them are used. // void FAR SetCheckboxesFromThemeFile(LPTSTR lpszFilename) { } // // SetCheckboxesFromRegistry // // Similar to SetCheckboxesFromThemeFile(), but for the case of // "Current Windows settings" there is no file -- you just query // the cur registry to get all the same values. // void FAR SetCheckboxesFromRegistry() { } #endif // // CheckSpace // // Checks if there is enough space on drive to apply theme file. // BOOL FAR CheckSpace (HWND hWnd, BOOL fComplain) { // // Step 1. Calculate Worst case Space needed // // 4 48 x 48 hicolor icons (10K each worst case) + (10K theme file) + padding // Multiple by 2 for Prev and Curr Themes #define FUDGE_SIZE (2L << 16) HDC hdc; ULONG cHorz; ULONG cVert; ULONG cPlanes; ULONG cBPP; ULONG fFlags; ULONG cbNeeded; ULONG cPalSize; ULONG cColorDepth; TCHAR szTemp[MAX_MSGLEN+1]; TCHAR szMsg[MAX_MSGLEN+1]; HANDLE hFile; WIN32_FIND_DATA fd; TCHAR chDrive; TCHAR szDrive[4]; ULONG ckNeeded; ULONG cbAvail; DWORD csCluster; DWORD cbSector; DWORD ccFree; DWORD ccTotal; hdc = GetDC (HWND_DESKTOP); cHorz = GetDeviceCaps (hdc, HORZRES); cVert = GetDeviceCaps (hdc, VERTRES); cPlanes = GetDeviceCaps (hdc, PLANES); cBPP = GetDeviceCaps (hdc, BITSPIXEL); fFlags = GetDeviceCaps (hdc, RASTERCAPS); cPalSize = 3L; cColorDepth = cPlanes * cBPP; if ((fFlags & RC_PALETTE) == RC_PALETTE) { cPalSize = GetDeviceCaps (hdc, SIZEPALETTE); } ReleaseDC (HWND_DESKTOP, hdc); // Get Worst case size of Plus! bitmap cbNeeded = (cHorz * cVert * cColorDepth)/8L; // Add in Bitmap File Header cbNeeded += sizeof (BITMAPFILEHEADER); // Add in Bitmap Info Header cbNeeded += sizeof (BITMAPINFOHEADER); // Add in worst case palette size cbNeeded += sizeof (RGBQUAD) * cPalSize; // Add in Fudge factor cbNeeded += FUDGE_SIZE; // // Step 2. Is there a current Plus! bitmap ?!? // Subtract it's size from our requirements // GetPlusBitmapName (szTemp); hFile = FindFirstFile(szTemp, &fd); if (hFile != INVALID_HANDLE_VALUE) { // Make sure it isn't larger than we know what to do with if (!fd.nFileSizeHigh) { if (cbNeeded > fd.nFileSizeLow) cbNeeded -= fd.nFileSizeLow; else { // Just to be safe we need some space cbNeeded = FUDGE_SIZE; } } FindClose (hFile); } // // Step 3. Get Space available on drive // chDrive = szTemp[0]; szDrive[0] = chDrive; szDrive[1] = TEXT(':'); szDrive[2] = TEXT('\\'); szDrive[3] = 0; if (! GetDiskFreeSpace (szDrive, &csCluster, &cbSector, &ccFree, &ccTotal)) return FALSE; cbAvail = ccFree * csCluster * cbSector; // // Step 3. Is there a problem ?!? // if (cbAvail < cbNeeded) { // Inform User ?!? if (fComplain) { // Let user know about space problem ckNeeded = cbNeeded/1024L; LoadString (hInstApp, STR_ERRNEEDSPACE, (LPTSTR)szTemp, MAX_MSGLEN); wsprintf ((LPTSTR)szMsg, (LPTSTR)szTemp, chDrive, ckNeeded, chDrive); MessageBox((HWND)hWnd, (LPTSTR)szMsg, (LPTSTR)szAppName, MB_OK | MB_ICONERROR | MB_APPLMODAL); } // Don't bother to do any work return FALSE; } return TRUE; } // GatherICONS // BOOL GatherICONS(LPCTSTR lpszThemefile) { DWORD ikey; // index for FROST_SUBKEY arrary DWORD dwMaxKey; // number of FROST_SUBKEY's in array int ival; // index for FROST_VALUE array DWORD dwType; // type of reg key DWORD dwSize; // size of reg key HKEY hKeyCU; // handle to CURRENT_USER key HKEY hKeyR; // handle to CLASSES_ROOT key BOOL bGotCU; // Do we have a good CU handle? BOOL bGotR; // Do we have a good ROOT handle? BOOL bGotValCU; // We got the value from the CU key BOOL bGotValR; // We got the value from the R key BOOL bRet; // Return from bool function BOOL bOK = TRUE; // cumulative return code for this function LONG lret; // function result TCHAR szNTReg[MAX_PATH]; // Reg path to use for NT dwMaxKey = sizeof(fsCUIcons)/sizeof(FROST_SUBKEY); // loop through each subkey in the fsCUIcons() enumeration for (ikey = 0; ikey < dwMaxKey; ikey++) { // The icon information is typically kept in the CURRENT_USER // branch, but if we don't find it there we need to check the // CLASSES_ROOT branch as well. // // On the NT Platform we check the c_szSoftwareClassesFmt reg // path first (instead of CURRENT_USER/fsCUIcons) then try the // CLASSES_ROOT branch. // Try to open the appropriate CURRENT_USER subkey for this platform if (IsPlatformNT()) { lstrcpy(szNTReg, c_szSoftwareClassesFmt); lstrcat(szNTReg, fsRoot[ikey].szSubKey); lret = RegOpenKeyEx(HKEY_CURRENT_USER, szNTReg, (DWORD)0, KEY_QUERY_VALUE, (PHKEY)&hKeyCU); } else // Not NT so don't use the touched-up current_user path { lret = RegOpenKeyEx(HKEY_CURRENT_USER, (LPTSTR)fsCUIcons[ikey].szSubKey, (DWORD)0, KEY_QUERY_VALUE, (PHKEY)&hKeyCU); } if (lret != ERROR_SUCCESS) bGotCU = FALSE; else bGotCU = TRUE; // Try to open the CLASSES_ROOT subkey lret = RegOpenKeyEx(HKEY_CLASSES_ROOT, (LPTSTR)fsRoot[ikey].szSubKey, (DWORD)0, KEY_QUERY_VALUE, (PHKEY)&hKeyR); if (lret != ERROR_SUCCESS) bGotR = FALSE; else bGotR = TRUE; // If we couldn't open a key in either the CU or R branch then // we should write a null value to the Theme file. if (!bGotCU && !bGotR) { // (null loop if default string only) for (ival = 0; ival < fsCUIcons[ikey].iNumVals; ival++) { bRet = WritePrivateProfileString( fsCUIcons[ikey].szSubKey, (LPTSTR)fsCUIcons[ikey].fvVals[ival].szValName, (LPTSTR)szNULL, lpszThemefile); bOK = bOK && bRet; } if (fsCUIcons[ikey].fValues != FV_LIST) { // either def or list+def bRet = WritePrivateProfileString( fsCUIcons[ikey].szSubKey, (LPTSTR)FROST_DEFSTR, (LPTSTR)szNULL, lpszThemefile); bOK = bOK && bRet; } continue; // Failed to OPEN reg key so continue on to next ikey } // Assume that we successfully opened either the CU or R subkey // treat depending on type of values for this subkey switch (fsCUIcons[ikey].fValues) { case FV_LIST: case FV_LISTPLUSDEFAULT: // loop through each value in the list for this subkey for (ival = 0; ival < fsCUIcons[ikey].iNumVals; ival++) { bGotValCU = FALSE; if (bGotCU) { dwSize = (DWORD)(MAX_VALUELEN * sizeof(TCHAR)); lret = RegQueryValueEx( hKeyCU, fsCUIcons[ikey].fvVals[ival].szValName, (LPDWORD)NULL, (LPDWORD)&dwType, (LPBYTE)pValue, (LPDWORD)&dwSize); if (lret == ERROR_SUCCESS) { bGotValCU = TRUE; if (REG_EXPAND_SZ == dwType) ExpandSZ(pValue); } } // If we have a CLASSES_ROOT handle AND: // // * We failed to read from CU OR // * We got a NULL string from CU // // Try reading from the CR branch instead: bGotValR = FALSE; if ((bGotR) && (!bGotValCU || !*pValue)) { dwSize = (DWORD)(MAX_VALUELEN * sizeof(TCHAR)); lret = RegQueryValueEx( hKeyR, fsRoot[ikey].fvVals[ival].szValName, (LPDWORD)NULL, (LPDWORD)&dwType, (LPBYTE)pValue, (LPDWORD)&dwSize); if (lret == ERROR_SUCCESS) { bGotValR = TRUE; if (REG_EXPAND_SZ == dwType) ExpandSZ(pValue); } } if (!bGotValCU && !bGotValR) { // Failed to get value from either CU or R so write // a null string to the Theme file bRet = WritePrivateProfileString( fsCUIcons[ikey].szSubKey, (LPTSTR)fsCUIcons[ikey].fvVals[ival].szValName, (LPTSTR)szNULL, lpszThemefile); bOK = bOK && bRet; continue; // Next ival } // Assume we got the value from either the CU or R key // Regardless of which one we *got* it from we'll write it // out to the Theme file as if it came from the CURRENT USER // branch if (fsCUIcons[ikey].fvVals[ival].bValRelPath) AbstractPath((LPTSTR)pValue, MAX_VALUELEN); bRet = WritePrivateProfileString( fsCUIcons[ikey].szSubKey, (LPTSTR)fsCUIcons[ikey].fvVals[ival].szValName, (LPTSTR)pValue, lpszThemefile); bOK = bOK && bRet; } // End for ival loop // check if just list or list plus default if (FV_LIST == fsCUIcons[ikey].fValues) break; // normal EXIT // else fall through and do default, too case FV_DEFAULT: // // Default string: There are no "valuenames" to search for under // this key. // // First try getting the default string from the CU key bGotValCU = FALSE; if (bGotCU) { dwSize = (DWORD)(MAX_VALUELEN * sizeof(TCHAR)); lret = RegQueryValueEx(hKeyCU, (LPTSTR)szNULL,// null str to get default (LPDWORD)NULL, (LPDWORD)&dwType, (LPBYTE)pValue, // getting actual def value (LPDWORD)&dwSize); if (ERROR_SUCCESS == lret) { bGotValCU = TRUE; if (REG_EXPAND_SZ == dwType) ExpandSZ(pValue); } } // If we have a CLASSES_ROOT handle AND: // // * We failed to read from CU OR // * We got a NULL string from CU // // Try reading from the CR branch instead: bGotValR = FALSE; if ((bGotR) && (!bGotValCU || !*pValue)) { dwSize = (DWORD)(MAX_VALUELEN * sizeof(TCHAR)); lret = RegQueryValueEx(hKeyR, (LPTSTR)szNULL,// null str to get default (LPDWORD)NULL, (LPDWORD)&dwType, (LPBYTE)pValue, // getting actual def value (LPDWORD)&dwSize); if (ERROR_SUCCESS == lret) { bGotValR = TRUE; if (REG_EXPAND_SZ == dwType) ExpandSZ(pValue); } } if (!bGotValCU && !bGotValR) { // Failed to get the default value from either the CU or R key *pValue = TEXT('\0'); // Set pValue to null string } // OK, if this is a path/filename, see about xlating to relative path if (fsCUIcons[ikey].bDefRelPath) AbstractPath((LPTSTR)pValue, MAX_VALUELEN); // // Phew, finally. Write single default value // bRet = WritePrivateProfileString((LPTSTR)(fsCUIcons[ikey].szSubKey), (LPTSTR)FROST_DEFSTR, (LPTSTR)pValue, lpszThemefile); bOK = bOK && bRet; break; default: Assert(FALSE, TEXT("Unlisted .fValues value in GatherICONS!\n")); break; } // End switch // close the keys if appropriate if (bGotR) RegCloseKey(hKeyR); if (bGotCU) RegCloseKey(hKeyCU); } // End for ikey return bOK; } // ApplyWebView // // For each setting in the [WebView] portion of the *.Theme // file copy the specified file into the \windir\web directory. // // If there is no setting, extract the resource from the WEBVW.DLL. // // [WebView] // // WVLEFT.BMP = filename.bmp // Webview top left "watermark" // WVLINE.GIF = filename.gif // Webview line in top left corner // WVLOGO.GIF = filename.gif // Webview gears & win98 logo // // Params: Full path to *.Theme file. // // Returns: FALSE if major catastrophe // TRUE if things (sort of) went OK // BOOL ApplyWebView(LPCTSTR szThemefile) { DWORD dwI = 0; // Index into szWVNames array DWORD dwResult = 0; // Result of function call TCHAR szWinDirWeb[MAX_PATH]; // Path to \windir\web TCHAR szWinDirWebFile[MAX_PATH]; // Full path to WebView art file TCHAR szBuffer[MAX_PATH]; // Read from *.Theme file TCHAR szTempPath[MAX_PATH]; // Path to temp directory TCHAR szTempFile[MAX_PATH]; // Full path to temporary file // Initialize the path to the \windir\web directory where we'll // put the WebView artwork files if (!GetWindowsDirectory(szWinDirWeb, MAX_PATH)) { // This is bad -- we can't find the windows directory?! Abandon ship. return FALSE; } lstrcat(szWinDirWeb, TEXT("\\Web\0")); // Get a temp filename where we can store the resource we extract // out of WEBVW.DLL (if we need to). // First the path to temp dir if (!GetTempPath(MAX_PATH, szTempPath)) { // This is bad -- we can't find the temp directory?! Abandon ship. return FALSE; } // Now a temp filename if (!GetTempFileName(szTempPath, TEXT("THM"), 0, szTempFile)) { // Couldn't get a temp file? Not likely but if so bail out... return FALSE; } // For each potential [WebView] setting in the Theme file do // this stuff... for (dwI = 0; dwI < MAX_WVNAMES; dwI++) { // Get the current setting from the *.Theme file if the setting // exists GetPrivateProfileString(TEXT("WebView"), szWVNames[dwI], TEXT("\0"), szBuffer, MAX_PATH, szThemefile); // Instantiate the path InstantiatePath(szBuffer, MAX_PATH); // Now check to see if this file even exists dwResult = GetFileAttributes(szBuffer); // If GFA failed we need to extract this file from webvw.dll if (0xFFFFFFFF == dwResult) { if (ExtractWVResource(szWVNames[dwI], szTempFile)) { // We successfully extracted the resource into TempFile. // Now copy it to the ultimate destination. // Create a path to the \windir\web\file lstrcpy(szWinDirWebFile, szWinDirWeb); lstrcat(szWinDirWebFile, TEXT("\\")); lstrcat(szWinDirWebFile, szWVNames[dwI]); // Copy the file DeleteFile(szWinDirWebFile); CopyFile(szTempFile, szWinDirWebFile, FALSE); // Delete the temporary file DeleteFile(szTempFile); } } // End if GFA failed else { // The .Theme file exists so we need to copy it to the Web dir // Create a path to the \windir\web\file lstrcpy(szWinDirWebFile, szWinDirWeb); lstrcat(szWinDirWebFile, TEXT("\\")); lstrcat(szWinDirWebFile, szWVNames[dwI]); DeleteFile(szWinDirWebFile); CopyFile(szBuffer, szWinDirWebFile, FALSE); } } // End for dwI loop // Cleanup the temp file DeleteFile(szTempFile); return TRUE; // this isn't very meaningful... } // GatherWebView // // Collect the current WebView artwork files, store them in the // theme dir under the appropriate name, and save the settings // in the *.Theme file under the appropriate setting. // // [WebView] // // WVLEFT.BMP = Theme name WVLEFT.BMP // Webview top left "watermark" // WVLINE.GIF = Theme name WVLINE.GIF // Webview line in top left corner // WVLOGO.GIF = Theme name WVLOGO.GIF // Webview gears & win98 logo // // Params: Full path to *.Theme file. // // Returns: FALSE if major catastrophe // TRUE if things sort of went OK // BOOL GatherWebView(LPCTSTR szThemefile) { DWORD dwI = 0; // Index into szWVNames array TCHAR szWinDirWeb[MAX_PATH]; // Path to \windir\web TCHAR szWinDirWebFile[MAX_PATH]; // Full path to WebView art file TCHAR szSaveFile[MAX_PATH]; // Full path to destination file // Initialize the path to the \windir\web directory where we'll // get the WebView artwork files if (!GetWindowsDirectory(szWinDirWeb, MAX_PATH)) { // This is bad -- we can't find the windows directory?! Abandon ship. return FALSE; } lstrcat(szWinDirWeb, TEXT("\\Web\0")); // For each potential [WebView] setting in the Theme file do // this stuff... for (dwI = 0; dwI < MAX_WVNAMES; dwI++) { // Verify that we have a file for the current setting lstrcpy(szWinDirWebFile, szWinDirWeb); lstrcat(szWinDirWebFile, TEXT("\\")); lstrcat(szWinDirWebFile, szWVNames[dwI]); if (GetFileAttributes(szWinDirWebFile)) { // We've got a file so let's save it to the theme dir // under a unique name if (GetWVFilename(szThemefile, szWVNames[dwI], szSaveFile)) { if (CopyFile(szWinDirWebFile, szSaveFile, FALSE)) { SetFileAttributes(szSaveFile, FILE_ATTRIBUTE_ARCHIVE); AbstractPath(szSaveFile, MAX_PATH); WritePrivateProfileString(TEXT("WebView"), szWVNames[dwI], szSaveFile, szThemefile); } } } } return TRUE; } // ExtractWVResource // // Extracts the specified custom resource from the windir\system\webvw.dll // file and stores it in the specified destination file. // // If the destination file exists it is overwritten. // // Params: lpszResource -- Resource name to extract (i.e. WVLEFT.BMP) // lpszDestination -- File to save resource to // // Returns: True if successful, False if not. // BOOL ExtractWVResource(LPCTSTR lpszResource, LPCTSTR lpszDestination) { HINSTANCE hInstWVDLL = NULL; // Instance handle for WEBVW.DLL HRSRC hRsrc = NULL; // Handle to resource in WEBVW.DLL HGLOBAL hGlobal = NULL; // Global handle to loaded resource LPVOID lpResource = NULL; // Memory pointer to locked resource DWORD dwRSize = 0; // Size of resource DWORD dwBytesW = 0; // Number of bytes written to dest file HANDLE hFile = NULL; // Handle to destination file TCHAR szWebVWDLL[MAX_PATH]; // Full path to \windir\system\webvw.dll DWORD dwResult; // Result of function call // Build full path to \windir\system\webvw.dll if (!GetWindowsDirectory(szWebVWDLL, MAX_PATH)) { // This is bad -- we can't find the windows directory?! Abandon ship. return FALSE; } if (IsPlatformNT()) { lstrcat(szWebVWDLL, TEXT("\\SYSTEM32\\WEBVW.DLL\0")); } else { lstrcat(szWebVWDLL, TEXT("\\SYSTEM\\WEBVW.DLL\0")); } // Load WEBVW.DLL hInstWVDLL = NULL; hInstWVDLL = LoadLibrary(szWebVWDLL); if (!hInstWVDLL) { return FALSE; } // Find the desired resource in WEBVW.DLL hRsrc = NULL; hRsrc = FindResource(hInstWVDLL, lpszResource, TEXT("#23") /*Resource Type*/); if (!hRsrc) { FreeLibrary(hInstWVDLL); return FALSE; } // Load the resource into memory hGlobal = NULL; hGlobal = LoadResource(hInstWVDLL, hRsrc); if (!hGlobal) { FreeLibrary(hInstWVDLL); return FALSE; } // Figure out how big the resource is. dwRSize = 0; dwRSize = SizeofResource(hInstWVDLL, hRsrc); if (!dwRSize) { FreeLibrary(hInstWVDLL); return FALSE; } // Get a memory pointer to and lock the resource lpResource = NULL; lpResource = LockResource(hGlobal); if (!lpResource) { FreeLibrary(hInstWVDLL); return FALSE; } // Get a handle to the destination file hFile = CreateFile(lpszDestination, GENERIC_WRITE, FILE_SHARE_READ, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_ARCHIVE, NULL); if (INVALID_HANDLE_VALUE == hFile) { FreeLibrary(hInstWVDLL); return FALSE; } // Write the full resource into the destination file dwBytesW = 0; dwResult = 0; dwResult = WriteFile(hFile, lpResource, dwRSize, &dwBytesW, NULL); // Problems writing the resource? if ((!dwResult) || (dwRSize != dwBytesW)) { CloseHandle(hFile); DeleteFile(lpszDestination); FreeLibrary(hInstWVDLL); return FALSE; } // Cleanup and hit the road CloseHandle(hFile); FreeLibrary(hInstWVDLL); return TRUE; } // GetWVFilename // // Given a Themefile (with path info), a WebView file name (i.e. WVLEFT.BMP), // and a pointer to a string buffer, build a name for the Theme WebView file. // // For example: // // lpszThemefile = C:\Program Files\Plus!\Themes\Sports.Theme // lpszWVName = WVLOGO.GIF // // Result: // // lpszWVFile = "C:\Program Files\Plus!\Themes\Sports WVLOGO.GIF" // // NOTE: lpszWVFile does not have the double quotes in it -- I put // them in this comment for clarity. // // Params: // // lpszThemefile -- path/name of theme file // lpszWVName -- name of WebView artwork file // lpszWVFile -- destination buffer to hold final file name // // Returns: TRUE if lpszWVFile is valid name, else FALSE BOOL GetWVFilename(LPCTSTR lpszThemefile, LPCTSTR lpszWVName, LPTSTR lpszWVFile) { LPTSTR lpszThemeName = NULL; // Pointer to Theme filename in path LPTSTR Begin; LPTSTR Current; LPTSTR End; // Take the easy out if we got bogus params if (!lpszThemefile || !*lpszThemefile || !lpszWVName || !*lpszWVName || !lpszWVFile) { return FALSE; } if (GetFullPathName(lpszThemefile, MAX_PATH, lpszWVFile, &lpszThemeName)) { // Remove the extension from the Theme name -- go to the // end of the string then back up to the first ".". Current = lpszWVFile; while (*Current) Current = CharNext(Current); End = Current; // Current now points to the end of lpszWVFile -- back up to the // first '.' (the extension marker). Begin = lpszWVFile; Current = CharPrev(Begin, Current); while ((Current > Begin) && (*Current != TEXT('.'))) Current = CharPrev(Begin, Current); if (Current >= Begin) *Current = TEXT('\0'); // Append a space followed by the WebView file name lstrcat(lpszWVFile, TEXT(" ")); lstrcat(lpszWVFile, lpszWVName); } return TRUE; } VOID ExpandSZ(LPTSTR pszSrc) { LPTSTR pszTmp; Assert(FALSE, TEXT("GOT EXPAND_SZ -- Before: ")); Assert(FALSE, pszSrc); Assert(FALSE, TEXT("\n")); pszTmp = (LPTSTR)GlobalAlloc(GPTR, (MAX_PATH * sizeof(TCHAR))); Assert(pszTmp, TEXT("THEMES: Error allocating memory in ExpandSZ()\n")); if (pszTmp) { if (ExpandEnvironmentStrings(pszSrc, pszTmp, MAX_PATH)) { lstrcpy(pszSrc, pszTmp); } GlobalFree(pszTmp); } Assert(FALSE, TEXT("GOT EXPAND_SZ -- After: ")); Assert(FALSE, pszSrc); Assert(FALSE, TEXT("\n")); return; }