/*****************************************************************************\ FILE: ThemeFile.cpp DESCRIPTION: This is the Autmation Object to theme scheme object. BryanSt 4/3/2000 (Bryan Starbuck) Copyright (C) Microsoft Corp 2000-2000. All rights reserved. \*****************************************************************************/ #include "priv.h" #include #include #include "ThSettingsPg.h" #include "ThemeFile.h" LPCTSTR s_pszCursorArray[SIZE_CURSOR_ARRAY] = { // different cursors TEXT("Arrow"), TEXT("Help"), TEXT("AppStarting"), TEXT("Wait"), TEXT("NWPen"), TEXT("No"), TEXT("SizeNS"), TEXT("SizeWE"), TEXT("Crosshair"), TEXT("IBeam"), TEXT("SizeNWSE"), TEXT("SizeNESW"), TEXT("SizeAll"), TEXT("UpArrow"), TEXT("Link"), }; // This is a list of string pairs. The first string in the pair is the RegKey and the second is the default sound. // NULL means to delete the key. If you use an environment string other than "%SystemRoot%", you need to // update _ApplySounds(); #define SOUND_DEFAULT (UINT)-1 THEME_FALLBACK_VALUES s_ThemeSoundsValues[SIZE_SOUNDS_ARRAY] = { {TEXT("AppEvents\\Schemes\\Apps\\.Default\\.Default\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\AppGPFault\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\Close\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\DeviceConnect\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\DeviceDisconnect\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\DeviceFail\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\LowBatteryAlarm\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\MailBeep\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\Maximize\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\MenuCommand\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\MenuPopup\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\Minimize\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\Open\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\PrintComplete\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\RestoreDown\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\RestoreUp\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\RingIn\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\Ringout\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\SystemAsterisk\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\SystemExclamation\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\SystemExit\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\SystemHand\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\SystemNotification\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\SystemQuestion\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\SystemStart\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\SystemStartMenu\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\WindowsLogoff\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\.Default\\WindowsLogon\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\Explorer\\EmptyRecycleBin\\.Current"), SOUND_DEFAULT}, {TEXT("AppEvents\\Schemes\\Apps\\Explorer\\Navigating\\.Current"), SOUND_DEFAULT}, }; //=========================== // *** Class Internals & Helpers *** //=========================== HRESULT CThemeFile::_GetCustomFont(LPCTSTR pszFontName, LOGFONT * pLogFont) { HRESULT hr = S_OK; TCHAR szFont[MAX_PATH]; if (GetPrivateProfileString(SZ_INISECTION_METRICS, pszFontName, SZ_EMPTY, szFont, ARRAYSIZE(szFont), m_pszThemeFile)) { if (TEXT('@') == szFont[0]) // Is the string indirect for MUI? { TCHAR szTemp[MAX_PATH]; if (SUCCEEDED(SHLoadIndirectString(szFont, szTemp, ARRAYSIZE(szTemp), NULL))) { StringCchCopy(szFont, ARRAYSIZE(szFont), szTemp); } } if (TEXT('{') == szFont[0]) { LPTSTR pszStart = &szFont[1]; BOOL fHasMore = TRUE; LPTSTR pszEnd = StrChr(pszStart, TEXT(',')); if (!pszEnd) { pszEnd = StrChr(pszStart, TEXT('}')); fHasMore = FALSE; } if (pszEnd) { pszEnd[0] = 0; // Terminate Name. StringCchCopy(pLogFont->lfFaceName, ARRAYSIZE(pLogFont->lfFaceName), pszStart); if (fHasMore) { pszStart = &pszEnd[1]; pszEnd = StrStr(pszStart, TEXT("pt")); if (pszEnd) { TCHAR szTemp[MAX_PATH]; pszEnd[0] = 0; // Terminate Name. pszEnd += 2; // Skip past the "pt" StringCchCopy(szTemp, ARRAYSIZE(szTemp), pszStart); PathRemoveBlanks(szTemp); pLogFont->lfHeight = -MulDiv(StrToInt(szTemp), DPI_PERSISTED, 72); // Map pt size to lfHeight pLogFont->lfHeight = min(-3, pLogFont->lfHeight); // Make sure the font doesn't get too small pLogFont->lfHeight = max(-100, pLogFont->lfHeight); // Make sure the font doesn't get too large if (TEXT(',') == pszEnd[0]) { pszStart = &pszEnd[1]; pszEnd = StrChr(pszStart, TEXT('}')); if (pszEnd) { pszEnd[0] = 0; // Terminate Name. pLogFont->lfCharSet = (BYTE) StrToInt(pszStart); } } } } } } } return hr; } HRESULT CThemeFile::_LoadCustomFonts(void) { _GetCustomFont(TEXT("CaptionFont"), &(m_systemMetrics.schemeData.ncm.lfCaptionFont)); _GetCustomFont(TEXT("SmCaptionFont"), &(m_systemMetrics.schemeData.ncm.lfSmCaptionFont)); _GetCustomFont(TEXT("MenuFont"), &(m_systemMetrics.schemeData.ncm.lfMenuFont)); _GetCustomFont(TEXT("StatusFont"), &(m_systemMetrics.schemeData.ncm.lfStatusFont)); _GetCustomFont(TEXT("MessageFont"), &(m_systemMetrics.schemeData.ncm.lfMessageFont)); _GetCustomFont(TEXT("IconFont"), &(m_systemMetrics.schemeData.lfIconTitle)); return S_OK; } // Load the settings in memory HRESULT CThemeFile::_LoadLiveSettings(int * pnDPI) { HRESULT hr = S_OK; if (m_pszThemeFile) { if (pnDPI) { *pnDPI = DPI_PERSISTED; } // Get property bag with default settings. if (_punkSite) { IPropertyBag * pPropertyBag; hr = _punkSite->QueryInterface(IID_PPV_ARG(IPropertyBag, &pPropertyBag)); if (SUCCEEDED(hr)) { hr = SHPropertyBag_ReadByRef(pPropertyBag, SZ_PBPROP_SYSTEM_METRICS, (void *)&m_systemMetrics, sizeof(m_systemMetrics)); if (pnDPI && FAILED(SHPropertyBag_ReadInt(pPropertyBag, SZ_PBPROP_DPI_MODIFIED_VALUE, pnDPI))) { *pnDPI = DPI_PERSISTED; // Default to the default DPI. } pPropertyBag->Release(); } } } return hr; } // Load the settings in the .theme file. HRESULT CThemeFile::_LoadSettings(void) { int nCurrentDPI = DPI_PERSISTED; HRESULT hr = _LoadLiveSettings(&nCurrentDPI); if (m_pszThemeFile) { BOOL fFontsFilter = _IsFiltered(THEMEFILTER_SMSTYLES); TCHAR szIconMetrics[2048]; if (m_systemMetrics.nIcon && m_systemMetrics.nSmallIcon) { //////////////////////////////////////////// // Get the icon Metrics // if we somehow come up with no icon metrics in the theme, just // PUNT and leave cur settings if (GetPrivateProfileString(SZ_INISECTION_METRICS, SZ_INIKEY_ICONMETRICS, SZ_EMPTY, szIconMetrics, ARRAYSIZE(szIconMetrics), m_pszThemeFile)) { // if something there to set ICONMETRICSA iconMetricsA; // translate stored data string to ICONMETRICS bytes if ((sizeof(iconMetricsA) == WriteBytesToBuffer(szIconMetrics, (void *)&iconMetricsA, sizeof(iconMetricsA))) && // char str read from and binary bytes (sizeof(iconMetricsA) == iconMetricsA.cbSize)) { // ICONMETRICS are stored in ANSI format in the Theme file so if // we're living in a UNICODE world we need to convert from ANSI // to UNICODE ICONMETRICSW iconMetricsW; if (!fFontsFilter) { ConvertIconMetricsToWIDE(&iconMetricsA, &iconMetricsW); m_systemMetrics.schemeData.lfIconTitle = iconMetricsW.lfFont; } } } //////////////////////////////////////////// // Get Non-Client Metrics // if we somehow come up with no icon metrics in the theme, just // PUNT and leave cur settings if (GetPrivateProfileString(SZ_INISECTION_METRICS, SZ_INIKEY_NONCLIENTMETRICS, SZ_EMPTY, szIconMetrics, ARRAYSIZE(szIconMetrics), m_pszThemeFile)) { BOOL fBordersFilter = _IsFiltered(THEMEFILTER_SMSIZES); NONCLIENTMETRICSA nonClientMetrics; // if something there to set // translate stored data string to ICONMETRICS bytes if ((sizeof(nonClientMetrics) == WriteBytesToBuffer(szIconMetrics, (void *)&nonClientMetrics, sizeof(nonClientMetrics))) && // char str read from and binary bytes (sizeof(nonClientMetrics) == nonClientMetrics.cbSize)) { // ICONMETRICS are stored in ANSI format in the Theme file so if // we're living in a UNICODE world we need to convert from ANSI // to UNICODE NONCLIENTMETRICSW nonClientMetricsW = {0}; ConvertNCMetricsToWIDE(&nonClientMetrics, &nonClientMetricsW); nonClientMetricsW.cbSize = sizeof(nonClientMetricsW); // paranoid // what we reset if the user checks Font names and styles if (!fFontsFilter) { // only (some) font information TransmitFontCharacteristics(&(m_systemMetrics.schemeData.ncm.lfCaptionFont), &(nonClientMetricsW.lfCaptionFont), TFC_STYLE); TransmitFontCharacteristics(&(m_systemMetrics.schemeData.ncm.lfSmCaptionFont), &(nonClientMetricsW.lfSmCaptionFont), TFC_STYLE); TransmitFontCharacteristics(&(m_systemMetrics.schemeData.ncm.lfMenuFont), &(nonClientMetricsW.lfMenuFont), TFC_STYLE); TransmitFontCharacteristics(&(m_systemMetrics.schemeData.ncm.lfStatusFont), &(nonClientMetricsW.lfStatusFont), TFC_STYLE); TransmitFontCharacteristics(&(m_systemMetrics.schemeData.ncm.lfMessageFont), &(nonClientMetricsW.lfMessageFont), TFC_STYLE); } // what we reset if the user checks Font and window si&zes if (!fBordersFilter) { // fonts TransmitFontCharacteristics(&(m_systemMetrics.schemeData.ncm.lfCaptionFont), &(nonClientMetricsW.lfCaptionFont), TFC_SIZE); TransmitFontCharacteristics(&(m_systemMetrics.schemeData.ncm.lfSmCaptionFont), &(nonClientMetricsW.lfSmCaptionFont), TFC_SIZE); TransmitFontCharacteristics(&(m_systemMetrics.schemeData.ncm.lfMenuFont), &(nonClientMetricsW.lfMenuFont), TFC_SIZE); TransmitFontCharacteristics(&(m_systemMetrics.schemeData.ncm.lfStatusFont), &(nonClientMetricsW.lfStatusFont), TFC_SIZE); TransmitFontCharacteristics(&(m_systemMetrics.schemeData.ncm.lfMessageFont), &(nonClientMetricsW.lfMessageFont), TFC_SIZE); // Since we are copying the font sizes, scale them to the current DPI. // window elements sizes m_systemMetrics.schemeData.ncm.iBorderWidth = nonClientMetricsW.iBorderWidth; m_systemMetrics.schemeData.ncm.iScrollWidth = nonClientMetricsW.iScrollWidth; m_systemMetrics.schemeData.ncm.iScrollHeight = nonClientMetricsW.iScrollHeight; m_systemMetrics.schemeData.ncm.iCaptionWidth = nonClientMetricsW.iCaptionWidth; m_systemMetrics.schemeData.ncm.iCaptionHeight = nonClientMetricsW.iCaptionHeight; m_systemMetrics.schemeData.ncm.iSmCaptionWidth = nonClientMetricsW.iSmCaptionWidth; m_systemMetrics.schemeData.ncm.iSmCaptionHeight = nonClientMetricsW.iSmCaptionHeight; m_systemMetrics.schemeData.ncm.iMenuWidth = nonClientMetricsW.iMenuWidth; m_systemMetrics.schemeData.ncm.iMenuHeight = nonClientMetricsW.iMenuHeight; // Local custom fonts _LoadCustomFonts(); if (nCurrentDPI != DPI_PERSISTED) { LogSystemMetrics("CThemeFile::_LoadSettings() BEFORE Loading from .theme", &m_systemMetrics); DPIConvert_SystemMetricsAll(TRUE, &m_systemMetrics, DPI_PERSISTED, nCurrentDPI); LogSystemMetrics("CThemeFile::_LoadSettings() AFTER Loading from .theme", &m_systemMetrics); } // CHARSET: In Win2k, fontfix.cpp was used as a hack to change the CHARSET from one language to another. // That doesn't work for many reasons: a) not called on roaming, b) not called for OS lang changes, // c) won't fix the problem for strings with multiple languages, d) etc. // Therefore, the SHELL team (BryanSt) had the NTUSER team (MSadek) agree to use DEFAULT_CHARSET all the time. // If some app has bad logic testing the charset parameter, then the NTUSER team will shim that app to fix it. // The shim would be really simple, on the return from a SystemParametersInfo(SPI_GETNONCLIENTMETRICS or ICONFONTS) // just patch the lfCharSet param to the current charset. // For all CHARSETs to DEFAULT_CHARSET m_systemMetrics.schemeData.ncm.lfCaptionFont.lfCharSet = DEFAULT_CHARSET; m_systemMetrics.schemeData.ncm.lfSmCaptionFont.lfCharSet = DEFAULT_CHARSET; m_systemMetrics.schemeData.ncm.lfMenuFont.lfCharSet = DEFAULT_CHARSET; m_systemMetrics.schemeData.ncm.lfStatusFont.lfCharSet = DEFAULT_CHARSET; m_systemMetrics.schemeData.ncm.lfMessageFont.lfCharSet = DEFAULT_CHARSET; m_systemMetrics.schemeData.lfIconTitle.lfCharSet = DEFAULT_CHARSET; } } } //////////////////////////////////////////// // Get Colors BOOL fGrad = FALSE; // Are gradient captions enabled? int nIndex; BOOL fColorFilter = _IsFiltered(THEMEFILTER_COLORS); ClassicSystemParametersInfo(SPI_GETGRADIENTCAPTIONS, 0, (LPVOID)&fGrad, 0); // Init fGrad if (!fColorFilter) { for (nIndex = 0; nIndex < ARRAYSIZE(s_pszColorNames); nIndex++) { TCHAR szColor[MAX_PATH]; // get string from theme if (!GetPrivateProfileString(SZ_INISECTION_COLORS, s_pszColorNames[nIndex], SZ_EMPTY, szColor, ARRAYSIZE(szColor), m_pszThemeFile)) { if ((nIndex == COLOR_GRADIENTACTIVECAPTION) && !szColor[0]) { // They didn't specify the COLOR_GRADIENTACTIVECAPTION color, so use COLOR_ACTIVECAPTION GetPrivateProfileString(SZ_INISECTION_COLORS, s_pszColorNames[COLOR_ACTIVECAPTION], SZ_EMPTY, szColor, ARRAYSIZE(szColor), m_pszThemeFile); } if ((nIndex == COLOR_GRADIENTINACTIVECAPTION) && !szColor[0]) { // They didn't specify the COLOR_GRADIENTINACTIVECAPTION color, so use COLOR_INACTIVECAPTION GetPrivateProfileString(SZ_INISECTION_COLORS, s_pszColorNames[COLOR_INACTIVECAPTION], SZ_EMPTY, szColor, ARRAYSIZE(szColor), m_pszThemeFile); } } if (szColor[0]) { m_systemMetrics.schemeData.rgb[nIndex] = RGBStringToColor(szColor); } } } } else { AssertMsg((NULL != _punkSite), TEXT("The caller needs to set our site or we can't succeed because we can't find out the icon size.")); hr = E_INVALIDARG; } hr = S_OK; } return hr; } HRESULT CThemeFile::_SaveSystemMetrics(SYSTEMMETRICSALL * pSystemMetrics) { HRESULT hr = _LoadSettings(); AssertMsg((NULL != m_pszThemeFile), TEXT("We don't have a file specified yet.")); if (SUCCEEDED(hr) && m_pszThemeFile) { int nCurrentDPI = DPI_PERSISTED; _LoadLiveSettings(&nCurrentDPI); hr = SystemMetricsAll_Copy(pSystemMetrics, &m_systemMetrics); if (SUCCEEDED(hr)) { // Write the following: LPWSTR pszStringOut; NONCLIENTMETRICSA nonClientMetricsA = {0}; SYSTEMMETRICSALL systemMetricsPDPI; // SYSMETS in persist DPI SystemMetricsAll_Copy(pSystemMetrics, &systemMetricsPDPI); // Scale the values so they are persisted in a DPI independent way. (A.k.a., in 96 DPI) LogSystemMetrics("CThemeFile::_SaveSystemMetrics() BEFORE scale to P-DPI for .theme file", &systemMetricsPDPI); DPIConvert_SystemMetricsAll(TRUE, &systemMetricsPDPI, nCurrentDPI, DPI_PERSISTED); LogSystemMetrics("CThemeFile::_SaveSystemMetrics() AFTER scale to P-DPI for .theme file", &systemMetricsPDPI); ConvertNCMetricsToANSI(&(systemMetricsPDPI.schemeData.ncm), &nonClientMetricsA); // #1 "NonclientMetrics" hr = ConvertBinaryToINIByteString((BYTE *)&nonClientMetricsA, sizeof(nonClientMetricsA), &pszStringOut); if (SUCCEEDED(hr)) { hr = _putThemeSetting(SZ_INISECTION_METRICS, SZ_INIKEY_NONCLIENTMETRICS, FALSE, pszStringOut); LocalFree(pszStringOut); if (SUCCEEDED(hr)) { // #2 "IconMetrics" ICONMETRICSA iconMetricsA; iconMetricsA.cbSize = sizeof(iconMetricsA); GetIconMetricsFromSysMetricsAll(&systemMetricsPDPI, &iconMetricsA, sizeof(iconMetricsA)); hr = ConvertBinaryToINIByteString((BYTE *)&iconMetricsA, sizeof(iconMetricsA), &pszStringOut); if (SUCCEEDED(hr)) { hr = _putThemeSetting(SZ_INISECTION_METRICS, SZ_INIKEY_ICONMETRICS, FALSE, pszStringOut); if (SUCCEEDED(hr)) { int nIndex; for (nIndex = 0; nIndex < ARRAYSIZE(s_pszColorNames); nIndex++) { LPWSTR pszColor; DWORD dwColor = systemMetricsPDPI.schemeData.rgb[nIndex]; hr = ConvertBinaryToINIByteString((BYTE *)&dwColor, 3, &pszColor); if (SUCCEEDED(hr)) { DWORD cchSize = lstrlen(pszColor); if (L' ' == pszColor[cchSize - 1]) { pszColor[cchSize - 1] = 0; } hr = HrWritePrivateProfileStringW(SZ_INISECTION_COLORS, s_pszColorNames[nIndex], pszColor, m_pszThemeFile); LocalFree(pszColor); } } // Delete the MUI version of the fonts because we just got new NONCLIENTMETRICs _putThemeSetting(SZ_INISECTION_METRICS, TEXT("CaptionFont"), FALSE, NULL); _putThemeSetting(SZ_INISECTION_METRICS, TEXT("SmCaptionFont"), FALSE, NULL); _putThemeSetting(SZ_INISECTION_METRICS, TEXT("MenuFont"), FALSE, NULL); _putThemeSetting(SZ_INISECTION_METRICS, TEXT("StatusFont"), FALSE, NULL); _putThemeSetting(SZ_INISECTION_METRICS, TEXT("MessageFont"), FALSE, NULL); _putThemeSetting(SZ_INISECTION_METRICS, TEXT("IconFont"), FALSE, NULL); } LocalFree(pszStringOut); } } } } } return hr; } BOOL CThemeFile::_IsFiltered(IN DWORD dwFilter) { BOOL fFiltered = FALSE; // Get property bag with default settings. if (_punkSite) { IPropertyBag * pPropertyBag; HRESULT hr = _punkSite->QueryInterface(IID_PPV_ARG(IPropertyBag, &pPropertyBag)); if (SUCCEEDED(hr)) { fFiltered = !SHPropertyBag_ReadBOOLDefRet(pPropertyBag, g_szCBNames[dwFilter], FALSE); pPropertyBag->Release(); } } return fFiltered; } HRESULT CThemeFile::_ApplySounds(void) { HRESULT hr = S_OK; if (!_IsFiltered(THEMEFILTER_SOUNDS)) { int nIndex; for (nIndex = 0; nIndex < ARRAYSIZE(s_ThemeSoundsValues); nIndex++) { CComBSTR bstrPath; hr = _GetSound(s_ThemeSoundsValues[nIndex].pszRegKey, &bstrPath); if (SUCCEEDED(hr)) { DWORD dwError = SHRegSetPathW(HKEY_CURRENT_USER, s_ThemeSoundsValues[nIndex].pszRegKey, NULL, bstrPath, 0); hr = HRESULT_FROM_WIN32(dwError); } else { // First delete the value because we many need to switch from REG_SZ to REG_EXPAND_SZ // Ignore if this fails HrRegDeleteValue(HKEY_CURRENT_USER, s_ThemeSoundsValues[nIndex].pszRegKey, NULL); hr = E_FAIL; // The file didn't specify what to use, so reset to the default values. if (s_ThemeSoundsValues[nIndex].nResourceID) { // Use the specified value. TCHAR szReplacement[MAX_PATH]; DWORD dwType; DWORD cbSize; if (s_ThemeSoundsValues[nIndex].nResourceID == SOUND_DEFAULT) { TCHAR szDefaultKey[MAX_PATH]; StringCchCopy(szDefaultKey, ARRAYSIZE(szDefaultKey), s_ThemeSoundsValues[nIndex].pszRegKey); DWORD cchDefaultKey = lstrlen(szDefaultKey); DWORD cchCurrent = ARRAYSIZE(L".Current"); DWORD cchp = ARRAYSIZE(szDefaultKey) - cchDefaultKey - cchCurrent + 1; LPTSTR p = szDefaultKey + cchDefaultKey - cchCurrent + 1; // Replace ".Current" with ".default" if (*p == L'.') { StringCchCopy(p, cchp, L".Default"); cbSize = sizeof szReplacement; hr = HrSHGetValue(HKEY_CURRENT_USER, szDefaultKey, NULL, &dwType, (LPVOID) szReplacement, &cbSize); if (SUCCEEDED(hr)) { PathUnExpandEnvStringsWrap(szReplacement, ARRAYSIZE(szReplacement)); } } } else { if (0 != LoadString(HINST_THISDLL, s_ThemeSoundsValues[nIndex].nResourceID, szReplacement, ARRAYSIZE(szReplacement))) { hr = S_OK; } } if (SUCCEEDED(hr)) { dwType = (StrStrW(szReplacement, L"%SystemRoot%")) ? REG_EXPAND_SZ : REG_SZ; cbSize = ((lstrlen(szReplacement) + 1) * sizeof(szReplacement[0])); hr = HrSHSetValue(HKEY_CURRENT_USER, s_ThemeSoundsValues[nIndex].pszRegKey, NULL, dwType, (LPVOID) szReplacement, cbSize); } } else { // We leave the value deleted because the default was empty. } } } hr = S_OK; // We don't care if it fails. // Need to flush buffer and ensure new sounds used for next events sndPlaySoundW(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, SZ_REGKEY_SOUNDS, REG_SZ, TEXT(".current"), 0); } return hr; } HRESULT CThemeFile::_ApplyCursors(void) { HRESULT hr = S_OK; if (!_IsFiltered(THEMEFILTER_CURSORS)) { int nIndex; for (nIndex = 0; nIndex < ARRAYSIZE(s_pszCursorArray); nIndex++) { BSTR bstrPath; hr = _getThemeSetting(SZ_INISECTION_CURSORS, s_pszCursorArray[nIndex], THEMESETTING_LOADINDIRECT, &bstrPath); if (FAILED(hr) || !bstrPath[0]) { // The caller didn't specify a value so delete the key so we use default values. hr = HrRegDeleteValue(HKEY_CURRENT_USER, SZ_INISECTION_CURSORS, s_pszCursorArray[nIndex]); if (HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) == hr) { hr = S_OK; // it may already not exist, which is fine. } } else if (SUCCEEDED(hr)) { hr = HrRegSetValueString(HKEY_CURRENT_USER, SZ_INISECTION_CURSORS, s_pszCursorArray[nIndex], bstrPath); } } BSTR bstrCursor; if (SUCCEEDED(_getThemeSetting(SZ_INISECTION_CURSORS, SZ_INIKEY_CURSORSCHEME, THEMESETTING_LOADINDIRECT, &bstrCursor)) && bstrCursor && bstrCursor[0]) { // Set the cursor scheme HrRegSetValueString(HKEY_CURRENT_USER, SZ_REGKEY_CP_CURSORS, NULL, bstrCursor); // GPease wants me to mark this regkey -1 so he knows it was changed from the display CPL. See // him with questions. HrRegSetDWORD(HKEY_CURRENT_USER, SZ_REGKEY_CP_CURSORS, SZ_REGVALUE_CURSOR_CURRENTSCHEME, 2); } else { HrRegDeleteValue(HKEY_CURRENT_USER, SZ_REGKEY_CP_CURSORS, NULL); HrRegDeleteValue(HKEY_CURRENT_USER, SZ_REGKEY_CP_CURSORS, SZ_REGVALUE_CURSOR_CURRENTSCHEME); } // For the system to start using the new cursors. SystemParametersInfoAsync(SPI_SETCURSORS, 0, 0, 0, SPIF_SENDCHANGE, NULL); } return hr; } HRESULT CThemeFile::_ApplyWebview(void) { HRESULT hr = S_OK; // We aren't going to support this. return hr; } HRESULT CThemeFile::_ApplyThemeSettings(void) { HRESULT hr = E_INVALIDARG; if (m_pszThemeFile) { HCURSOR hCursorOld = ::SetCursor(LoadCursor(NULL, IDC_WAIT)); hr = S_OK; if (!((METRIC_CHANGE | COLOR_CHANGE | SCHEME_CHANGE) & m_systemMetrics.dwChanged)) { // Only load settings if we haven't loaded the settings yet. hr = _LoadSettings(); } if (SUCCEEDED(hr)) { hr = _ApplySounds(); if (SUCCEEDED(hr)) { hr = _ApplyCursors(); if (SUCCEEDED(hr)) { hr = _ApplyWebview(); } } } // OTHERS: // 1. Save Icon: SPI_SETICONMETRICS w/iconMetricsW.iHorzSpacing, iVertSpacing, (Policy bIconSpacing). // 2. Save Icon: SPI_SETICONMETRICS w/iconMetricsW.lfFont (Policy bIconFont). // 2. Save Icon: from Theme:"Control Panel\\Desktop\\WindowMetrics","Shell Icon Size" to reg same. (Policy bIconSpacing). Repeate for "Shell Small Icon Size" ::SetCursor(hCursorOld); } return hr; } HRESULT CThemeFile::_getThemeSetting(IN LPCWSTR pszIniSection, IN LPCWSTR pszIniKey, DWORD dwFlags, OUT BSTR * pbstrPath) { HRESULT hr = E_INVALIDARG; if (pbstrPath) { *pbstrPath = 0; hr = HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND); if (m_pszThemeFile) { WCHAR szPath[MAX_PATH]; DWORD cbRead = 0; szPath[0] = 0; if (THEMESETTING_LOADINDIRECT & dwFlags) { TCHAR szMUIIniKey[MAX_PATH]; StringCchPrintf(szMUIIniKey, ARRAYSIZE(szMUIIniKey), TEXT("%s.MUI"), pszIniKey); cbRead = SHGetIniStringW(pszIniSection, szMUIIniKey, szPath, ARRAYSIZE(szPath), m_pszThemeFile); } if (0 == cbRead) { cbRead = SHGetIniStringW(pszIniSection, pszIniKey, szPath, ARRAYSIZE(szPath), m_pszThemeFile); } if (cbRead) { if (L'@' == szPath[0]) { TCHAR szTemp[MAX_PATH]; if (SUCCEEDED(SHLoadIndirectString(szPath, szTemp, ARRAYSIZE(szTemp), NULL))) { StringCchCopy(szPath, ARRAYSIZE(szPath), szTemp); } } hr = ExpandResourceDir(szPath, ARRAYSIZE(szPath)); hr = ExpandThemeTokens(m_pszThemeFile, szPath, ARRAYSIZE(szPath)); // Expand %ThemeDir% or %WinDir% // Sometimes szPath won't be a path. if (SUCCEEDED(hr) && !PathIsFileSpec(szPath)) { hr = ((CF_NOTFOUND == ConfirmFile(szPath, TRUE)) ? HRESULT_FROM_WIN32(ERROR_FILE_NOT_FOUND) : S_OK); } if (SUCCEEDED(hr)) { hr = HrSysAllocString(szPath, pbstrPath); } } } } return hr; } // pszPath - NULL means delete value HRESULT CThemeFile::_putThemeSetting(IN LPCWSTR pszIniSection, IN LPCWSTR pszIniKey, BOOL fUTF7, IN OPTIONAL LPWSTR pszPath) { HRESULT hr = E_INVALIDARG; if (m_pszThemeFile) { TCHAR szPath[MAX_PATH]; LPCWSTR pszValue = pszPath; szPath[0] = 0; if (pszValue && !PathIsRelative(pszValue) && PathFileExists(pszValue)) { if (PathUnExpandEnvStringsForUser(NULL, pszValue, szPath, ARRAYSIZE(szPath))) { pszValue = szPath; } } StrReplaceToken(TEXT("%WinDir%\\"), TEXT("%WinDir%"), szPath, ARRAYSIZE(szPath)); StrReplaceToken(TEXT("%SystemRoot%\\"), TEXT("%WinDir%"), szPath, ARRAYSIZE(szPath)); if (fUTF7) { if (SHSetIniStringW(pszIniSection, pszIniKey, pszValue, m_pszThemeFile)) { hr = S_OK; } else { hr = HRESULT_FROM_WIN32(GetLastError()); } } else { hr = HrWritePrivateProfileStringW(pszIniSection, pszIniKey, pszValue, m_pszThemeFile); } TCHAR szMUIIniKey[MAX_PATH]; // Delete any ".MUI" copies because they are out of date. StringCchPrintf(szMUIIniKey, ARRAYSIZE(szMUIIniKey), TEXT("%s.MUI"), pszIniKey); HrWritePrivateProfileStringW(pszIniSection, szMUIIniKey, NULL, m_pszThemeFile); } return hr; } HRESULT CThemeFile::_getIntSetting(IN LPCWSTR pszIniSection, IN LPCWSTR pszIniKey, int nDefault, OUT int * pnValue) { HRESULT hr = E_INVALIDARG; if (pnValue) { *pnValue = 0; hr = E_FAIL; if (m_pszThemeFile) { *pnValue = GetPrivateProfileInt(pszIniSection, pszIniKey, nDefault, m_pszThemeFile); hr = S_OK; } } return hr; } //=========================== // *** ITheme Interface *** //=========================== HRESULT CThemeFile::get_DisplayName(OUT BSTR * pbstrDisplayName) { HRESULT hr = E_INVALIDARG; if (pbstrDisplayName) { WCHAR szDisplayName[MAX_PATH]; *pbstrDisplayName = NULL; hr = _getThemeSetting(SZ_INISECTION_THEME, SZ_INIKEY_DISPLAYNAME, THEMESETTING_NORMAL, pbstrDisplayName); if (FAILED(hr)) { LPCTSTR pszFileName = PathFindFileName(m_pszThemeFile); hr = E_FAIL; if (pszFileName) { SHTCharToUnicode(pszFileName, szDisplayName, ARRAYSIZE(szDisplayName)); PathRemoveExtensionW(szDisplayName); hr = HrSysAllocStringW(szDisplayName, pbstrDisplayName); } } } return hr; } HRESULT CThemeFile::put_DisplayName(IN BSTR bstrDisplayName) { HRESULT hr = E_INVALIDARG; // NULL bstrDisplayName is allowed, it means to delete the name in the file // so the filename will be used in the future. if (bstrDisplayName) { hr = _putThemeSetting(SZ_INISECTION_THEME, SZ_INIKEY_DISPLAYNAME, TRUE, bstrDisplayName); } else { SHSetIniStringW(SZ_INISECTION_THEME, SZ_INIKEY_DISPLAYNAME, NULL, m_pszThemeFile); hr = S_OK; } return hr; } HRESULT CThemeFile::get_ScreenSaver(OUT BSTR * pbstrPath) { return _getThemeSetting(SZ_INISECTION_SCREENSAVER, SZ_INIKEY_SCREENSAVER, THEMESETTING_NORMAL, pbstrPath); } HRESULT CThemeFile::put_ScreenSaver(IN BSTR bstrPath) { return _putThemeSetting(SZ_INISECTION_SCREENSAVER, SZ_INIKEY_SCREENSAVER, TRUE, bstrPath); } HRESULT CThemeFile::get_Background(OUT BSTR * pbstrPath) { HRESULT hr = E_INVALIDARG; if (pbstrPath) { hr = _getThemeSetting(SZ_INISECTION_BACKGROUND, SZ_INIKEY_BACKGROUND, THEMESETTING_LOADINDIRECT, pbstrPath); if (SUCCEEDED(hr)) { TCHAR szNone[MAX_PATH]; LoadString(HINST_THISDLL, IDS_NONE, szNone, ARRAYSIZE(szNone)); if (!StrCmpI(szNone, *pbstrPath)) { (*pbstrPath)[0] = 0; } } } return hr; } HRESULT CThemeFile::put_Background(IN BSTR bstrPath) { return _putThemeSetting(SZ_INISECTION_BACKGROUND, SZ_INIKEY_BACKGROUND, TRUE, bstrPath); } HRESULT CThemeFile::get_BackgroundTile(OUT enumBkgdTile * pnTile) { HRESULT hr = E_INVALIDARG; if (pnTile) { TCHAR szSize[10]; int tile = 0; // Zero is the default value to use if the registry is empty. int stretch = 0; if (SUCCEEDED(HrRegGetValueString(HKEY_CURRENT_USER, SZ_INISECTION_BACKGROUND, SZ_REGVALUE_TILEWALLPAPER, szSize, ARRAYSIZE(szSize)))) { tile = StrToInt(szSize); } if (SUCCEEDED(HrRegGetValueString(HKEY_CURRENT_USER, SZ_INISECTION_BACKGROUND, SZ_REGVALUE_WALLPAPERSTYLE, szSize, ARRAYSIZE(szSize)))) { tile = (2 & StrToInt(szSize)); } // If a theme is selected, and we are using a plus wall paper then // find out if tiling is on, and what style to use from the ini file. // Otherwise, we already got the information from the registry. _getIntSetting(SZ_INISECTION_BACKGROUND, SZ_REGVALUE_TILEWALLPAPER, tile, &tile); _getIntSetting(SZ_INISECTION_BACKGROUND, SZ_REGVALUE_WALLPAPERSTYLE, stretch, &stretch); stretch &= 2; _getIntSetting(SZ_INISECTION_MASTERSELECTOR, SZ_REGVALUE_STRETCH, stretch, &stretch); if (tile) { *pnTile = BKDGT_TILE; } else if (stretch) { *pnTile = BKDGT_STRECH; } else { *pnTile = BKDGT_CENTER; } hr = S_OK; } return hr; } HRESULT CThemeFile::put_BackgroundTile(IN enumBkgdTile nTile) { HRESULT hr = E_INVALIDARG; switch (nTile) { case BKDGT_STRECH: hr = _putThemeSetting(SZ_INISECTION_BACKGROUND, SZ_REGVALUE_TILEWALLPAPER, FALSE, TEXT("0")); hr = _putThemeSetting(SZ_INISECTION_BACKGROUND, SZ_REGVALUE_WALLPAPERSTYLE, FALSE, TEXT("2")); break; case BKDGT_CENTER: hr = _putThemeSetting(SZ_INISECTION_BACKGROUND, SZ_REGVALUE_TILEWALLPAPER, FALSE, TEXT("0")); hr = _putThemeSetting(SZ_INISECTION_BACKGROUND, SZ_REGVALUE_WALLPAPERSTYLE, FALSE, TEXT("0")); break; case BKDGT_TILE: hr = _putThemeSetting(SZ_INISECTION_BACKGROUND, SZ_REGVALUE_TILEWALLPAPER, FALSE, TEXT("1")); hr = _putThemeSetting(SZ_INISECTION_BACKGROUND, SZ_REGVALUE_WALLPAPERSTYLE, FALSE, TEXT("0")); break; }; return hr; } HRESULT CThemeFile::get_VisualStyle(OUT BSTR * pbstrPath) { return _getThemeSetting(SZ_INISECTION_VISUALSTYLES, SZ_INIKEY_VISUALSTYLE, THEMESETTING_NORMAL, pbstrPath); } HRESULT CThemeFile::put_VisualStyle(IN BSTR bstrPath) { return _putThemeSetting(SZ_INISECTION_VISUALSTYLES, SZ_INIKEY_VISUALSTYLE, TRUE, bstrPath); } HRESULT CThemeFile::get_VisualStyleColor(OUT BSTR * pbstrPath) { return _getThemeSetting(SZ_INISECTION_VISUALSTYLES, SZ_INIKEY_VISUALSTYLECOLOR, THEMESETTING_NORMAL, pbstrPath); } HRESULT CThemeFile::put_VisualStyleColor(IN BSTR bstrPath) { return _putThemeSetting(SZ_INISECTION_VISUALSTYLES, SZ_INIKEY_VISUALSTYLECOLOR, TRUE, bstrPath); } HRESULT CThemeFile::get_VisualStyleSize(OUT BSTR * pbstrPath) { return _getThemeSetting(SZ_INISECTION_VISUALSTYLES, SZ_INIKEY_VISUALSTYLESIZE, THEMESETTING_NORMAL, pbstrPath); } HRESULT CThemeFile::put_VisualStyleSize(IN BSTR bstrPath) { return _putThemeSetting(SZ_INISECTION_VISUALSTYLES, SZ_INIKEY_VISUALSTYLESIZE, TRUE, bstrPath); } HRESULT CThemeFile::GetPath(IN VARIANT_BOOL fExpand, OUT BSTR * pbstrPath) { HRESULT hr = E_INVALIDARG; if (pbstrPath && m_pszThemeFile) { TCHAR szPath[MAX_PATH]; StringCchCopy(szPath, ARRAYSIZE(szPath), m_pszThemeFile); if (VARIANT_TRUE == fExpand) { TCHAR szPathTemp[MAX_PATH]; if (SHExpandEnvironmentStrings(szPath, szPathTemp, ARRAYSIZE(szPathTemp))) { StringCchCopy(szPath, ARRAYSIZE(szPath), szPathTemp); } } hr = HrSysAllocString(szPath, pbstrPath); } return hr; } HRESULT CThemeFile::SetPath(IN BSTR bstrPath) { HRESULT hr = E_INVALIDARG; if (bstrPath) { Str_SetPtr(&m_pszThemeFile, bstrPath); hr = S_OK; } return hr; } HRESULT CThemeFile::GetCursor(IN BSTR bstrCursor, OUT BSTR * pbstrPath) { HRESULT hr = E_INVALIDARG; if (pbstrPath) { *pbstrPath = NULL; if (bstrCursor) { hr = _getThemeSetting(SZ_INISECTION_CURSORS, bstrCursor, THEMESETTING_LOADINDIRECT, pbstrPath); } } return hr; } HRESULT CThemeFile::SetCursor(IN BSTR bstrCursor, IN BSTR bstrPath) { HRESULT hr = E_INVALIDARG; if (bstrCursor) { hr = _putThemeSetting(SZ_INISECTION_CURSORS, bstrCursor, TRUE, bstrPath); } return hr; } HRESULT CThemeFile::_GetSound(LPCWSTR pszSoundName, OUT BSTR * pbstrPath) { HRESULT hr = E_INVALIDARG; if (pszSoundName && pbstrPath) { *pbstrPath = NULL; hr = _getThemeSetting(pszSoundName, SZ_INIKEY_DEFAULTVALUE, THEMESETTING_LOADINDIRECT, pbstrPath); } return hr; } HRESULT CThemeFile::GetSound(IN BSTR bstrSoundName, OUT BSTR * pbstrPath) { HRESULT hr = E_INVALIDARG; if (pbstrPath) { *pbstrPath = NULL; if (bstrSoundName) { hr = _GetSound(bstrSoundName, pbstrPath); if (FAILED(hr)) { int nIndex; for (nIndex = 0; nIndex < ARRAYSIZE(s_ThemeSoundsValues); nIndex++) { if (!StrCmpI(bstrSoundName, s_ThemeSoundsValues[nIndex].pszRegKey)) { // First delete the value because we many need to switch from REG_SZ to REG_EXPAND_SZ TCHAR szReplacement[MAX_PATH]; LoadString(HINST_THISDLL, s_ThemeSoundsValues[nIndex].nResourceID, szReplacement, ARRAYSIZE(szReplacement)); hr = HrSysAllocStringW(szReplacement, pbstrPath); break; } } } } } return hr; } HRESULT CThemeFile::SetSound(IN BSTR bstrSoundName, IN BSTR bstrPath) { HRESULT hr = E_INVALIDARG; if (bstrSoundName && bstrPath) { hr = _putThemeSetting(bstrSoundName, SZ_INIKEY_DEFAULTVALUE, TRUE, bstrPath); } return hr; } HRESULT CThemeFile::GetIcon(IN BSTR bstrIconName, OUT BSTR * pbstrIconPath) { HRESULT hr = E_INVALIDARG; if (pbstrIconPath) { *pbstrIconPath = NULL; if (bstrIconName) { WCHAR szPath[MAX_URL_STRING]; WCHAR szIconType[MAX_PATH]; StringCchCopy(szPath, ARRAYSIZE(szPath), bstrIconName); LPWSTR pszSeparator = StrChrW(szPath, L':'); if (pszSeparator) { StringCchCopy(szIconType, ARRAYSIZE(szIconType), CharNext(pszSeparator)); pszSeparator[0] = 0; } else { // The caller should specify this but this is a safe fallback. StringCchCopy(szIconType, ARRAYSIZE(szIconType), L"DefaultValue"); } hr = _getThemeSetting(szPath, szIconType, THEMESETTING_NORMAL, pbstrIconPath); if (FAILED(hr)) { // The Plus! 98 format started adding "Software\Classes" to the path. // So try that now. // Plus!95 format: "[CLSID\{20D04FE0-3AEA-1069-A2D8-08002B30309D}\DefaultIcon]" // Plus!98 format: "[Software\Classes\CLSID\{20D04FE0-3AEA-1069-A2D8-08002B30309D}\DefaultIcon]" WCHAR szPath98[MAX_URL_STRING]; StringCchPrintf(szPath98, ARRAYSIZE(szPath98), L"Software\\Classes\\%ls", szPath); hr = _getThemeSetting(szPath98, szIconType, THEMESETTING_NORMAL, pbstrIconPath); } } } return hr; } HRESULT CThemeFile::SetIcon(IN BSTR bstrIconName, IN BSTR bstrIconPath) { HRESULT hr = E_INVALIDARG; if (bstrIconName && bstrIconPath) { WCHAR szPath[MAX_URL_STRING]; WCHAR szIconType[MAX_PATH]; StringCchCopy(szPath, ARRAYSIZE(szPath), bstrIconName); LPWSTR pszSeparator = StrChrW(szPath, L':'); if (pszSeparator) { StringCchCopy(szIconType, ARRAYSIZE(szIconType), CharNext(pszSeparator)); pszSeparator[0] = 0; } else { // The caller should specify this but this is a safe fallback. StringCchCopy(szIconType, ARRAYSIZE(szIconType), L"DefaultValue"); } hr = _putThemeSetting(szPath, szIconType, TRUE, bstrIconPath); } return hr; } //=========================== // *** IPropertyBag Interface *** //=========================== HRESULT CThemeFile::Read(IN LPCOLESTR pszPropName, IN VARIANT * pVar, IN IErrorLog *pErrorLog) { HRESULT hr = E_INVALIDARG; if (pszPropName && pVar) { if (!StrCmpW(pszPropName, SZ_PBPROP_SYSTEM_METRICS)) { hr = _LoadSettings(); // This is pretty ugly. pVar->vt = VT_BYREF; pVar->byref = &m_systemMetrics; } else if (!StrCmpW(pszPropName, SZ_PBPROP_HASSYSMETRICS)) { hr = _LoadSettings(); pVar->vt = VT_BOOL; pVar->boolVal = VARIANT_FALSE; if (SUCCEEDED(hr)) { TCHAR szIconMetrics[2048]; if (GetPrivateProfileString(SZ_INISECTION_METRICS, SZ_INIKEY_ICONMETRICS, SZ_EMPTY, szIconMetrics, ARRAYSIZE(szIconMetrics), m_pszThemeFile)) { if (GetPrivateProfileString(SZ_INISECTION_METRICS, SZ_INIKEY_NONCLIENTMETRICS, SZ_EMPTY, szIconMetrics, ARRAYSIZE(szIconMetrics), m_pszThemeFile)) { GetPrivateProfileString(SZ_INISECTION_COLORS, s_pszColorNames[COLOR_ACTIVECAPTION], SZ_EMPTY, szIconMetrics, ARRAYSIZE(szIconMetrics), m_pszThemeFile); pVar->boolVal = (szIconMetrics[0] ? VARIANT_TRUE : VARIANT_FALSE); } } } } } return hr; } HRESULT CThemeFile::Write(IN LPCOLESTR pszPropName, IN VARIANT *pVar) { HRESULT hr = E_NOTIMPL; if (pszPropName && pVar) { if (!StrCmpW(pszPropName, SZ_PBPROP_APPLY_THEMEFILE)) { VariantInit(pVar); hr = _ApplyThemeSettings(); // This will do nothing if already loaded. } else if (!StrCmpW(pszPropName, SZ_PBPROP_SYSTEM_METRICS) && (VT_BYREF == pVar->vt) && pVar->byref) { SYSTEMMETRICSALL * pCurrent = (SYSTEMMETRICSALL *) pVar->byref; // The caller will pass SYSTEMMETRICS in the live system DPI. hr = _SaveSystemMetrics(pCurrent); } } return hr; } //=========================== // *** IUnknown Interface *** //=========================== ULONG CThemeFile::AddRef() { return InterlockedIncrement(&m_cRef); } ULONG CThemeFile::Release() { ASSERT( 0 != m_cRef ); ULONG cRef = InterlockedDecrement(&m_cRef); if ( 0 == cRef ) { delete this; } return cRef; } HRESULT CThemeFile::QueryInterface(REFIID riid, void **ppvObj) { static const QITAB qit[] = { QITABENT(CThemeFile, IObjectWithSite), QITABENT(CThemeFile, IPropertyBag), QITABENT(CThemeFile, ITheme), QITABENT(CThemeFile, IDispatch), { 0 }, }; return QISearch(this, qit, riid, ppvObj); } //=========================== // *** Class Methods *** //=========================== CThemeFile::CThemeFile(LPCTSTR pszThemeFile) : m_cRef(1) { DllAddRef(); // This needs to be allocated in Zero Inited Memory. // Assert that all Member Variables are inited to Zero. m_dwCachedState = 0xFFFFFFFF; InitFrost(); } CThemeFile::~CThemeFile() { Str_SetPtr(&m_pszThemeFile, NULL); DllRelease(); } HRESULT CThemeFile_CreateInstance(IN LPCWSTR pszThemeFile, OUT ITheme ** ppTheme) { HRESULT hr = E_INVALIDARG; if (ppTheme) { CThemeFile * pObject = new CThemeFile(pszThemeFile); hr = E_OUTOFMEMORY; *ppTheme = NULL; if (pObject) { hr = pObject->SetPath((BSTR)pszThemeFile); if (SUCCEEDED(hr)) { hr = pObject->QueryInterface(IID_PPV_ARG(ITheme, ppTheme)); } pObject->Release(); } } return hr; }