mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
3425 lines
115 KiB
3425 lines
115 KiB
/* 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;
|
|
}
|