#include "precomp.h" #include "winuser.h" #include // For CLSID_CDeskHtmlProp #include #include #include #include #include #include // hydra stuff #include #include #include "cplext.h" #include "cplp.h" HWND g_hDlg = NULL; /////////////////////////////////////////////////////////////////////////////// // Array defining each page in the sheet /////////////////////////////////////////////////////////////////////////////// typedef struct { int id; DLGPROC pfnDlgProc; RESTRICTIONS dwPolicy1; RESTRICTIONS dwPolicy2; long nExtensionID; // The page } PAGEINFO; PAGEINFO aPageInfo[] = { { 0, NULL, REST_NODISPLAYAPPEARANCEPAGE, REST_NOTHEMESTAB, PAGE_DISPLAY_THEMES}, // Theme page { DLG_BACKGROUND, BackgroundDlgProc, REST_NODISPBACKGROUND, (RESTRICTIONS)0, 0}, // Background page { DLG_SCREENSAVER, NULL, REST_NODISPSCREENSAVEPG, (RESTRICTIONS)0, 0}, // Screen Saver page { 0, NULL, REST_NODISPLAYAPPEARANCEPAGE, (RESTRICTIONS)0, PAGE_DISPLAY_APPEARANCE}, // Appearance page { 0, NULL, REST_NODISPSETTINGSPG, (RESTRICTIONS)0, PAGE_DISPLAY_SETTINGS}, // Settings page }; #define C_PAGES_DESK ARRAYSIZE(aPageInfo) #define IPI_SETTINGS (C_PAGES_DESK-1) // Index to "Settings" page #define SZ_WALLPAPER L"Wallpaper" #define EnableApplyButton(hdlg) PropSheet_Changed(GetParent(hdlg), hdlg) IThemeUIPages * g_pThemeUI = NULL; // Local Constant Declarations static const TCHAR sc_szCoverClass[] = TEXT("DeskSaysNoPeekingItsASurprise"); LRESULT CALLBACK CoverWindowProc( HWND, UINT, WPARAM, LPARAM ); // These are actions that can be passed in the cmdline. // FORMAT: "/Action:" #define DESKACTION_NONE 0x00000000 #define DESKACTION_OPENTHEME 0x00000001 #define DESKACTION_OPENMSTHEM 0x00000002 /////////////////////////////////////////////////////////////////////////////// // Globals /////////////////////////////////////////////////////////////////////////////// TCHAR gszDeskCaption[CCH_MAX_STRING]; TCHAR g_szNULL[] = TEXT(""); TCHAR g_szControlIni[] = TEXT("control.ini"); TCHAR g_szPatterns[] = TEXT("patterns") ; TCHAR g_szNone[CCH_NONE]; // this is the '(None)' string TCHAR g_szSystemIni[] = TEXT("system.ini"); TCHAR g_szWindows[] = TEXT("Windows"); TCHAR szRegStr_Colors[] = REGSTR_PATH_COLORS; HDC g_hdcMem = NULL; HBITMAP g_hbmDefault = NULL; BOOL g_bMirroredOS = FALSE; /////////////////////////////////////////////////////////////////////////////// // Externs /////////////////////////////////////////////////////////////////////////////// extern BOOL NEAR PASCAL GetStringFromReg(HKEY hKey, LPCTSTR lpszSubkey, LPCTSTR lpszValueName, LPCTSTR lpszDefault, LPTSTR lpszValue, DWORD cchSizeofValueBuff); //============================================================================================================ // Class //============================================================================================================ class CDisplayControlPanel : public CObjectWithSite { public: ////////////////////////////////////////////////////// // Public Interfaces ////////////////////////////////////////////////////// // *** IUnknown *** virtual STDMETHODIMP QueryInterface(REFIID riid, LPVOID * ppvObj); virtual STDMETHODIMP_(ULONG) AddRef(void); virtual STDMETHODIMP_(ULONG) Release(void); void DisplayDialog(HINSTANCE hInst, HWND hwndParent, LPCTSTR pszCmdline); CDisplayControlPanel(void); virtual ~CDisplayControlPanel(void); private: // Private Member Variables long m_cRef; HANDLE m_hBackgroundThreads; void _ShowDialog(HINSTANCE hInst, HWND hwndParent, LPCTSTR pszCmdline); }; /*--------------------------------------------------------- ** **---------------------------------------------------------*/ BOOL NEAR PASCAL CreateGlobals() { WNDCLASS wc; HBITMAP hbm; HDC hdc; // // Check if the mirroring APIs exist on the current // platform. // g_bMirroredOS = IS_MIRRORING_ENABLED(); if( !GetClassInfo( hInstance, sc_szCoverClass, &wc ) ) { // if two pages put one up, share one dc wc.style = CS_CLASSDC; wc.lpfnWndProc = CoverWindowProc; wc.cbClsExtra = wc.cbWndExtra = 0; wc.hInstance = hInstance; wc.hIcon = (HICON)( wc.hCursor = NULL ); // use a real brush since user will try to paint us when we're "hung" wc.hbrBackground = (HBRUSH) GetStockObject( NULL_BRUSH ); wc.lpszMenuName = NULL; wc.lpszClassName = sc_szCoverClass; if( !RegisterClass( &wc ) ) return FALSE; } hdc = GetDC(NULL); g_hdcMem = CreateCompatibleDC(hdc); ReleaseDC(NULL, hdc); if (!g_hdcMem) return FALSE; hbm = CreateBitmap(1, 1, 1, 1, NULL); if (hbm) { g_hbmDefault = (HBITMAP) SelectObject(g_hdcMem, hbm); SelectObject(g_hdcMem, g_hbmDefault); DeleteObject(hbm); } LoadString(hInstance, IDS_NONE, g_szNone, ARRAYSIZE(g_szNone)); return TRUE; } int DisplaySaveSettings(PVOID pContext, HWND hwnd) { int iRet = 0; if (g_pThemeUI) { g_pThemeUI->DisplaySaveSettings(pContext, hwnd, &iRet); } return iRet; } /////////////////////////////////////////////////////////////////////////////// // // InstallScreenSaver // // Provides a RUNDLL32-callable routine to install a screen saver // /////////////////////////////////////////////////////////////////////////////// // // Windows NT: // // Thunk ANSI version to the Unicode function // void WINAPI InstallScreenSaverW( HWND wnd, HINSTANCE inst, LPWSTR cmd, int shw ); void WINAPI InstallScreenSaverA( HWND wnd, HINSTANCE inst, LPSTR cmd, int shw ) { LPWSTR pwszCmd; int cch; cch = MultiByteToWideChar( CP_ACP, 0, cmd, -1, NULL, 0); if (cch == 0) return; pwszCmd = (LPWSTR) LocalAlloc( LMEM_FIXED, cch * SIZEOF(TCHAR) ); if (pwszCmd == NULL) return; if (0 != MultiByteToWideChar( CP_ACP, 0, cmd, -1, pwszCmd, cch)) { InstallScreenSaverW(wnd, inst, pwszCmd, shw); } LocalFree(pwszCmd); } #define REAL_INSTALL_SCREEN_SAVER InstallScreenSaverW void WINAPI REAL_INSTALL_SCREEN_SAVER( HWND wnd, HINSTANCE inst, LPTSTR cmd, int shw ) { TCHAR buf[ MAX_PATH ]; int timeout; StringCchCopy( buf, ARRAYSIZE(buf), cmd ); PathGetShortPath( buf ); // so msscenes doesn't die WritePrivateProfileString( TEXT("boot"), TEXT("SCRNSAVE.EXE"), buf, TEXT("system.ini") ); SystemParametersInfo( SPI_SETSCREENSAVEACTIVE, TRUE, NULL, SPIF_UPDATEINIFILE ); // make sure the user has a reasonable timeout set SystemParametersInfo( SPI_GETSCREENSAVETIMEOUT, 0, &timeout, 0 ); if( timeout <= 0 ) { // 15 minutes seems like a nice default SystemParametersInfo( SPI_SETSCREENSAVETIMEOUT, 900, NULL, SPIF_UPDATEINIFILE ); } // bring up the screen saver page on our rundll Control_RunDLLW( wnd, inst, TEXT("DESK.CPL,,1"), shw ); } /*****************************************************************************\ * * DeskInitCpl( void ) * \*****************************************************************************/ BOOL DeskInitCpl(void) { InitCommonControls(); CreateGlobals(); return TRUE; } HRESULT OpenAdvancedDialog(HWND hDlg, const CLSID * pClsid) { HRESULT hr = E_FAIL; IEnumUnknown * pEnumUnknown; hr = g_pThemeUI->GetBasePagesEnum(&pEnumUnknown); if (SUCCEEDED(hr)) { IUnknown * punk; hr = IEnumUnknown_FindCLSID(pEnumUnknown, *pClsid, &punk); if (SUCCEEDED(hr)) { IBasePropPage * pBasePage; hr = punk->QueryInterface(IID_PPV_ARG(IBasePropPage, &pBasePage)); if (SUCCEEDED(hr)) { IPropertyBag * pPropertyBag; hr = punk->QueryInterface(IID_PPV_ARG(IPropertyBag, &pPropertyBag)); if (SUCCEEDED(hr)) { if (IsEqualCLSID(PPID_Background, *pClsid)) { // We are going to treat the Background tab differently. We tell it to open // the advanced dialog. We do this so it can close the dialog if the user // clicks to open the Gallery and we need the CPL to close. hr = SHPropertyBag_WriteBOOL(pPropertyBag, SZ_PBPROP_OPENADVANCEDDLG, TRUE); } else { IAdvancedDialog * pAdvAppearDialog; hr = pBasePage->GetAdvancedDialog(&pAdvAppearDialog); if (SUCCEEDED(hr)) { BOOL fEnableApply = FALSE; hr = pAdvAppearDialog->DisplayAdvancedDialog(hDlg, pPropertyBag, &fEnableApply); if (SUCCEEDED(hr) && fEnableApply) { EnableApplyButton(hDlg); g_pThemeUI->UpdatePreview(0); // The Preview settings may have changed. } pAdvAppearDialog->Release(); } } pPropertyBag->Release(); } pBasePage->Release(); } punk->Release(); } pEnumUnknown->Release(); } return hr; } HRESULT SetAdvStartPage(LPTSTR pszStartPage, DWORD cchSize) { HRESULT hr = S_OK; // Does the caller want us to open the advanced dialog to a certain tab? if (g_pThemeUI) { // Yes, so open the dialog. if (!StrCmpI(pszStartPage, TEXT("Theme Settings"))) { OpenAdvancedDialog(g_hDlg, &PPID_Theme); } else if (!StrCmpI(pszStartPage, TEXT("Appearance"))) { OpenAdvancedDialog(g_hDlg, &PPID_BaseAppearance); } else if (!StrCmpI(pszStartPage, TEXT("Web"))) { OpenAdvancedDialog(g_hDlg, &PPID_Background); StringCchCopy(pszStartPage, cchSize, L"Desktop"); } } return hr; } typedef struct { LPCTSTR pszCanonical; UINT nResourceID; } CANONICAL_TO_LOCALIZE_TABMAPPING; CANONICAL_TO_LOCALIZE_TABMAPPING s_TabMapping[] = { {SZ_DISPLAYCPL_OPENTO_THEMES, IDS_TAB_THEMES}, {SZ_DISPLAYCPL_OPENTO_DESKTOP, IDS_TAB_DESKTOP}, {TEXT("Background"), IDS_TAB_DESKTOP}, // These are other names people may use {TEXT("Screen Saver"), IDS_TAB_SCREENSAVER}, // These are other names people may use {SZ_DISPLAYCPL_OPENTO_SCREENSAVER, IDS_TAB_SCREENSAVER}, {SZ_DISPLAYCPL_OPENTO_APPEARANCE, IDS_TAB_APPEARANCE}, {SZ_DISPLAYCPL_OPENTO_SETTINGS, IDS_TAB_SETTINGS}, }; HRESULT _TabCanonicalToLocalized(IN OUT LPTSTR pszStartPage, DWORD cchSize) { HRESULT hr = S_OK; // pszStartPage is an in AND out param for (int nIndex = 0; nIndex < ARRAYSIZE(s_TabMapping); nIndex++) { if (!StrCmpI(s_TabMapping[nIndex].pszCanonical, pszStartPage)) { if (0 == s_TabMapping[nIndex].nResourceID) { hr = E_FAIL; } else { LoadString(hInstance, s_TabMapping[nIndex].nResourceID, pszStartPage, cchSize); } break; } } return hr; } /////////////////////////////////////////////////////////////////////////////// // SetStartPage checks the command line for start page by name. /////////////////////////////////////////////////////////////////////////////// #define SZ_ACTIONFLAG_THEME TEXT("/Action:OpenTheme") #define SZ_ACTIONFLAG_MSTHEME TEXT("/Action:OpenMSTheme") #define SZ_FILEFLAG TEXT("/File:\"") void SetStartPage(PROPSHEETHEADER *ppsh, LPCTSTR pszCmdLine, DWORD * pdwAction, LPTSTR pszPath, DWORD cchPathSize, LPTSTR pszStartPage, DWORD cchSize) { pszPath[0] = L'\0'; pszStartPage[0] = L'\0'; if (pszCmdLine) { // Strip spaces while (*pszCmdLine == TEXT(' ')) { pszCmdLine++; } // Check for @ sign. if (*pszCmdLine == TEXT('@')) { LPCTSTR pszBegin; BOOL fInQuote = FALSE; int cchLen; pszCmdLine++; // Skip past a quote if (*pszCmdLine == TEXT('"')) { pszCmdLine++; fInQuote = TRUE; } // Save the beginning of the name. pszBegin = pszCmdLine; // Find the end of the name. while (pszCmdLine[0] && (fInQuote || (pszCmdLine[0] != TEXT(' '))) && (!fInQuote || (pszCmdLine[0] != TEXT('"')))) { pszCmdLine++; } cchLen = (int)(pszCmdLine - pszBegin); TCHAR szStartPage[MAX_PATH]; StringCchCopy(szStartPage, cchLen+1, pszBegin); SetAdvStartPage(szStartPage, ARRAYSIZE(szStartPage)); // Store the name in the pStartPage field. StringCchCopy(pszStartPage, cchSize, szStartPage); if (StrStrIW(pszCmdLine, SZ_ACTIONFLAG_THEME) || StrStrW(pszCmdLine, SZ_ACTIONFLAG_MSTHEME)) { *pdwAction = (StrStrW(pszCmdLine, SZ_ACTIONFLAG_THEME) ? DESKACTION_OPENTHEME : DESKACTION_OPENMSTHEM); pszCmdLine = StrStrIW(pszCmdLine, SZ_FILEFLAG); if (pszCmdLine) { pszCmdLine += (ARRAYSIZE(SZ_FILEFLAG) - 1); // Skip past flag LPCWSTR pszEnd = StrStrIW(pszCmdLine, L"\""); if (pszEnd) { DWORD cchSize = (DWORD)((pszEnd - pszCmdLine) + 1); StringCchCopy(pszPath, min(cchPathSize, cchSize), pszCmdLine); } } } if (SUCCEEDED(_TabCanonicalToLocalized(pszStartPage, cchSize))) // The caller passes a canonical name but the propsheet wants to localized name { ppsh->dwFlags |= PSH_USEPSTARTPAGE; ppsh->pStartPage = pszStartPage; } } } } /////////////////////////////////////////////////////////////////////////////// // _AddDisplayPropSheetPage adds pages for outside callers... /////////////////////////////////////////////////////////////////////////////// BOOL CALLBACK _AddDisplayPropSheetPage(HPROPSHEETPAGE hpage, LPARAM lParam) { PROPSHEETHEADER FAR * ppsh = (PROPSHEETHEADER FAR *) lParam; if (ppsh) { if (hpage && (ppsh->nPages < MAX_PAGES)) { ppsh->phpage[ppsh->nPages++] = hpage; return TRUE; } } return FALSE; } static int GetClInt( const TCHAR *p ) { BOOL neg = FALSE; int v = 0; while( *p == TEXT(' ') ) p++; // skip spaces if( *p == TEXT('-') ) // is it negative? { neg = TRUE; // yes, remember that p++; // skip '-' char } // parse the absolute portion while( ( *p >= TEXT('0') ) && ( *p <= TEXT('9') ) ) // digits only v = v * 10 + *p++ - TEXT('0'); // accumulate the value return ( neg? -v : v ); // return the result } BOOL CheckRestrictionPage(const PAGEINFO * pPageInfo) { BOOL fRestricted = SHRestricted(pPageInfo->dwPolicy1); if (!fRestricted && pPageInfo->dwPolicy2) { fRestricted = SHRestricted(pPageInfo->dwPolicy2); } return fRestricted; } /////////////////////////////////////////////////////////////////////////////// // CreateReplaceableHPSXA creates a new hpsxa that contains only the // interfaces with valid ReplacePage methods. // APPCOMPAT - EzDesk only implemented AddPages. ReplacePage is NULL for them. /////////////////////////////////////////////////////////////////////////////// typedef struct { UINT count, alloc; IShellPropSheetExt *interfaces[0]; } PSXA; HPSXA CreateReplaceableHPSXA(HPSXA hpsxa) { PSXA *psxa = (PSXA *)hpsxa; DWORD cb = SIZEOF(PSXA) + SIZEOF(IShellPropSheetExt *) * psxa->alloc; PSXA *psxaRet = (PSXA *)LocalAlloc(LPTR, cb); if (psxaRet) { UINT i; psxaRet->count = 0; psxaRet->alloc = psxa->alloc; for (i=0; icount; i++) { if (psxa->interfaces[i]) { psxaRet->interfaces[psxaRet->count++] = psxa->interfaces[i]; } } } return (HPSXA)psxaRet; } HRESULT AddPropSheetExtArrayToThemePageUI(IThemeUIPages * pThemeUI, HPSXA hpsxa) { HRESULT hr = E_INVALIDARG; if (pThemeUI && hpsxa) { PSXA *psxa = (PSXA *)hpsxa; IShellPropSheetExt **spsx = psxa->interfaces; UINT nIndex; for (nIndex = 0; nIndex < psxa->count; nIndex++) { if (psxa->interfaces[nIndex]) { IBasePropPage * pBasePropPage; if (SUCCEEDED(psxa->interfaces[nIndex]->QueryInterface(IID_PPV_ARG(IBasePropPage, &pBasePropPage)))) { pThemeUI->AddBasePage(pBasePropPage); pBasePropPage->Release(); } } } } return hr; } /*****************************************************************************\ DESCRIPTION: If the caller gave the page index, we need to open to that page. The order of the pages has changed from Win2k to Whistler, so map the indexes. Win2K: Index 0: Background Index 1: Screen Saver Index 2: Appearance None: Web None: Effects Index 3: Settings (Index 3) Whistler: (Base Dlg) None: Themes Index 0: Background Index 1: Screen Saver Index 2: Appearance Index 3: Settings Whistler: (Adv Dlg) None: Themes Settings None: Adv Appearance None: Web None: Effects \*****************************************************************************/ int UpgradeStartPageMappping(LPTSTR pszCmdLine, DWORD cchSize) { int nNewStartPage = GetClInt(pszCmdLine); if (pszCmdLine) { switch (nNewStartPage) { case 0: // Background StringCchCopy(pszCmdLine, cchSize, TEXT("@Desktop")); break; case 1: // Screen Saver case 2: // Screen Saver StringCchCopy(pszCmdLine, cchSize, TEXT("@ScreenSaver")); break; case 3: // Settings StringCchCopy(pszCmdLine, cchSize, TEXT("@Settings")); break; default: return nNewStartPage; break; } } else { return nNewStartPage; } return 0; } #define DestroyReplaceableHPSXA(hpsxa) LocalFree((HLOCAL)hpsxa) /*****************************************************************************\ * * DeskShowPropSheet( HWND hwndParent ) * \*****************************************************************************/ void DeskShowPropSheet(HINSTANCE hInst, HWND hwndParent, LPCTSTR pszCmdline) { CDisplayControlPanel displayCPL; displayCPL.DisplayDialog(hInst, hwndParent, pszCmdline); } //=========================== // *** IUnknown Interface *** //=========================== ULONG CDisplayControlPanel::AddRef() { return InterlockedIncrement(&m_cRef); } ULONG CDisplayControlPanel::Release() { ASSERT( 0 != m_cRef ); ULONG cRef = InterlockedDecrement(&m_cRef); if ( 0 == cRef ) { delete this; } else if ((1 == cRef) && m_hBackgroundThreads) { SetEvent(m_hBackgroundThreads); } return cRef; } HRESULT CDisplayControlPanel::QueryInterface(REFIID riid, void **ppvObj) { HRESULT hr = E_NOINTERFACE; static const QITAB qit[] = { QITABENT(CDisplayControlPanel, IObjectWithSite), { 0 }, }; return QISearch(this, qit, riid, ppvObj); } CDisplayControlPanel::CDisplayControlPanel(void) : m_cRef(1) { m_hBackgroundThreads = NULL; } CDisplayControlPanel::~CDisplayControlPanel(void) { if (m_hBackgroundThreads) { CloseHandle(m_hBackgroundThreads); m_hBackgroundThreads = NULL; } } // Wait 30 seconds for hung apps to process our message before we give up. // It would be nice to wait longer, but if the user tries to launch the Display // Control Panel again, it will not launch because we are still running. The only // thing that we will give up on doing after 30 seconds it notifying apps. In the worse // case the user will need to log-off and back in to get apps to refresh. #define MAX_WAITFORHUNGAPPS (30) void CDisplayControlPanel::DisplayDialog(HINSTANCE hInst, HWND hwndParent, LPCTSTR pszCmdline) { if (SUCCEEDED(CoInitialize(NULL))) { SHSetInstanceExplorer(SAFECAST(this, IUnknown *)); _ShowDialog(hInst, hwndParent, pszCmdline); // Wait until the background threads finish. if (m_cRef > 1) { m_hBackgroundThreads = CreateEvent(NULL, FALSE, FALSE, NULL); if (m_hBackgroundThreads && (m_cRef > 1)) { DWORD dwResult = SHProcessMessagesUntilEvent(NULL, m_hBackgroundThreads, (MAX_WAITFORHUNGAPPS * 1000)); if (WAIT_TIMEOUT == dwResult) { TraceMsg(TF_GENERAL, "A thread hung and we needed to shutdown while it was still running."); } Sleep(100); } } SHSetInstanceExplorer(NULL); CoUninitialize(); } } void CDisplayControlPanel::_ShowDialog(HINSTANCE hInst, HWND hwndParent, LPCTSTR pszCmdline) { HPROPSHEETPAGE hpsp, ahPages[MAX_PAGES]; HPSXA hpsxa = NULL; PROPSHEETPAGE psp; PROPSHEETHEADER psh; int i; DWORD exitparam = 0UL; HRESULT hr = S_OK; TCHAR szCmdLine[MAX_PATH]; StringCchCopy(szCmdLine, ARRAYSIZE(szCmdLine), (pszCmdline ? pszCmdline : TEXT(""))); // check if whole sheet is locked out if (SHRestricted(REST_NODISPLAYCPL)) { TCHAR szMessage[255],szTitle[255]; LoadString( hInst, IDS_DISPLAY_DISABLED, szMessage, ARRAYSIZE(szMessage) ); LoadString( hInst, IDS_DISPLAY_TITLE, szTitle, ARRAYSIZE(szTitle) ); MessageBox( hwndParent, szMessage, szTitle, MB_OK | MB_ICONINFORMATION ); return; } // Create the property sheet ZeroMemory(&psh, sizeof(psh)); psh.dwSize = sizeof(PROPSHEETHEADER); psh.dwFlags = (PSH_PROPTITLE | PSH_USECALLBACK); psh.hwndParent = hwndParent; psh.hInstance = hInst; psh.pszCaption = MAKEINTRESOURCE(IDS_DISPLAY_TITLE); psh.nPages = 0; psh.phpage = ahPages; psh.nStartPage = 0; psh.pfnCallback = NULL; if (szCmdLine && szCmdLine[0] && (TEXT('@') != szCmdLine[0])) { psh.nStartPage = UpgradeStartPageMappping(szCmdLine, ARRAYSIZE(szCmdLine)); // We changed the order so do the mapping } ZeroMemory( &psp, sizeof(psp) ); psp.dwSize = sizeof(psp); psp.dwFlags = PSP_DEFAULT; psp.hInstance = hInst; // Build the property sheet. If we are under setup, then just include // the "settings" page, and no otheres if (!g_pThemeUI) { // CoCreate Themes, Appearance, and Advanced Appearance tabs hr = CoCreateInstance(CLSID_ThemeUIPages, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IThemeUIPages, &g_pThemeUI)); } if (SUCCEEDED(hr)) { DWORD dwExecMode; if (g_pThemeUI && (SUCCEEDED(g_pThemeUI->GetExecMode(&dwExecMode))) && (dwExecMode == EM_NORMAL)) { if (!GetSystemMetrics(SM_CLEANBOOT)) { hpsxa = SHCreatePropSheetExtArray(HKEY_LOCAL_MACHINE, REGSTR_PATH_CONTROLSFOLDER TEXT("\\Desk"), 8); } for (i = 0; i < C_PAGES_DESK; i++) { BOOL fHideThisPage = FALSE; if (CheckRestrictionPage(&aPageInfo[i])) { // This page is locked out by admin, don't put it up fHideThisPage = TRUE; } if (-1 == aPageInfo[i].nExtensionID) { psp.pszTemplate = MAKEINTRESOURCE(aPageInfo[i].id); psp.pfnDlgProc = aPageInfo[i].pfnDlgProc; psp.dwFlags = PSP_DEFAULT; psp.lParam = 0L; if (!fHideThisPage && (psp.pfnDlgProc == BackgroundDlgProc)) { // This page can be overridden by extensions if( hpsxa ) { UINT cutoff = psh.nPages; UINT added = 0; HPSXA hpsxaReplace = CreateReplaceableHPSXA(hpsxa); if (hpsxaReplace) { added = SHReplaceFromPropSheetExtArray( hpsxaReplace, CPLPAGE_DISPLAY_BACKGROUND, _AddDisplayPropSheetPage, (LPARAM)&psh); DestroyReplaceableHPSXA(hpsxaReplace); } if (added) { if (psh.nStartPage >= cutoff) psh.nStartPage += added-1; continue; } } } if (!fHideThisPage && (hpsp = CreatePropertySheetPage(&psp))) { psh.phpage[psh.nPages++] = hpsp; } } else if (g_pThemeUI && !fHideThisPage) { IBasePropPage * pBasePage = NULL; // add extensions from the registry // CAUTION: Do not check for "fHideThisPage" here. We need to add the pages for // property sheet extensions even if the "Settings" page is hidden. if (i == IPI_SETTINGS && hpsxa) { UINT cutoff = psh.nPages; UINT added = SHAddFromPropSheetExtArray(hpsxa, _AddDisplayPropSheetPage, (LPARAM)&psh); if (psh.nStartPage >= cutoff) psh.nStartPage += added; } switch (aPageInfo[i].id) { case 0: hr = g_pThemeUI->AddPage(_AddDisplayPropSheetPage, (LPARAM)&psh, aPageInfo[i].nExtensionID); break; case DLG_SCREENSAVER: hr = CoCreateInstance(CLSID_ScreenSaverPage, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IBasePropPage, &pBasePage)); break; case DLG_BACKGROUND: hr = CoCreateInstance(CLSID_CDeskHtmlProp, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IBasePropPage, &pBasePage)); break; default: AssertMsg(0, TEXT("The value must be specified")); break; }; if (pBasePage) { IShellPropSheetExt * pspse = NULL; // If they implement IShellPropSheetExt, then add their base pages. if (SUCCEEDED(pBasePage->QueryInterface(IID_PPV_ARG(IShellPropSheetExt, &pspse)))) { hr = pspse->AddPages(_AddDisplayPropSheetPage, (LPARAM)&psh); pspse->Release(); } hr = g_pThemeUI->AddBasePage(pBasePage); pBasePage->Release(); } } } if (hpsxa) { // Have the dynamically added pages added to IThemeUIPages. AddPropSheetExtArrayToThemePageUI(g_pThemeUI, hpsxa); } // add a fake settings page to fool OEM extensions // !!! this page must be last !!! if (hpsxa) { g_pThemeUI->AddFakeSettingsPage((LPVOID)&psh); } } else { // For the SETUP case, only the display page should show up. hr = g_pThemeUI->AddPage(_AddDisplayPropSheetPage, (LPARAM)&psh, aPageInfo[IPI_SETTINGS].nExtensionID); } if (psh.nStartPage >= psh.nPages) psh.nStartPage = 0; if (psh.nPages) { DWORD dwAction = DESKACTION_NONE; TCHAR szStartPage[MAX_PATH]; WCHAR szOpenPath[MAX_PATH]; IPropertyBag * pPropertyBag; SetStartPage(&psh, szCmdLine, &dwAction, szOpenPath, ARRAYSIZE(szOpenPath), szStartPage, ARRAYSIZE(szStartPage)); hr = g_pThemeUI->QueryInterface(IID_PPV_ARG(IPropertyBag, &pPropertyBag)); if (SUCCEEDED(hr)) { VARIANT var; if (DESKACTION_NONE != dwAction) { var.vt = VT_LPWSTR; var.bstrVal = szOpenPath; hr = pPropertyBag->Write(((DESKACTION_OPENTHEME == dwAction) ? SZ_PBPROP_THEME_LAUNCHTHEME : SZ_PBPROP_APPEARANCE_LAUNCHMSTHEME), &var); } // The following SZ_PBPROP_PREOPEN call will save a "Custom.theme" so users can always go back to // their settings if they don't like changes they make in the CPL. pPropertyBag->Write(SZ_PBPROP_PREOPEN, NULL); pPropertyBag->Release(); } if (PropertySheet(&psh) == ID_PSRESTARTWINDOWS) { exitparam = EWX_REBOOT; } } if (g_pThemeUI) { IUnknown_SetSite(g_pThemeUI, NULL); // Tell him to break the ref-count cycle with his children. g_pThemeUI->Release(); g_pThemeUI = NULL; } // free any loaded extensions if (hpsxa) { SHDestroyPropSheetExtArray(hpsxa); } if (exitparam == EWX_REBOOT) { RestartDialogEx(hwndParent, NULL, exitparam, (SHTDN_REASON_MAJOR_SYSTEM | SHTDN_REASON_MINOR_RECONFIG)); } } return; } DWORD gdwCoverStyle = WS_EX_TOPMOST | WS_EX_TOOLWINDOW; HWND FAR PASCAL CreateCoverWindow( DWORD flags ) { HWND hwndCover = CreateWindowEx( gdwCoverStyle, sc_szCoverClass, g_szNULL, WS_POPUP | WS_VISIBLE | flags, GetSystemMetrics( SM_XVIRTUALSCREEN ), GetSystemMetrics( SM_YVIRTUALSCREEN ), GetSystemMetrics( SM_CXVIRTUALSCREEN ), GetSystemMetrics( SM_CYVIRTUALSCREEN ), NULL, NULL, hInstance, NULL ); if( hwndCover ) { SetForegroundWindow( hwndCover ); if (flags & COVER_NOPAINT) SetCursor(LoadCursor(NULL, IDC_WAIT)); UpdateWindow( hwndCover); } return hwndCover; } /////////////////////////////////////////////////////////////////////////////// // CoverWndProc (see CreateCoverWindow) /////////////////////////////////////////////////////////////////////////////// #define WM_PRIV_KILL_LATER (WM_APP + 100) //Private message to kill ourselves later. LRESULT CALLBACK CoverWindowProc( HWND window, UINT message, WPARAM wparam, LPARAM lparam ) { switch( message ) { case WM_CREATE: SetTimer( window, ID_CVRWND_TIMER, CMSEC_COVER_WINDOW_TIMEOUT, NULL ); break; case WM_TIMER: // Times up... Shut ourself down if (wparam == ID_CVRWND_TIMER) DestroyWindow(window); break; case WM_ERASEBKGND: // NOTE: assumes our class brush is the NULL_BRUSH stock object if( !( GetWindowLong( window, GWL_STYLE ) & COVER_NOPAINT ) ) { HDC dc = (HDC)wparam; RECT rc; if( GetClipBox( dc, (LPRECT)&rc ) != NULLREGION ) { FillRect( dc, (LPRECT)&rc, (HBRUSH) GetStockObject( BLACK_BRUSH ) ); // HACK: make sure fillrect is done before we return // this is to better hide flicker during dynares-crap GetPixel( dc, rc.left + 1, rc.top + 1 ); } } break; // We post a private message to ourselves because: // When WM_CLOSE is processed by this window, it calls DestroyWindow() which results in // WM_ACTIVATE (WA_INACTIVE) message to be sent to this window. If this code calls // DestroyWindow again, it causes a loop. So, instead of calling DestroyWindow immediately, // we post ourselves a message and destroy us letter. case WM_ACTIVATE: if( GET_WM_ACTIVATE_STATE( wparam, lparam ) == WA_INACTIVE ) { PostMessage( window, WM_PRIV_KILL_LATER, 0L, 0L ); return 1L; } break; case WM_PRIV_KILL_LATER: DestroyWindow(window); break; case WM_DESTROY: KillTimer(window, ID_CVRWND_TIMER); break; } return DefWindowProc( window, message, wparam, lparam ); } BOOL _FindCoverWindowCallback(HWND hwnd, LPARAM lParam) { TCHAR szClass[MAX_PATH]; HWND *phwnd = (HWND*)lParam; if( !GetClassName(hwnd, szClass, ARRAYSIZE(szClass)) ) return TRUE; if( StrCmp(szClass, sc_szCoverClass) == 0 ) { if( phwnd ) *phwnd = hwnd; return FALSE; } return TRUE; } LONG APIENTRY CPlApplet( HWND hwnd, WORD message, LPARAM wParam, LPARAM lParam) { LPCPLINFO lpCPlInfo; LPNEWCPLINFO lpNCPlInfo; HWND hwndCover; switch (message) { case CPL_INIT: // Is any one there ? // Init the common controls if (!DeskInitCpl()) return 0; // Load ONE string for emergencies. LoadString (hInstance, IDS_DISPLAY_TITLE, gszDeskCaption, ARRAYSIZE(gszDeskCaption)); return !0; case CPL_GETCOUNT: // How many applets do you support ? return 1; case CPL_INQUIRE: // Fill CplInfo structure lpCPlInfo = (LPCPLINFO)lParam; lpCPlInfo->idIcon = IDI_DISPLAY; lpCPlInfo->idName = IDS_NAME; lpCPlInfo->idInfo = IDS_INFO; lpCPlInfo->lData = 0; break; case CPL_NEWINQUIRE: lpNCPlInfo = (LPNEWCPLINFO)lParam; lpNCPlInfo->hIcon = LoadIcon(hInstance, (LPTSTR) MAKEINTRESOURCE(IDI_DISPLAY)); LoadString(hInstance, IDS_NAME, lpNCPlInfo->szName, ARRAYSIZE(lpNCPlInfo->szName)); if (!LoadString(hInstance, IDS_INFO, lpNCPlInfo->szInfo, ARRAYSIZE(lpNCPlInfo->szInfo))) lpNCPlInfo->szInfo[0] = (TCHAR) 0; lpNCPlInfo->dwSize = sizeof( NEWCPLINFO ); lpNCPlInfo->lData = 0; lpNCPlInfo->dwHelpContext = 0; lpNCPlInfo->szHelpFile[0] = 0; return TRUE; case CPL_DBLCLK: // You have been chosen to run /* * One of your applets has been double-clicked. * wParam is an index from 0 to (NUM_APPLETS-1) * lParam is the lData value associated with the applet */ lParam = 0L; // fall through... case CPL_STARTWPARMS: DeskShowPropSheet( hInstance, hwnd, (LPTSTR)lParam ); // ensure that any cover windows we've created have been destroyed do { hwndCover = 0; EnumWindows( _FindCoverWindowCallback, (LPARAM)&hwndCover ); if( hwndCover ) { DestroyWindow( hwndCover ); } } while( hwndCover ); return TRUE; // Tell RunDLL.exe that I succeeded case CPL_EXIT: // You must really die if (g_hdcMem) { ReleaseDC(NULL, g_hdcMem); g_hdcMem = NULL; } // Fall thru... case CPL_STOP: // You must die if (g_pThemeUI) { IUnknown_SetSite(g_pThemeUI, NULL); // Tell him to break the ref-count cycle with his children. g_pThemeUI->Release(); g_pThemeUI = NULL; } break; case CPL_SELECT: // You have been selected /* * Sent once for each applet prior to the CPL_EXIT msg. * wParam is an index from 0 to (NUM_APPLETS-1) * lParam is the lData value associated with the applet */ break; // // Private message sent when this applet is running under "Setup" // case CPL_SETUP: if (g_pThemeUI) { g_pThemeUI->SetExecMode(EM_SETUP); } break; // Private message used by userenv.dll to refresh the display colors case CPL_POLICYREFRESH: if (g_pThemeUI) // If this object doesn't exist, then we don't need to refresh anything. { IPreviewSystemMetrics * ppsm; if (SUCCEEDED(g_pThemeUI->QueryInterface(IID_PPV_ARG(IPreviewSystemMetrics, &ppsm)))) { ppsm->RefreshColors(); ppsm->Release(); } } SystemParametersInfo(SPI_SETDESKWALLPAPER, 0, 0, FALSE); break; } return 0L; } BOOL WINAPI DeskSetCurrentSchemeW(IN LPCWSTR pwzSchemeName) { BOOL fSuccess = FALSE; IThemeUIPages * pThemeUI = NULL; HRESULT hr; HRESULT hrOle = SHCoInitialize(); if (g_pThemeUI) { hr = g_pThemeUI->QueryInterface(IID_PPV_ARG(IThemeUIPages, &pThemeUI)); } else { hr = CoCreateInstance(CLSID_ThemeManager, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IThemeUIPages, &pThemeUI)); } if (SUCCEEDED(hr)) { IPreviewSystemMetrics * ppsm; hr = pThemeUI->QueryInterface(IID_PPV_ARG(IPreviewSystemMetrics, &ppsm)); if (SUCCEEDED(hr)) { hr = ppsm->DeskSetCurrentScheme(pwzSchemeName); ppsm->Release(); } fSuccess = SUCCEEDED(hr); pThemeUI->Release(); } SHCoUninitialize(hrOle); return fSuccess; } //------------------------------------------------------------------------------------------------ // This function gets the current DPI, reads the last updated DPI from registry and compares // these two. If these two are equal, it returns immediately. // If these two DPI values are different, then it updates the size of UI fonts to reflect the // change in the DPI values. // // This function is called from explorer sothat when DPI value is changed by admin and then every // other user who logs-in gets this change. //------------------------------------------------------------------------------------------------ void WINAPI UpdateUIfontsDueToDPIchange(int iOldDPI, int iNewDPI) { BOOL fSuccess = FALSE; IThemeManager * pThemeMgr = NULL; HRESULT hr; HRESULT hrOle = SHCoInitialize(); if (g_pThemeUI) { hr = g_pThemeUI->QueryInterface(IID_PPV_ARG(IThemeManager, &pThemeMgr)); } else { hr = CoCreateInstance(CLSID_ThemeManager, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IThemeManager, &pThemeMgr)); } if (SUCCEEDED(hr)) { IPropertyBag * pPropertyBag; hr = GetPageByCLSID(pThemeMgr, &PPID_BaseAppearance, &pPropertyBag); if (SUCCEEDED(hr)) { hr = SHPropertyBag_WriteInt(pPropertyBag, SZ_PBPROP_DPI_APPLIED_VALUE, iOldDPI); // We are going to pretend we had the old DPI to force the scale to happen. hr = SHPropertyBag_WriteInt(pPropertyBag, SZ_PBPROP_DPI_MODIFIED_VALUE, iNewDPI); if (SUCCEEDED(hr)) { hr = pThemeMgr->ApplyNow(); } pPropertyBag->Release(); } fSuccess = SUCCEEDED(hr); pThemeMgr->Release(); } SHCoUninitialize(hrOle); } BOOL DeskSetCurrentSchemeA(IN LPCSTR pszSchemeName) { WCHAR wzSchemeName[MAX_PATH]; MultiByteToWideChar(CP_ACP, 0, pszSchemeName, -1, wzSchemeName, ARRAYSIZE(wzSchemeName)); return DeskSetCurrentSchemeW(wzSchemeName); } STDAPI UpdateCharsetChanges(void) { BOOL fSuccess = FALSE; IThemeUIPages * pThemeUI = NULL; HRESULT hr; HRESULT hrOle = SHCoInitialize(); if (g_pThemeUI) { hr = g_pThemeUI->QueryInterface(IID_PPV_ARG(IThemeUIPages, &pThemeUI)); } else { hr = CoCreateInstance(CLSID_ThemeUIPages, NULL, CLSCTX_INPROC_SERVER, IID_PPV_ARG(IThemeUIPages, &pThemeUI)); } if (SUCCEEDED(hr)) { IPreviewSystemMetrics * ppsm; hr = pThemeUI->QueryInterface(IID_PPV_ARG(IPreviewSystemMetrics, &ppsm)); if (SUCCEEDED(hr)) { hr = ppsm->UpdateCharsetChanges(); ppsm->Release(); } pThemeUI->Release(); } SHCoUninitialize(hrOle); return hr; }