windows-nt/Source/XPSP1/NT/shell/osshell/themes/themesw/regutils.c
2020-09-26 16:20:57 +08:00

3426 lines
115 KiB
C

/* 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;
}