|
|
// --------------------------------------------------------------------------
// Module Name: Services.cpp
//
// Copyright (c) 2000, Microsoft Corporation
//
// APIs to communicate with the theme service running in the winlogon
// process context.
//
// History: 2000-08-10 vtan created
// 2000-10-11 vtan rewrite for LPC
// --------------------------------------------------------------------------
#include "stdafx.h"
#include "Services.h"
#include <uxthemep.h>
#include "errors.h"
#include "info.h"
#include "MessageBroadcast.h"
#include "stringtable.h"
#include "themefile.h"
#include "ThemeSection.h"
#include "ThemeServer.h"
#include "tmreg.h"
#include "tmutils.h"
#include <regstr.h> // REGSTR_PATH_POLICIES
#define TBOOL(x) ((BOOL)(x))
#define TW32(x) ((DWORD)(x))
#define THR(x) ((HRESULT)(x))
#define TSTATUS(x) ((NTSTATUS)(x))
#undef ASSERTMSG
#define ASSERTMSG(x, y)
#define goto !!DO NOT USE GOTO!! - DO NOT REMOVE THIS ON PAIN OF DEATH
// --------------------------------------------------------------------------
// CThemeServices::s_hAPIPort
//
// Purpose: Static member variables for CThemeServices.
//
// NOTE: The critical section provides a lock for s_hAPIPort.
// It's not acquired consistently because most of the API calls
// would block trying to acquire the lock while another API call
// is holding the lock across a request. The handle could be
// copied to a local variable but this would defeat the purpose
// of the lock. So the lock isn't used. It's possible for the
// handle to become invalid. If so the request will just fail.
//
// History: 2000-11-09 vtan created
// --------------------------------------------------------------------------
CRITICAL_SECTION CThemeServices::s_lock = {0}; HANDLE CThemeServices::s_hAPIPort = INVALID_HANDLE_VALUE;
// --------------------------------------------------------------------------
// CThemeServices::StaticInitialize
//
// Arguments: <none>
//
// Returns: <none>
//
// Purpose: Initialize static member variables.
//
// History: 2000-10-11 vtan created
// 2000-11-09 vtan make static
// --------------------------------------------------------------------------
void CThemeServices::StaticInitialize (void)
{ if( !InitializeCriticalSectionAndSpinCount(&s_lock, 0) ) { ASSERT(0 == s_lock.DebugInfo); } }
// --------------------------------------------------------------------------
// CThemeServices::~CThemeServices
//
// Arguments: <none>
//
// Returns: <none>
//
// Purpose: Release static resources used by CThemeServices.
//
// History: 2000-10-11 vtan created
// 2000-11-09 vtan make static
// --------------------------------------------------------------------------
void CThemeServices::StaticTerminate (void)
{ ReleaseConnection(); SAFE_DELETECRITICALSECTION(&s_lock); }
// --------------------------------------------------------------------------
// CThemeServices::ThemeHooksOn
//
// Arguments: <none>
//
// Returns: HRESULT
//
// Purpose: Ask the server what the hook DLL HMODULE and
// pfnInitUserApiHook is and call user32!RegisterUserApiHook on
// the client side. This is done because it's specific to the
// session on which the client runs.
//
// History: 2000-11-09 vtan created
// --------------------------------------------------------------------------
HRESULT CThemeServices::ThemeHooksOn (HWND hwndTarget)
{ HRESULT hr = MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT);
if (ConnectedToService()) { NTSTATUS status; THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
ZeroMemory(&portMessageIn, sizeof(portMessageIn)); ZeroMemory(&portMessageOut, sizeof(portMessageOut)); portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_THEMEHOOKSON; portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES); portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE)); status = NtRequestWaitReplyPort(s_hAPIPort, &portMessageIn.portMessage, &portMessageOut.portMessage); CheckForDisconnectedPort(status); if (NT_SUCCESS(status)) { status = portMessageOut.apiThemes.apiGeneric.status; if (NT_SUCCESS(status)) { hr = portMessageOut.apiThemes.apiSpecific.apiThemeHooksOn.out.hr; } } if (!NT_SUCCESS(status)) { hr = HRESULT_FROM_NT(status); }
//---- send the WM_UAHINIT msg to engage hooking now ----
if (SUCCEEDED(hr)) { if (hwndTarget) { (LRESULT)SendMessage(hwndTarget, WM_UAHINIT, 0, 0); } else { CMessageBroadcast messageBroadcast; messageBroadcast.PostAllThreadsMsg(WM_UAHINIT, 0, 0);
//Log(LOG_TMCHANGEMSG, L"Just sent WM_UAHINIT, hwndTarget=0x%x", hwndTarget);
} }
Log(LOG_TMCHANGE, L"ThemeHooksOn called, hr=0x%x", hr); }
return hr; }
// --------------------------------------------------------------------------
// CThemeServices::ThemeHooksOff
//
// Arguments: <none>
//
// Returns: HRESULT
//
// Purpose: Tell the server that this session is unregistering hooks.
// Call user32!UnregisterUserApiHook either way.
//
// History: 2000-11-09 vtan created
// --------------------------------------------------------------------------
HRESULT CThemeServices::ThemeHooksOff (void)
{ HRESULT hr;
hr = MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT); if (ConnectedToService()) { NTSTATUS status; THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
ZeroMemory(&portMessageIn, sizeof(portMessageIn)); ZeroMemory(&portMessageOut, sizeof(portMessageOut)); portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_THEMEHOOKSOFF; portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES); portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE)); status = NtRequestWaitReplyPort(s_hAPIPort, &portMessageIn.portMessage, &portMessageOut.portMessage); CheckForDisconnectedPort(status); if (NT_SUCCESS(status)) { status = portMessageOut.apiThemes.apiGeneric.status; if (NT_SUCCESS(status)) { hr = portMessageOut.apiThemes.apiSpecific.apiThemeHooksOff.out.hr; }
if (SUCCEEDED(hr)) { //---- real unhooking happens on next window message in each process ----
//---- so post a dummy msg to everyone to make it happen asap ----
PostMessage(HWND_BROADCAST, WM_THEMECHANGED, WPARAM(-1), 0);
Log(LOG_TMLOAD, L"Message to kick all window threads in session posted"); } } if (!NT_SUCCESS(status)) { hr = HRESULT_FROM_NT(status); } }
return(hr); }
// --------------------------------------------------------------------------
// CThemeServices::GetStatusFlags
//
// Arguments: pdwFlags = Status flags returned from the theme services.
//
// Returns: HRESULT
//
// Purpose: Gets status flags from the theme services.
//
// History: 2000-08-10 vtan created
// 2000-10-11 vtan rewrite for LPC
// --------------------------------------------------------------------------
HRESULT CThemeServices::GetStatusFlags (DWORD *pdwFlags)
{ HRESULT hr;
hr = MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT); if (ConnectedToService()) { NTSTATUS status; THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
ZeroMemory(&portMessageIn, sizeof(portMessageIn)); ZeroMemory(&portMessageOut, sizeof(portMessageOut)); portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_GETSTATUSFLAGS; portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES); portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE)); status = NtRequestWaitReplyPort(s_hAPIPort, &portMessageIn.portMessage, &portMessageOut.portMessage); CheckForDisconnectedPort(status); if (NT_SUCCESS(status)) { status = portMessageOut.apiThemes.apiGeneric.status; if (NT_SUCCESS(status)) { *pdwFlags = portMessageOut.apiThemes.apiSpecific.apiGetStatusFlags.out.dwFlags; hr = S_OK; } } if (!NT_SUCCESS(status)) { hr = HRESULT_FROM_NT(status); } } return(hr); }
// --------------------------------------------------------------------------
// CThemeServices::GetCurrentChangeNumber
//
// Arguments: piValue = Current change number returned to caller.
//
// Returns: HRESULT
//
// Purpose: Gets the current change number of the theme services.
//
// History: 2000-08-10 vtan created
// 2000-10-11 vtan rewrite for LPC
// --------------------------------------------------------------------------
HRESULT CThemeServices::GetCurrentChangeNumber (int *piValue)
{ HRESULT hr;
hr = MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT); if (ConnectedToService()) { NTSTATUS status; THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
ZeroMemory(&portMessageIn, sizeof(portMessageIn)); ZeroMemory(&portMessageOut, sizeof(portMessageOut)); portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_GETCURRENTCHANGENUMBER; portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES); portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE)); status = NtRequestWaitReplyPort(s_hAPIPort, &portMessageIn.portMessage, &portMessageOut.portMessage); CheckForDisconnectedPort(status); if (NT_SUCCESS(status)) { status = portMessageOut.apiThemes.apiGeneric.status; if (NT_SUCCESS(status)) { *piValue = portMessageOut.apiThemes.apiSpecific.apiGetCurrentChangeNumber.out.iChangeNumber; hr = S_OK; }
Log(LOG_TMLOAD, L"*** GetCurrentChangeNumber: num=%d, hr=0x%x", *piValue, hr); } if (!NT_SUCCESS(status)) { hr = HRESULT_FROM_NT(status); } } return(hr); }
// --------------------------------------------------------------------------
// CThemeServices::SetGlobalTheme
//
// Arguments: hSection = Section to set as the global theme.
//
// Returns: HRESULT
//
// Purpose: Sets the current global theme section handle.
//
// History: 2000-08-10 vtan created
// 2000-10-11 vtan rewrite for LPC
// --------------------------------------------------------------------------
HRESULT CThemeServices::SetGlobalTheme (HANDLE hSection)
{ HRESULT hr;
hr = MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT); if (ConnectedToService()) { NTSTATUS status; THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
ZeroMemory(&portMessageIn, sizeof(portMessageIn)); ZeroMemory(&portMessageOut, sizeof(portMessageOut)); portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_SETGLOBALTHEME; portMessageIn.apiThemes.apiSpecific.apiSetGlobalTheme.in.hSection = hSection; portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES); portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE)); status = NtRequestWaitReplyPort(s_hAPIPort, &portMessageIn.portMessage, &portMessageOut.portMessage); CheckForDisconnectedPort(status); if (NT_SUCCESS(status)) { status = portMessageOut.apiThemes.apiGeneric.status; if (NT_SUCCESS(status)) { hr = portMessageOut.apiThemes.apiSpecific.apiSetGlobalTheme.out.hr; } } if (!NT_SUCCESS(status)) { hr = HRESULT_FROM_NT(status); } } return(hr); }
// --------------------------------------------------------------------------
// CThemeServices::GetGlobalTheme
//
// Arguments: phSection = Section object returned from theme services.
//
// Returns: HRESULT
//
// Purpose: Gets the current global theme section handle.
//
// History: 2000-08-10 vtan created
// 2000-10-11 vtan rewrite for LPC
// --------------------------------------------------------------------------
HRESULT CThemeServices::GetGlobalTheme (HANDLE *phSection)
{ HRESULT hr;
hr = MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT); if (ConnectedToService()) { NTSTATUS status; THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
ZeroMemory(&portMessageIn, sizeof(portMessageIn)); ZeroMemory(&portMessageOut, sizeof(portMessageOut)); portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_GETGLOBALTHEME; portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES); portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE)); status = NtRequestWaitReplyPort(s_hAPIPort, &portMessageIn.portMessage, &portMessageOut.portMessage); CheckForDisconnectedPort(status); if (NT_SUCCESS(status)) { status = portMessageOut.apiThemes.apiGeneric.status; if (NT_SUCCESS(status)) { hr = portMessageOut.apiThemes.apiSpecific.apiGetGlobalTheme.out.hr; if (SUCCEEDED(hr)) { *phSection = portMessageOut.apiThemes.apiSpecific.apiGetGlobalTheme.out.hSection; } } } if (!NT_SUCCESS(status)) { hr = HRESULT_FROM_NT(status); } } return(hr); }
// --------------------------------------------------------------------------
// CThemeServices::CheckThemeSignature
//
// Arguments: pszThemeName = File path of theme to check.
//
// Returns: HRESULT
//
// Purpose: Checks the given theme's signature.
//
// History: 2000-08-10 vtan created
// 2000-10-11 vtan rewrite for LPC
// --------------------------------------------------------------------------
HRESULT CThemeServices::CheckThemeSignature (const WCHAR *pszThemeName)
{ HRESULT hr;
hr = MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT); if (ConnectedToService()) { NTSTATUS status; THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
ZeroMemory(&portMessageIn, sizeof(portMessageIn)); ZeroMemory(&portMessageOut, sizeof(portMessageOut)); portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_CHECKTHEMESIGNATURE; portMessageIn.apiThemes.apiSpecific.apiCheckThemeSignature.in.pszName = pszThemeName; portMessageIn.apiThemes.apiSpecific.apiCheckThemeSignature.in.cchName = lstrlen(pszThemeName) + sizeof('\0'); portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES); portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE)); status = NtRequestWaitReplyPort(s_hAPIPort, &portMessageIn.portMessage, &portMessageOut.portMessage); CheckForDisconnectedPort(status); if (NT_SUCCESS(status)) { status = portMessageOut.apiThemes.apiGeneric.status; if (NT_SUCCESS(status)) { hr = portMessageOut.apiThemes.apiSpecific.apiCheckThemeSignature.out.hr; } } if (!NT_SUCCESS(status)) { hr = HRESULT_FROM_NT(status); } } return(hr); }
// --------------------------------------------------------------------------
// CThemeServices::LoadTheme
//
// Arguments: phSection = Section object to theme returned.
// pszThemeName = Theme file to load.
// pszColorParam = Color.
// pszSizeParam = Size.
// fGlobal = FALSE for a preview.
//
// Returns: HRESULT
//
// Purpose: Loads the given theme and creates a section object for it.
//
// History: 2000-08-10 vtan created
// 2000-10-11 vtan rewrite for LPC
// --------------------------------------------------------------------------
HRESULT CThemeServices::LoadTheme (HANDLE *phSection, const WCHAR *pszThemeName, const WCHAR *pszColor, const WCHAR *pszSize, BOOL fGlobal)
{ HRESULT hr;
*phSection = NULL; // Result if failure
hr = MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT); if (ConnectedToService()) { HANDLE hSection; CThemeLoader *pLoader; WCHAR szColor[MAX_PATH]; WCHAR szSize[MAX_PATH];
// Because the loader makes GDI calls that directly affect the
// client instance of win32k the theme must be loaded on the
// client side. Once the theme is loaded it is handed to the
// server (which creates a new section) and copies the data to
// it. The server then controls the theme data and the client
// discards the temporary theme.
hSection = NULL; pLoader = new CThemeLoader; if (pLoader != NULL) { HINSTANCE hInst = NULL; // Keep the DLL loaded to avoid loading it 3 times below
hr = LoadThemeLibrary(pszThemeName, &hInst);
if (SUCCEEDED(hr) && (pszColor == NULL || *pszColor == L'\0')) { hr = GetThemeDefaults(pszThemeName, szColor, ARRAYSIZE(szColor), NULL, 0); pszColor = szColor; }
if (SUCCEEDED(hr) && (pszSize == NULL || *pszSize == L'\0')) { hr = GetThemeDefaults(pszThemeName, NULL, 0, szSize, ARRAYSIZE(szSize)); pszSize = szSize; }
if (SUCCEEDED(hr)) { hr = pLoader->LoadTheme(pszThemeName, pszColor, pszSize, &hSection, fGlobal); } delete pLoader; if (hInst) { FreeLibrary(hInst); } } else { hr = MakeError32(E_OUTOFMEMORY); } if (SUCCEEDED(hr) && (hSection != NULL)) { NTSTATUS status; THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
ZeroMemory(&portMessageIn, sizeof(portMessageIn)); ZeroMemory(&portMessageOut, sizeof(portMessageOut)); portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_LOADTHEME; portMessageIn.apiThemes.apiSpecific.apiLoadTheme.in.pszName = pszThemeName; portMessageIn.apiThemes.apiSpecific.apiLoadTheme.in.cchName = lstrlen(pszThemeName) + sizeof('\0'); portMessageIn.apiThemes.apiSpecific.apiLoadTheme.in.pszColor = pszColor; portMessageIn.apiThemes.apiSpecific.apiLoadTheme.in.cchColor = lstrlen(pszColor) + sizeof('\0'); portMessageIn.apiThemes.apiSpecific.apiLoadTheme.in.pszSize = pszSize; portMessageIn.apiThemes.apiSpecific.apiLoadTheme.in.cchSize = lstrlen(pszSize) + sizeof('\0'); portMessageIn.apiThemes.apiSpecific.apiLoadTheme.in.hSection = hSection; portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES); portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE)); status = NtRequestWaitReplyPort(s_hAPIPort, &portMessageIn.portMessage, &portMessageOut.portMessage); CheckForDisconnectedPort(status); if (NT_SUCCESS(status)) { status = portMessageOut.apiThemes.apiGeneric.status; if (NT_SUCCESS(status)) { hr = portMessageOut.apiThemes.apiSpecific.apiLoadTheme.out.hr; if (SUCCEEDED(hr)) { *phSection = portMessageOut.apiThemes.apiSpecific.apiLoadTheme.out.hSection; } else { } } } if (!NT_SUCCESS(status)) { hr = HRESULT_FROM_NT(status); } }
// Clear our temporary section
if (hSection != NULL) { // If we didn't transfer the stock objects handles to a new section, clear them always
if (*phSection == NULL) { THR(ClearStockObjects(hSection)); } TBOOL(CloseHandle(hSection)); } } return(hr); }
// --------------------------------------------------------------------------
HRESULT CThemeServices::ProcessLoadGlobalTheme( const WCHAR *pszThemeName, const WCHAR *pszColor, const WCHAR *pszSize, OUT HANDLE *phSection ) { HRESULT hr; *phSection = 0;
hr = MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT); if (ConnectedToService()) { WCHAR szColor[MAX_PATH]; WCHAR szSize[MAX_PATH]; HINSTANCE hInst = NULL;
hr = S_OK;
// In this version of theme load, we're going to request the server initiate
// the load from disk. Because the loader makes GDI calls that directly affect the
// client instance of win32k the theme must be loaded on the client's window station.
// To accomplish this, the theme server may decide to launch a new process or inject
// a thread into some existing process on this client's window station. The client
// doesn't know or care which method is used.
// Addl note: On case of failure, any GDI stock objects created will be cleaned up
// elsewhere.
// fetch default color variant name if needed
if (SUCCEEDED(hr) && !(pszColor && *pszColor)) { // Map in the .msstyles dll to avoid multiple loads.
hr = LoadThemeLibrary(pszThemeName, &hInst);
if( SUCCEEDED(hr) ) { hr = GetThemeDefaults(pszThemeName, szColor, ARRAYSIZE(szColor), NULL, 0); pszColor = szColor; } }
// fetch default size variant name if needed
if (SUCCEEDED(hr) && !(pszSize && *pszSize)) { hr = GetThemeDefaults(pszThemeName, NULL, 0, szSize, ARRAYSIZE(szSize)); pszSize = szSize; }
// drop our msstyles reference, if any
if (hInst) { FreeLibrary(hInst); } // ready to do the LPC request
if (SUCCEEDED(hr)) { NTSTATUS status; THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
ZeroMemory(&portMessageIn, sizeof(portMessageIn)); ZeroMemory(&portMessageOut, sizeof(portMessageOut)); portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_PROCESSLOADTHEME; portMessageIn.apiThemes.apiSpecific.apiProcessLoadTheme.in.pszName = pszThemeName; portMessageIn.apiThemes.apiSpecific.apiProcessLoadTheme.in.cchName = lstrlen(pszThemeName) + sizeof('\0'); portMessageIn.apiThemes.apiSpecific.apiProcessLoadTheme.in.pszColor = pszColor; portMessageIn.apiThemes.apiSpecific.apiProcessLoadTheme.in.cchColor = lstrlen(pszColor) + sizeof('\0'); portMessageIn.apiThemes.apiSpecific.apiProcessLoadTheme.in.pszSize = pszSize; portMessageIn.apiThemes.apiSpecific.apiProcessLoadTheme.in.cchSize = lstrlen(pszSize) + sizeof('\0'); portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES); portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE)); status = NtRequestWaitReplyPort(s_hAPIPort, &portMessageIn.portMessage, &portMessageOut.portMessage); CheckForDisconnectedPort(status); if (NT_SUCCESS(status)) { status = portMessageOut.apiThemes.apiGeneric.status; if (NT_SUCCESS(status)) { hr = portMessageOut.apiThemes.apiSpecific.apiProcessLoadTheme.out.hr; if (SUCCEEDED(hr)) { *phSection = portMessageOut.apiThemes.apiSpecific.apiProcessLoadTheme.out.hSection; } } else { hr = HRESULT_FROM_NT(status); } } else { hr = HRESULT_FROM_NT(status); } } } return(hr); }
// --------------------------------------------------------------------------
HRESULT CThemeServices::CheckColorDepth(CUxThemeFile *pThemeFile) { HRESULT hr = S_OK; THEMEMETRICS *pMetrics = GetThemeMetricsPtr(pThemeFile); DWORD dwDepthRequired = pMetrics->iInts[TMT_MINCOLORDEPTH - TMT_FIRSTINT];
if (MinimumDisplayColorDepth() < dwDepthRequired) { hr = MakeError32(ERROR_BAD_ENVIRONMENT); }
return hr; }
// --------------------------------------------------------------------------
HRESULT CThemeServices::UpdateThemeRegistry(BOOL fThemeActive, LPCWSTR pszThemeFileName, LPCWSTR pszColorParam, LPCWSTR pszSizeParam, BOOL fJustSetActive, BOOL fJustApplied) { if (fThemeActive) { if (fJustSetActive) { //---- see if a theme was previously active ----
WCHAR szThemeName[MAX_PATH];
THR(GetCurrentUserThemeString(THEMEPROP_DLLNAME, L"", szThemeName, ARRAYSIZE(szThemeName))); if (szThemeName[0] != L'\0') { THR(SetCurrentUserThemeInt(THEMEPROP_THEMEACTIVE, 1)); } } else { WCHAR szFullName[MAX_PATH];
if (GetFullPathName(pszThemeFileName, ARRAYSIZE(szFullName), szFullName, NULL) == 0) { SafeStringCchCopyW(szFullName, ARRAYSIZE(szFullName), pszThemeFileName); }
THR(SetCurrentUserThemeInt(THEMEPROP_THEMEACTIVE, 1));
if (fJustApplied) { THR(SetCurrentUserThemeInt(THEMEPROP_LOADEDBEFORE, 1)); THR(SetCurrentUserThemeInt(THEMEPROP_LANGID, (int) GetUserDefaultUILanguage()));
// Theme identification
THR(SetCurrentUserThemeStringExpand(THEMEPROP_DLLNAME, szFullName)); THR(SetCurrentUserThemeString(THEMEPROP_COLORNAME, pszColorParam)); THR(SetCurrentUserThemeString(THEMEPROP_SIZENAME, pszSizeParam)); } else // for forcing theme to be loaded from InitUserTheme()
{ WCHAR szThemeName[MAX_PATH];
THR(GetCurrentUserThemeString(THEMEPROP_DLLNAME, L"", szThemeName, ARRAYSIZE(szThemeName)));
if (lstrcmpiW(szThemeName, szFullName) != 0) { THR(SetCurrentUserThemeString(THEMEPROP_DLLNAME, szFullName));
TW32(DeleteCurrentUserThemeValue(THEMEPROP_LOADEDBEFORE)); TW32(DeleteCurrentUserThemeValue(THEMEPROP_LANGID)); TW32(DeleteCurrentUserThemeValue(THEMEPROP_COLORNAME)); TW32(DeleteCurrentUserThemeValue(THEMEPROP_SIZENAME)); } else { return S_FALSE; // S_FALSE means we did nothing really
} } } } else { THR(SetCurrentUserThemeInt(THEMEPROP_THEMEACTIVE, 0));
if (! fJustSetActive) // wipe out all theme info
{ THR(DeleteCurrentUserThemeValue(THEMEPROP_DLLNAME)); THR(DeleteCurrentUserThemeValue(THEMEPROP_COLORNAME)); THR(DeleteCurrentUserThemeValue(THEMEPROP_SIZENAME)); THR(DeleteCurrentUserThemeValue(THEMEPROP_LOADEDBEFORE)); THR(DeleteCurrentUserThemeValue(THEMEPROP_LANGID)); } }
return S_OK; }
// --------------------------------------------------------------------------
void CThemeServices::SendThemeChangedMsg(BOOL fNewTheme, HWND hwndTarget, DWORD dwFlags, int iLoadId) { WPARAM wParam; LPARAM lParamBits, lParamMixed;
BOOL fExcluding = ((dwFlags & AT_EXCLUDE) != 0); BOOL fCustom = ((dwFlags & AT_PROCESS) != 0);
//---- change number was set in ApplyTheme() for both global and preview cases ----
int iChangeNum; if( SUCCEEDED(GetCurrentChangeNumber(&iChangeNum)) ) { wParam = iChangeNum;
lParamBits = 0; if (fNewTheme) { lParamBits |= WTC_THEMEACTIVE; }
if (fCustom) { lParamBits |= WTC_CUSTOMTHEME; }
if ((hwndTarget) && (! fExcluding)) { SendMessage(hwndTarget, WM_THEMECHANGED, wParam, lParamBits); } else { lParamMixed = (iLoadId << 4) | (lParamBits & 0xf);
CMessageBroadcast messageBroadcast;
// POST the WM_THEMECHANGED_TRIGGER msg to all targeted windows
messageBroadcast.PostAllThreadsMsg(WM_THEMECHANGED_TRIGGER, wParam, lParamMixed);
Log(LOG_TMCHANGEMSG, L"Just Broadcasted WM_THEMECHANGED_TRIGGER: iLoadId=%d", iLoadId); } } } // --------------------------------------------------------------------------
int CThemeServices::GetLoadId(HANDLE hSectionOld) { int iLoadId = 0;
//---- extract LoadId from old section ----
if (hSectionOld) { CThemeSection pThemeSectionFile;
if (SUCCEEDED(pThemeSectionFile.Open(hSectionOld))) { CUxThemeFile *pThemeFile = pThemeSectionFile; if (pThemeFile) { THEMEHDR *hdr = (THEMEHDR *)(pThemeFile->_pbThemeData); if (hdr) { iLoadId = hdr->iLoadId; } } } }
return iLoadId; }
// --------------------------------------------------------------------------
// CThemeServices::ApplyTheme
//
// Arguments: pThemeFile = Object wrapping the theme section to apply.
// dwFlags = Flags.
// hwndTarget = HWND.
//
// Returns: HRESULT
//
// Purpose: Applies the given theme. Do some metric and color depth
// validation, clear the stock bitmaps of the old theme, set
// the given theme as the current theme and broadcast this fact.
//
// History: 2000-08-10 vtan created
// 2000-10-11 vtan rewrite for LPC
// --------------------------------------------------------------------------
// In the design notes below, note that SEND and POST differences are
// significant.
//
// Also, when the "WM_THEMECHANGED_TRIGGER" msg is sent,
// the uxtheme hooking code in each process will:
//
// 1. enumerate all windows for process (using desktop enumeration and
// the per-process "foreign window list") to:
//
// a. process WM_THEMECHANGED for nonclient area
// b. SEND a WM_THEMECHANGED msg to regular window
//
// 2. call FreeRenderObjects() for old theme, if any
// --------------------------------------------------------------------------
// To ensure correct window notification of theme changes and correct removal
// of old theme file RefCounts, the following CRITICAL STEPS must be taken
// in the 4 basic theme transition sequences:
//
// turning ON preview theme:
// a. turn ON global UAE hooks
// b. SEND WM_UAHINIT msg to hwndTarget
// c. SEND WM_THEMECHANGED to hwndTarget
//
// turning ON global theme:
// a. turn ON global UAE hooks
// b. POST WM_UAHINIT msg to all accessible windows
// c. POST WM_THEMECHANGED_TRIGGER to all accessible window threads
//
// turning OFF preview theme:
// c. SEND WM_THEMECHANGED to hwndTarget
//
// turning OFF global theme:
// a. turn OFF global UAE hooks
// b. step "a" will cause WM_THEMECHANGED_TRIGGER-type processing
// to occur from OnHooksDisabled() in each process
// --------------------------------------------------------------------------
HRESULT CThemeServices::ApplyTheme (CUxThemeFile *pThemeFile, DWORD dwFlags, HWND hwndTarget) { HRESULT hr; bool fNewTheme, fGlobal; int iLoadId; WCHAR szThemeFileName[MAX_PATH]; WCHAR szColorParam[MAX_PATH]; WCHAR szSizeParam[MAX_PATH]; HANDLE hSection = NULL;
if (pThemeFile != NULL) { hSection = pThemeFile->Handle(); } fGlobal = (((dwFlags & AT_EXCLUDE) != 0) || ((hwndTarget == NULL) && ((dwFlags & AT_PROCESS) == 0))); fNewTheme = (hSection != NULL); iLoadId = 0;
Log(LOG_TMHANDLE, L"ApplyTheme: hSection=0x%x, dwFlags=0x%x, hwndTarget=0x%x", hSection, dwFlags, hwndTarget);
if (fNewTheme) { if (pThemeFile->HasStockObjects() && !fGlobal) // Don't do this
{ hr = E_INVALIDARG; } else { //---- get some basic info used thruout this function ----
hr = GetThemeNameId(pThemeFile, szThemeFileName, ARRAYSIZE(szThemeFileName), szColorParam, ARRAYSIZE(szColorParam), szSizeParam, ARRAYSIZE(szSizeParam), NULL, NULL); if (SUCCEEDED(hr)) { //---- ensure color depth of monitor(s) are enough for theme ----
if (GetSystemMetrics(SM_REMOTESESSION)) // only check for terminal server sessions
{ hr = CheckColorDepth(pThemeFile); }
if (SUCCEEDED(hr)) { //---- ensure hooks are on ----
hr = ThemeHooksOn(hwndTarget); } } } } else { hr = S_OK; }
if (SUCCEEDED(hr) && fGlobal) { HANDLE hSectionOld;
//---- get a handle to the old global theme (for stock cleanup) ----
hr = GetGlobalTheme(&hSectionOld); if (SUCCEEDED(hr)) { //---- extract Load ID before theme becomes invalid (dwFlags & SECTION_READY=0) ----
if (hSectionOld != NULL) { iLoadId = GetLoadId(hSectionOld); }
//---- tell server to switch global themes ----
hr = SetGlobalTheme(hSection); if (SUCCEEDED(hr)) { //---- update needed registry settings ----
if ((dwFlags & AT_NOREGUPDATE) == 0) // if caller allows update
{ hr = UpdateThemeRegistry(fNewTheme, szThemeFileName, szColorParam, szSizeParam, FALSE, TRUE); if (FAILED(hr)) { Log(LOG_ALWAYS, L"UpdateThemeRegistry call failed, hr=0x%x", hr); hr = S_OK; // not a fatal error
} }
//---- set system metrics, if requested ----
if ((dwFlags & AT_LOAD_SYSMETRICS) != 0) { BOOL fSync = ((dwFlags & AT_SYNC_LOADMETRICS) != 0);
if (fNewTheme) { SetSystemMetrics(GetThemeMetricsPtr(pThemeFile), fSync); } else // just load classic metrics
{ LOADTHEMEMETRICS tm;
hr = InitThemeMetrics(&tm); if (SUCCEEDED(hr)) { SetSystemMetrics(&tm, fSync); } } } } if (hSectionOld != NULL) { TBOOL(CloseHandle(hSectionOld)); } } }
//---- if we turned off global theme, turn hooks off now ----
if (SUCCEEDED(hr)) { if (!fNewTheme && fGlobal) { hr = ThemeHooksOff(); } else { //---- send the correct WM_THEMECHANGED_XXX msg to window(s) ----
SendThemeChangedMsg(fNewTheme, hwndTarget, dwFlags, iLoadId); } } // If the service is down but we're trying to turn Themes off, clean up the registry
else if (hr == MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT) && !fNewTheme && fGlobal && (dwFlags & AT_NOREGUPDATE) == 0) { // Ignore failure here
UpdateThemeRegistry(fNewTheme, NULL, NULL, NULL, FALSE, TRUE); }
return(hr); }
// --------------------------------------------------------------------------
// CThemeServices::AdjustTheme
//
// Arguments: BOOL fEnable - if TRUE, enable CU theme; if FALSE, disable it
//
// Returns: HRESULT
//
// Purpose: for 3rd party skinning apps to cooperate better with theme mgr
//
// History: 2001-03-12 rfernand created
// --------------------------------------------------------------------------
HRESULT CThemeServices::AdjustTheme(BOOL fEnable) { HRESULT hr = UpdateThemeRegistry(fEnable, NULL, NULL, NULL, TRUE, FALSE);
if (SUCCEEDED(hr)) { hr = InitUserTheme(FALSE); }
return hr; }
// --------------------------------------------------------------------------
// CThemeServices::ApplyDefaultMetrics
//
// Arguments: <none>
//
// Returns: <none>
//
// Purpose: Make sure the user metrics gets reset to Windows Standard
//
// History: 2001-03-30 lmouton created
// --------------------------------------------------------------------------
void CThemeServices::ApplyDefaultMetrics(void) { HKEY hKeyThemes; CCurrentUser hKeyCurrentUser(KEY_READ | KEY_WRITE);
Log(LOG_TMLOAD, L"Applying default metrics");
if ((ERROR_SUCCESS == RegOpenKeyEx(hKeyCurrentUser, THEMES_REGKEY L"\\" SZ_DEFAULTVS_OFF, 0, KEY_QUERY_VALUE, &hKeyThemes))) { WCHAR szVisualStyle[MAX_PATH] = {L'\0'}; WCHAR szColor[MAX_PATH] = {L'\0'}; WCHAR szSize[MAX_PATH] = {L'\0'}; BOOL fGotOne; // Note: These will fail for the first user logon, themeui sets these keys and needs to call InstallVS itself
fGotOne = SUCCEEDED(RegistryStrRead(hKeyThemes, SZ_INSTALLVISUALSTYLE, szVisualStyle, ARRAYSIZE(szVisualStyle))); fGotOne = SUCCEEDED(RegistryStrRead(hKeyThemes, SZ_INSTALLVISUALSTYLECOLOR, szColor, ARRAYSIZE(szColor))) || fGotOne; fGotOne = SUCCEEDED(RegistryStrRead(hKeyThemes, SZ_INSTALLVISUALSTYLESIZE, szSize, ARRAYSIZE(szSize))) || fGotOne;
if (fGotOne) { // At least one key is present in the registry, it may be enough
WCHAR szSysDir[MAX_PATH];
if (0 < GetSystemDirectory(szSysDir, ARRAYSIZE(szSysDir))) { WCHAR *pszCmdLine = new WCHAR[MAX_PATH * 5];
if (pszCmdLine) { StringCchPrintfW(pszCmdLine, MAX_PATH * 5, L"%s\\regsvr32.exe /s /n /i:\"" SZ_INSTALL_VS L"%s','%s','%s'\" %s\\themeui.dll", szSysDir, szVisualStyle, szColor, szSize, szSysDir); // Set a reg key to have themeui install the proper settings instead of defaults
// We can't do this now because the user could not be completely logged on
HKEY hKeyRun;
if ((ERROR_SUCCESS == RegOpenKeyEx(hKeyCurrentUser, REGSTR_PATH_RUNONCE, 0, KEY_SET_VALUE, &hKeyRun))) { THR(RegistryStrWrite(hKeyRun, szColor, pszCmdLine)); TW32(RegCloseKey(hKeyRun)); } delete [] pszCmdLine; } } } TW32(RegCloseKey(hKeyThemes)); } }
// --------------------------------------------------------------------------
// CThemeServices::InitUserTheme
//
// Arguments: BOOL fPolicyCheckOnly
// TRUE means
// "only do something if the policy is different from the current loaded theme"
//
// Returns: HRESULT
//
// Purpose: Special entry point for winlogon/msgina to control themes
// for user logon/logoff.
//
// History: 2000-11-11 vtan created
// --------------------------------------------------------------------------
HRESULT CThemeServices::InitUserTheme (BOOL fPolicyCheckOnly) { BOOL fActive = FALSE; BOOL fOldActive = FALSE; BOOL fPolicyActive = FALSE;
//---- should theme be active for this user? ----
if (! IsRemoteThemeDisabled()) { THR(GetCurrentUserThemeInt(THEMEPROP_THEMEACTIVE, FALSE, &fActive)); fOldActive = fActive;
fPolicyActive = ThemeEnforcedByPolicy(fActive != FALSE); if (fPolicyActive) { // Refresh fActive because the policy changed it
THR(GetCurrentUserThemeInt(THEMEPROP_THEMEACTIVE, FALSE, &fActive)); }
if ((fActive) && (ThemeSettingsModified())) { fActive = FALSE; } }
#ifdef DEBUG
if (LogOptionOn(LO_TMLOAD)) { WCHAR szUserName[MAX_PATH]; DWORD dwSize = ARRAYSIZE(szUserName); GetUserName(szUserName, &dwSize);
Log(LOG_TMLOAD, L"InitUserTheme: User=%s, ThemeActive=%d, SM_REMOTESESSION=%d, fPolicyActive=%d, fPolicyCheckOnly=%d", szUserName, fActive, GetSystemMetrics(SM_REMOTESESSION), fPolicyActive, fPolicyCheckOnly); } #endif
BOOL fEarlyExit = FALSE;
if (fPolicyCheckOnly) { // Bail out early if nothing has changed since last time, which is most of the time
if (!fPolicyActive) { Log(LOG_TMLOAD, L"InitUserTheme: Nothing to do after Policy check"); fEarlyExit = TRUE; } else { Log(LOG_TMLOAD, L"InitUserTheme: Reloading after Policy check"); } }
if (!fEarlyExit) { if (fActive) { //---- load this user's theme ----
HRESULT hr = LoadCurrentTheme();
if (FAILED(hr)) { fActive = FALSE; } }
if (! fActive) // turn off themes
{ // if fPolicyActive, force refresh system metrics from temporary defaults
THR(ApplyTheme(NULL, AT_NOREGUPDATE | (fPolicyActive ? AT_LOAD_SYSMETRICS | AT_SYNC_LOADMETRICS: 0), false));
// Apply the proper default metrics
if (fPolicyActive) { ApplyDefaultMetrics(); } } }
return S_OK; // never fail this guy
}
// --------------------------------------------------------------------------
// CThemeServices::InitUserRegistry
//
// Arguments: <none>
//
// Returns: HRESULT
//
// Purpose: Propogate settings from HKLM to HKCU. This should only be
// invoked for ".Default" hives. Assert to ensure this.
//
// History: 2000-11-11 vtan created (ported from themeldr.cpp)
// --------------------------------------------------------------------------
HRESULT CThemeServices::InitUserRegistry (void)
{ HRESULT hr; DWORD dwErrorCode; HKEY hklm; CCurrentUser hkeyCurrentUser(KEY_READ | KEY_WRITE);
#ifdef DEBUG
ASSERT(CThemeServer::IsSystemProcessContext()); #endif /* DEBUG */
if (ERROR_SUCCESS == RegOpenKeyEx(HKEY_LOCAL_MACHINE, THEMEMGR_REGKEY, 0, KEY_QUERY_VALUE | KEY_SET_VALUE, &hklm)) { HKEY hkcu;
if (ERROR_SUCCESS == RegCreateKeyEx(hkeyCurrentUser, THEMEMGR_REGKEY, 0, NULL, 0, KEY_QUERY_VALUE | KEY_SET_VALUE, NULL, &hkcu, NULL)) { int iLMVersion;
hr = RegistryIntRead(hklm, THEMEPROP_LMVERSION, &iLMVersion); if (SUCCEEDED(hr)) { int iCUVersion;
if (FAILED(RegistryIntRead(hkcu, THEMEPROP_LMVERSION, &iCUVersion))) { iCUVersion = 0; } if (iLMVersion != iCUVersion) { BOOL fOverride; WCHAR szValueData[MAX_PATH];
hr = RegistryIntWrite(hkcu, THEMEPROP_LMVERSION, iLMVersion); if (FAILED(hr) || FAILED(RegistryIntRead(hklm, THEMEPROP_LMOVERRIDE, &fOverride))) { fOverride = FALSE; } if ((fOverride != FALSE) || FAILED(RegistryStrRead(hkcu, THEMEPROP_DLLNAME, szValueData, ARRAYSIZE(szValueData))) || (lstrlenW(szValueData) == 0)) { DWORD dwIndex;
dwIndex = 0; do { DWORD dwType, dwValueNameSize, dwValueDataSize; WCHAR szValueName[MAX_PATH];
dwValueNameSize = ARRAYSIZE(szValueName); dwValueDataSize = sizeof(szValueData); dwErrorCode = RegEnumValue(hklm, dwIndex++, szValueName, &dwValueNameSize, NULL, &dwType, reinterpret_cast<LPBYTE>(szValueData), &dwValueDataSize); if ((ERROR_SUCCESS == dwErrorCode) && ((REG_SZ == dwType) || (REG_EXPAND_SZ == dwType)) && (AsciiStrCmpI(szValueName, THEMEPROP_LMOVERRIDE) != 0)) { if (AsciiStrCmpI(szValueName, THEMEPROP_DLLNAME) == 0) { hr = RegistryStrWriteExpand(hkcu, szValueName, szValueData); } else { hr = RegistryStrWrite(hkcu, szValueName, szValueData); } } } while ((dwErrorCode == ERROR_SUCCESS) && SUCCEEDED(hr)); // Since we wrote a new DLL name, erase the old names
(DWORD)RegDeleteValue(hkcu, THEMEPROP_COLORNAME); (DWORD)RegDeleteValue(hkcu, THEMEPROP_SIZENAME); } } } else { hr = S_OK; }
BOOL fLoadedBefore = 1;
if (SUCCEEDED(RegistryIntRead(hklm, THEMEPROP_LOADEDBEFORE, &fLoadedBefore)) && fLoadedBefore == 0) { // HKLM\..\LoadedBefore was reset to 0 during Setup, propagate it to HKCU (.DEFAULT), so that the
// metrics get refreshed for the Winlogon dialogs.
RegistryIntWrite(hkcu, THEMEPROP_LOADEDBEFORE, 0); // Mark it done
RegistryIntWrite(hklm, THEMEPROP_LOADEDBEFORE, 1); }
TW32(RegCloseKey(hkcu)); } else { dwErrorCode = GetLastError(); hr = HRESULT_FROM_WIN32(dwErrorCode); } TW32(RegCloseKey(hklm)); } else { // It's possible for this key to be absent. Ignore the error.
hr = S_OK; } return(hr); }
// --------------------------------------------------------------------------
// CThemeServices::ReestablishServerConnection
//
// Arguments: <none>
//
// Returns: HRESULT
//
// Purpose: Forces an attempt to reconnect to the theme server. Used when
// the port was disconnected but a refresh is desired because the
// server came back up.
//
// History: 2000-11-17 vtan created
// --------------------------------------------------------------------------
HRESULT CThemeServices::ReestablishServerConnection (void)
{ HRESULT hr; NTSTATUS status;
//---- do we have a good looking handle that as gone bad? ----
if ((s_hAPIPort != NULL) && (s_hAPIPort != INVALID_HANDLE_VALUE)) { THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
ZeroMemory(&portMessageIn, sizeof(portMessageIn)); ZeroMemory(&portMessageOut, sizeof(portMessageOut)); portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_PING; portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES); portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE)); status = NtRequestWaitReplyPort(s_hAPIPort, &portMessageIn.portMessage, &portMessageOut.portMessage); if (NT_SUCCESS(status)) { status = portMessageOut.apiThemes.apiGeneric.status; } } else { status = STATUS_PORT_DISCONNECTED; }
if (NT_SUCCESS(status)) { hr = S_OK; } else { //---- our handle has gone bad; reset for another try on next service call ----
LockAcquire(); if ((s_hAPIPort != NULL) && (s_hAPIPort != INVALID_HANDLE_VALUE)) { TBOOL(CloseHandle(s_hAPIPort)); } s_hAPIPort = INVALID_HANDLE_VALUE; LockRelease(); hr = S_FALSE; }
return(hr); }
// --------------------------------------------------------------------------
// CThemeServices::LockAcquire
//
// Arguments: <none>
//
// Returns: <none>
//
// Purpose: Acquire the critical section.
//
// History: 2000-12-01 vtan created
// --------------------------------------------------------------------------
void CThemeServices::LockAcquire (void) { SAFE_ENTERCRITICALSECTION(&s_lock); }
// --------------------------------------------------------------------------
// CThemeServices::LockRelease
//
// Arguments: <none>
//
// Returns: <none>
//
// Purpose: Release the critical section.
//
// History: 2000-12-01 vtan created
// --------------------------------------------------------------------------
void CThemeServices::LockRelease (void) { SAFE_LEAVECRITICALSECTION(&s_lock); }
// --------------------------------------------------------------------------
// CThemeServices::ConnectedToService
//
// Arguments: <none>
//
// Returns: bool
//
// Purpose: Demand connect to service. Only do this once. This function
// has knowledge of where the port exists within the NT object
// namespace.
//
// History: 2000-10-27 vtan created
// --------------------------------------------------------------------------
bool CThemeServices::ConnectedToService (void)
{ if (s_hAPIPort == INVALID_HANDLE_VALUE) { ULONG ulConnectionInfoLength; UNICODE_STRING portName; SECURITY_QUALITY_OF_SERVICE sqos; WCHAR szConnectionInfo[32];
RtlInitUnicodeString(&portName, THEMES_PORT_NAME); sqos.Length = sizeof(sqos); sqos.ImpersonationLevel = SecurityImpersonation; sqos.ContextTrackingMode = SECURITY_DYNAMIC_TRACKING; sqos.EffectiveOnly = TRUE; StringCchCopyW(szConnectionInfo, ARRAYSIZE(szConnectionInfo), THEMES_CONNECTION_REQUEST); ulConnectionInfoLength = sizeof(szConnectionInfo); LockAcquire(); if (!NT_SUCCESS(NtConnectPort(&s_hAPIPort, &portName, &sqos, NULL, NULL, NULL, szConnectionInfo, &ulConnectionInfoLength))) { s_hAPIPort = NULL; } LockRelease(); } return(s_hAPIPort != NULL); }
// --------------------------------------------------------------------------
// CThemeServices::ReleaseConnection
//
// Arguments: <none>
//
// Returns: <none>
//
// Purpose: Releases the API port connection.
//
// History: 2000-11-17 vtan created
// --------------------------------------------------------------------------
void CThemeServices::ReleaseConnection (void)
{ if ((s_hAPIPort != INVALID_HANDLE_VALUE) && (s_hAPIPort != NULL)) { LockAcquire(); TBOOL(CloseHandle(s_hAPIPort)); s_hAPIPort = INVALID_HANDLE_VALUE; LockRelease(); } }
// --------------------------------------------------------------------------
// CThemeServices::CheckForDisconnectedPort
//
// Arguments: status = NTSTATUS of last API request.
//
// Returns: <none>
//
// Purpose: Checks for STATUS_PORT_DISCONNECTED. If found then it
// releases the port object and NULL out the handle.
//
// History: 2000-11-17 vtan created
// --------------------------------------------------------------------------
void CThemeServices::CheckForDisconnectedPort (NTSTATUS status)
{ if (STATUS_PORT_DISCONNECTED == status) { ReleaseConnection(); } #ifdef DEBUG
else if( !NT_SUCCESS(status) ) { Log(LOG_ALWAYS, L"ThemeServices::CheckForDisconnectedPort failure status code: %08lX\n", status); } #endif DEBUG
}
// --------------------------------------------------------------------------
// CThemeServices::CurrentThemeMatch
//
// Arguments: pszThemeName = Name of theme.
// pszColor = Color.
// pszSize = Size.
// fLoadMetricsOnMatch = Load metrics.
//
// Returns: HRESULT
//
// Purpose: Is the current theme the same as the theme specified? This
// can be used to save reloading a theme when it's the same.
//
// History: 2000-11-11 vtan created (ported from themeldr.cpp)
// --------------------------------------------------------------------------
bool CThemeServices::CurrentThemeMatch (LPCWSTR pszThemeName, LPCWSTR pszColor, LPCWSTR pszSize, LANGID wLangID, bool fLoadMetricsOnMatch)
{ bool fMatch; HANDLE hSection;
fMatch = false;
if (SUCCEEDED(GetGlobalTheme(&hSection)) && (hSection != NULL)) { CThemeSection pThemeSectionFile;
if (SUCCEEDED(pThemeSectionFile.Open(hSection))) { fMatch = (ThemeMatch(pThemeSectionFile, pszThemeName, pszColor, pszSize, wLangID) != FALSE);
if (fMatch) { //---- ensure color depth of monitor(s) are enough for theme ----
if (GetSystemMetrics(SM_REMOTESESSION)) // only check for terminal server sessions
{ if (FAILED(CheckColorDepth(pThemeSectionFile))) { fMatch = FALSE; } } }
if (fMatch && fLoadMetricsOnMatch) { SetSystemMetrics(GetThemeMetricsPtr(pThemeSectionFile), FALSE); } } TBOOL(CloseHandle(hSection)); } return(fMatch); }
// --------------------------------------------------------------------------
// CThemeServices::LoadCurrentTheme
//
// Arguments: <none>
//
// Returns: HRESULT
//
// Purpose: Loads the current theme as set in the registry for the
// impersonated user.
//
// History: 2000-11-11 vtan created (ported from themeldr.cpp)
// --------------------------------------------------------------------------
HRESULT CThemeServices::LoadCurrentTheme (void)
{ HRESULT hr = S_OK; WCHAR szThemeName[MAX_PATH]; WCHAR szColorName[MAX_PATH]; WCHAR szSizeName[MAX_PATH];
THR(GetCurrentUserThemeString(THEMEPROP_DLLNAME, L"", szThemeName, ARRAYSIZE(szThemeName))); if (szThemeName[0] != L'\0') { int iLoadedBefore; HANDLE hSection; int nLangID;
THR(GetCurrentUserThemeString(THEMEPROP_COLORNAME, L"", szColorName, ARRAYSIZE(szColorName))); THR(GetCurrentUserThemeString(THEMEPROP_SIZENAME, L"", szSizeName, ARRAYSIZE(szSizeName))); THR(GetCurrentUserThemeInt(THEMEPROP_LOADEDBEFORE, 0, &iLoadedBefore)); THR(GetCurrentUserThemeInt(THEMEPROP_LANGID, -1, &nLangID));
// Does new user's theme match the current theme?
if (nLangID != -1 && CurrentThemeMatch(szThemeName, szColorName, szSizeName, (LANGID) nLangID, (iLoadedBefore == 0))) { DWORD dwFlags;
// Everything is done except this registry value.
if (iLoadedBefore == 0) { THR(SetCurrentUserThemeInt(THEMEPROP_LOADEDBEFORE, 1)); }
hr = GetStatusFlags(&dwFlags); if (SUCCEEDED(hr)) { if ((dwFlags & QTS_RUNNING) == 0) { hr = GetGlobalTheme(&hSection); if (SUCCEEDED(hr)) { CUxThemeFile file; // Will clean up on destruction
if (SUCCEEDED(file.OpenFromHandle(hSection, FILE_MAP_READ, TRUE))) { hr = ApplyTheme(&file, 0, false); } } } } } else { hr = LoadTheme(&hSection, szThemeName, szColorName, szSizeName, TRUE);
if (SUCCEEDED(hr)) { DWORD dwFlags;
dwFlags = 0;
// Has this theme been loaded before?
// or has the user changed his/her language?
if (iLoadedBefore == 0 || ((nLangID != -1) && ((LANGID) nLangID != GetUserDefaultUILanguage()))) { dwFlags |= AT_LOAD_SYSMETRICS; }
CUxThemeFile file; // Will clean up on destruction
if (SUCCEEDED(file.OpenFromHandle(hSection, FILE_MAP_READ, TRUE))) { hr = ApplyTheme(&file, dwFlags, false); } } } } else { hr = MakeError32(ERROR_NOT_FOUND); }
return(hr); }
// --------------------------------------------------------------------------
// CThemeServices::SectionProcessType
//
// Arguments: hSection = Section to walk and clear stock objects in.
//
// Returns: HRESULT
//
// Purpose: Walks the section (read-only) and finds HBITMAPs that are stock
// listed in the section and deletes these objects.
// This is work that needs to be done on the client.
//
// History: 2000-11-17 lmouton created
// vtan rewritten from themeldr.cpp
// --------------------------------------------------------------------------
int CThemeServices::SectionProcessType (const BYTE *pbThemeData, MIXEDPTRS& u)
{ UNPACKED_ENTRYHDR header;
FillAndSkipHdr(u, &header); switch (header.ePrimVal) { case TMT_PARTJUMPTABLE: case TMT_STATEJUMPTABLE: break; case TMT_DIBDATA: TMBITMAPHEADER *pThemeBitmapHeader;
pThemeBitmapHeader = reinterpret_cast<TMBITMAPHEADER*>(u.pb); ASSERT(pThemeBitmapHeader->dwSize == TMBITMAPSIZE);
// Clean up stock bitmaps
if (pThemeBitmapHeader->hBitmap != NULL) { HBITMAP hBitmap;
hBitmap = pThemeBitmapHeader->hBitmap; hBitmap = ClearBitmapAttributes(hBitmap, SBA_STOCK); #ifdef DEBUG
if (hBitmap == NULL) { Log(LOG_TMBITMAP, L"UxTheme: ClearBitmapAttributes failed for %8X in SetGlobalTheme", hBitmap); } else if (!DeleteObject(hBitmap)) { Log(LOG_TMBITMAP, L"Failed to delete bitmap:%8X", hBitmap); } #else
if (hBitmap != NULL) { DeleteObject(hBitmap); } #endif
}
// Fall thru to the default case that increments the mixed pointer.
default: u.pb += header.dwDataLen; break; } return(header.ePrimVal); }
// --------------------------------------------------------------------------
// CThemeServices::SectionWalkData
//
// Arguments: pV = Address of section data to walk.
// iIndex = Index into section.
//
// Returns: <none>
//
// Purpose:
//
// History: 2000-11-17 lmouton created
// vtan rewritten from themeldr.cpp
// --------------------------------------------------------------------------
void CThemeServices::SectionWalkData (const BYTE *pbThemeData, int iIndexIn)
{ bool fDone; MIXEDPTRS u;
fDone = false; u.pb = const_cast<BYTE*>(pbThemeData + iIndexIn); while (!fDone) { //---- special post-handling ----
switch (SectionProcessType(pbThemeData, u)) { int i, iLimit, iIndex;
case TMT_PARTJUMPTABLE: u.pi++; iLimit = *u.pb++; for (i = 0; i < iLimit; ++i) { iIndex = *u.pi++; if (iIndex > -1) { SectionWalkData(pbThemeData, iIndex); } } fDone = true; break; case TMT_STATEJUMPTABLE: iLimit = *u.pb++; for (i = 0; i < iLimit; ++i) { iIndex = *u.pi++; if (iIndex > -1) { SectionWalkData(pbThemeData, iIndex); } } fDone = true; break; case TMT_JUMPTOPARENT: fDone = true; break; default: break; } } }
// --------------------------------------------------------------------------
// CThemeServices::ClearStockObjects
//
// Arguments: hSection = Section to walk and clear bitmaps in.
//
// Returns: HRESULT
//
// Purpose: Walks the section (read-only) and finds HBITMAPs and corresponding
// HBRUSHes that are stock listed in the section and deletes these objects.
// This is work that needs to be done on the client.
//
// History: 2000-11-17 lmouton created
// vtan rewritten from themeldr.cpp
// 2001-05-15 lmouton Added semaphore support for cleaning from ~CUxThemeFile
// --------------------------------------------------------------------------
HRESULT CThemeServices::ClearStockObjects (HANDLE hSection, BOOL fForce)
{ HRESULT hr; BYTE* pbThemeData; bool bWriteable = true; HANDLE hSectionWrite = NULL;
// If the section is global, we can't write to it since only the server can.
// So let's try to get write access, else we'll call the server
pbThemeData = static_cast<BYTE*>(MapViewOfFile(hSection, FILE_MAP_WRITE, 0, 0, 0)); if (pbThemeData == NULL) { // Let's try to reopen a write handle for ourselves
if (DuplicateHandle(GetCurrentProcess(), hSection, GetCurrentProcess(), &hSectionWrite, FILE_MAP_WRITE, FALSE, 0) != FALSE) { pbThemeData = static_cast<BYTE*>(MapViewOfFile(hSectionWrite, FILE_MAP_WRITE, 0, 0, 0)); }
if (pbThemeData == NULL) { // We can't open it for write, let's try read-only
pbThemeData = static_cast<BYTE*>(MapViewOfFile(hSection, FILE_MAP_READ, 0, 0, 0)); bWriteable = false; } #ifdef DEBUG
else { Log(LOG_TMLOAD, L"Reopened section %d for write", reinterpret_cast<THEMEHDR*>(pbThemeData)->iLoadId); } #endif
}
#ifdef DEBUG
if (LogOptionOn(LO_TMLOAD)) { // Unexpected failure
ASSERT(pbThemeData != NULL); } #endif
if (pbThemeData != NULL) { int i, iLimit; THEMEHDR *pThemeHdr; APPCLASSLIVE *pACL;
pThemeHdr = reinterpret_cast<THEMEHDR*>(pbThemeData); hr = S_OK;
Log(LOG_TMLOAD, L"ClearStockObjects for section %X, bWriteable=%d, dwFlags=%d, iLoadId=%d, fForce=%d", hSection, bWriteable, pThemeHdr->dwFlags, pThemeHdr->iLoadId, fForce);
volatile THEMEHDR* pTmpHdr = pThemeHdr; // If this is a local section with stock objects, it's this process's responsibility to
// clean them up.
if ((pTmpHdr->dwFlags & SECTION_HASSTOCKOBJECTS) && !(pTmpHdr->dwFlags & SECTION_GLOBAL)) { // Make sure we don't contend with any other cleanup threads
WCHAR szName[64];
if (pThemeHdr->iLoadId != 0) { // Each section has a unique iLoadId but not across sessions
// It has to be global because the Theme service can create it
StringCchPrintfW(szName, ARRAYSIZE(szName), L"Global\\ClearStockGlobal%d-%d", pThemeHdr->iLoadId, NtCurrentPeb()->SessionId); } else { // The session is local to the process
StringCchPrintfW(szName, ARRAYSIZE(szName), L"ClearStockLocal%d-%d", GetCurrentProcessId(), NtCurrentPeb()->SessionId); }
HANDLE hSemaphore = CreateSemaphore(NULL, 0, 1, szName); DWORD dwError = GetLastError();
Log(LOG_TMLOAD, L"Opening semaphore %s, hSemaphore=%X, gle=%d", szName, hSemaphore, dwError);
// If CreateSemaphore fails for another reason, ignore the failure, we have to clean and we only
// risk a GDI assert on CHK builds.
// We'll get access denied if the semaphore was created in the service on SetGlobalTheme, but
// in this case fForce is true for winlogon, false for the other callers.
bool bAlreadyExists = (dwError == ERROR_ALREADY_EXISTS || dwError == ERROR_ACCESS_DENIED);
#ifdef DEBUG
if (LogOptionOn(LO_TMLOAD)) { // Unexpected failure
ASSERT(dwError == 0 || bAlreadyExists); } #endif
// Recheck again that this is a local section with stock objects. We've seen in stress
// runs where in the middle of creating the semaphore (above), another thread
// raced ahead and removed the SECTION_GLOBAL flag and cleared the stock bitmaps, but hadn't
// quite gotten around to removing the SECTION_HASSTOCKOBJECTS bit before this thread
// woke up and executed, resulting in hundreds of GDI assertions that the bitmap isn't stock, etc.
if ((!bAlreadyExists || fForce) && ((pTmpHdr->dwFlags & SECTION_HASSTOCKOBJECTS) && !(pTmpHdr->dwFlags & SECTION_GLOBAL))) { // If nobody else is already doing it
Log(LOG_TMLOAD, L"ClearStockObjects: Clearing data, semaphore = %s", szName); #ifdef DEBUG
bool bDisconnected = false; #endif
pACL = reinterpret_cast<APPCLASSLIVE*>(pbThemeData + pThemeHdr->iSectionIndexOffset); iLimit = pThemeHdr->iSectionIndexLength / sizeof(APPCLASSLIVE);
for (i = 0; i < iLimit; ++pACL, ++i) { SectionWalkData(pbThemeData, pACL->iIndex); } if (bWriteable) { pThemeHdr->dwFlags &= ~SECTION_HASSTOCKOBJECTS; // To avoid doing it twice
} else { // Can't write to it, let's call MarkSection in the service to do it
hr = MakeError32(ERROR_SERVICE_REQUEST_TIMEOUT); if (ConnectedToService()) { NTSTATUS status; THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut;
ZeroMemory(&portMessageIn, sizeof(portMessageIn)); ZeroMemory(&portMessageOut, sizeof(portMessageOut)); portMessageIn.apiThemes.apiGeneric.ulAPINumber = API_THEMES_MARKSECTION; portMessageIn.apiThemes.apiSpecific.apiMarkSection.in.hSection = hSection; portMessageIn.apiThemes.apiSpecific.apiMarkSection.in.dwAdd = 0; portMessageIn.apiThemes.apiSpecific.apiMarkSection.in.dwRemove = SECTION_HASSTOCKOBJECTS; portMessageIn.portMessage.u1.s1.DataLength = sizeof(API_THEMES); portMessageIn.portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE)); status = NtRequestWaitReplyPort(s_hAPIPort, &portMessageIn.portMessage, &portMessageOut.portMessage); CheckForDisconnectedPort(status); #ifdef DEBUG
if (STATUS_PORT_DISCONNECTED == status) { bDisconnected = true; // This failure must not trigger the assert
} #endif
if (NT_SUCCESS(status)) { status = portMessageOut.apiThemes.apiGeneric.status; if (NT_SUCCESS(status)) { hr = S_OK; } } if (!NT_SUCCESS(status)) { hr = HRESULT_FROM_NT(status); } } } #ifdef DEBUG
// When the service goes down, we may fail ApplyTheme (so iLoadId is still 0),
// and we fail MarkSection too, ignore this error.
if (LogOptionOn(LO_TMLOAD) && !bDisconnected && pThemeHdr->iLoadId != 0) { ASSERT(!(pThemeHdr->dwFlags & SECTION_HASSTOCKOBJECTS)); } #endif
} else { Log(LOG_TMLOAD, L"ClearStockObjects: Doing nothing, semaphore %s, dwFlags=%d, gle=%d", szName, pThemeHdr->dwFlags, dwError); } if (hSemaphore) { Log(LOG_TMLOAD, L"ClearStockObjects: Closing semaphore %X", hSemaphore); CloseHandle(hSemaphore); } }
TBOOL(UnmapViewOfFile(pbThemeData)); } else { DWORD dwErrorCode;
dwErrorCode = GetLastError(); hr = HRESULT_FROM_WIN32(dwErrorCode); }
if (hSectionWrite != NULL) { CloseHandle(hSectionWrite); }
return(hr); }
// --------------------------------------------------------------------------
// CThemeServices::ThemeSettingsModified
//
// Returns: BOOL
//
// Purpose: Detects that appearance settings have been changed on a
// W2K machine by a roaming user.
//
// History: 2000-11-28 lmouton created
// --------------------------------------------------------------------------
bool CThemeServices::ThemeSettingsModified (void)
{ WCHAR szCurrent[MAX_PATH]; WCHAR szNewCurrent[MAX_PATH];
// If NewCurrent exists and is different from Current, Current
// has been tampered with on a roaming W2K machine
THR(GetCurrentUserString(CONTROLPANEL_APPEARANCE_REGKEY, THEMEPROP_CURRSCHEME, L" ", szCurrent, ARRAYSIZE(szCurrent))); THR(GetCurrentUserString(CONTROLPANEL_APPEARANCE_REGKEY, THEMEPROP_NEWCURRSCHEME, L" ", szNewCurrent, ARRAYSIZE(szNewCurrent))); return((lstrcmpW(szNewCurrent, L" ") != 0) && (lstrcmpW(szCurrent, szNewCurrent) != 0)); }
// --------------------------------------------------------------------------
// CThemeServices::ThemeEnforcedByPolicy
//
// Arguments: BOOL TRUE if a .msstyles file is currently active for the user
//
// Returns: BOOL TRUE if the policy changed something
//
// Purpose: Loads the .msstyles file specified in the SetVisualStyle policy.
//
// History: 2000-11-28 lmouton created
// --------------------------------------------------------------------------
bool CThemeServices::ThemeEnforcedByPolicy (bool fActive)
{ bool fPolicyPresent; HKEY hKeyPol = NULL; CCurrentUser hKeyCurrentUser(KEY_READ | KEY_WRITE);
fPolicyPresent = false;
// See if a policy overrides the theme name
if ((ERROR_SUCCESS == RegOpenKeyEx(hKeyCurrentUser, REGSTR_PATH_POLICIES L"\\" SZ_THEME_POLICY_KEY, 0, KEY_QUERY_VALUE, &hKeyPol))) { WCHAR szNewThemeName[MAX_PATH + 1];
StringCchCopyW(szNewThemeName, ARRAYSIZE(szNewThemeName), L" "); if (SUCCEEDED(RegistryStrRead(hKeyPol, SZ_POLICY_SETVISUALSTYLE, szNewThemeName, ARRAYSIZE(szNewThemeName)))) { if (szNewThemeName[0] == L'\0') // Disable themes
{ if (fActive) { THR(UpdateThemeRegistry(FALSE, NULL, NULL, NULL, FALSE, FALSE));
fPolicyPresent = true; } } else { if (FileExists(szNewThemeName)) { HRESULT hr = UpdateThemeRegistry(TRUE, szNewThemeName, NULL, NULL, FALSE, FALSE);
THR(hr); if (!fActive || hr == S_OK) { // If we had no theme before or a different one, say we changed something
fPolicyPresent = true; } } } } TW32(RegCloseKey(hKeyPol)); } return(fPolicyPresent); }
// --------------------------------------------------------------------------
// ::CThemeServices::SendProcessAssignSection
//
// Arguments: hrApply = Error HRESULT to forward to themeservice
// hSection = read-write theme section handle, valid in current address space.
// dwHash = hash value for theme section
// pPortMsgIn = port message object for parameters sent to service
// pPortMsgOut = port message object for parameters received in response
//
//
// Returns: NTSTATUS
//
// Purpose: Worker function to send API_THEMES_PROCESSASSIGNSECTION to request
//
// History: 2002-02-26 scotthan created
// --------------------------------------------------------------------------
NTSTATUS CThemeServices::SendProcessAssignSection( HRESULT hrAssign, HANDLE hSection, DWORD dwHash, OUT THEMESAPI_PORT_MESSAGE* pPortMsgIn, OUT THEMESAPI_PORT_MESSAGE* pPortMsgOut ) { NTSTATUS status = STATUS_PORT_DISCONNECTED; if (ConnectedToService()) { ZeroMemory(pPortMsgIn, sizeof(*pPortMsgIn)); ZeroMemory(pPortMsgOut, sizeof(*pPortMsgOut)); pPortMsgIn->apiThemes.apiGeneric.ulAPINumber = API_THEMES_PROCESSASSIGNSECTION; pPortMsgIn->apiThemes.apiSpecific.apiProcessAssignSection.in.hrLoad = hrAssign; pPortMsgIn->apiThemes.apiSpecific.apiProcessAssignSection.in.dwHash = dwHash; pPortMsgIn->apiThemes.apiSpecific.apiProcessAssignSection.in.hSection = hSection;
pPortMsgIn->portMessage.u1.s1.DataLength = sizeof(API_THEMES); pPortMsgIn->portMessage.u1.s1.TotalLength = static_cast<CSHORT>(sizeof(THEMESAPI_PORT_MESSAGE));
status = NtRequestWaitReplyPort(s_hAPIPort,&pPortMsgIn->portMessage, &pPortMsgOut->portMessage); CheckForDisconnectedPort(status); }
return status; }
// --------------------------------------------------------------------------
// ::ProcessLoadTheme_RunDLLW
//
// (Deliberately undocumented entrypoint.)
//
// History: 2002-02-26 scotthan created
// --------------------------------------------------------------------------
STDAPI_(void) ProcessLoadTheme_RunDLLW( HWND hwndDefer, HINSTANCE hInst, LPWSTR lpwszDeferMsg, int cTimeout) { __try { WCHAR szModule[MAX_PATH];
// validate that we're a rundll32 process launched from %windir%\system32
if( GetModuleFileName(NULL, szModule, ARRAYSIZE(szModule)) ) { WCHAR szRunDll[MAX_PATH]; if( GetSystemDirectory(szRunDll, ARRAYSIZE(szRunDll)) && SUCCEEDED(StringCchCatW(szRunDll, ARRAYSIZE(szRunDll), L"\\rundll32.exe")) ) { if( 0 == AsciiStrCmpI(szModule, szRunDll) != 0 ) { CThemeServices::ProcessLoaderEntry(lpwszDeferMsg); } } } } __except(EXCEPTION_EXECUTE_HANDLER) { Log(LOG_ALWAYS, L"Exception in theme loader process."); } }
// --------------------------------------------------------------------------
// ::CThemeServices::ProcessLoaderEntry
//
// Arguments: lpwszCmdLine = cmdline
//
//
// Returns: HRESULT, but no one is listening.
//
// Purpose: This is the secure loader process's RunDLL worker function
// (needed to be a static member to access private static
// class data.).
//
// History: 2002-02-26 scotthan created
// --------------------------------------------------------------------------
HRESULT CThemeServices::ProcessLoaderEntry(LPWSTR lpwszCmdLine) { size_t cchCmdLine; HRESULT hr = StringCchLengthW(lpwszCmdLine, (MAX_PATH*3) + 2 /* three strings delimited by 2 spaces */, &cchCmdLine); ASSERT(SUCCEEDED(hr)); if( SUCCEEDED(hr) ) { // make a local copy of the command line, which we'll modify in parsing.
LPWSTR pszCmdLine = new WCHAR[cchCmdLine + 1];
if( pszCmdLine ) { LPWSTR rgArgs[3] = {0}; // array of pointers to args in pszCmdLine
int iArg = 0; // tracks current index into rgArgs.
enum {iThemeFileArg, iColorVariantArg, iSizeVariantArg}; // rgArg array indices
StringCchCopyW(pszCmdLine, cchCmdLine + 1, lpwszCmdLine); LPWSTR psz; // working cmdline char pointer
LPWSTR pszArg; // address of current arg token
// skip spaces
for( psz = pszCmdLine; L' ' == *psz; psz++);
// init first arg token
pszArg = psz;
// capture arg tokens (delimited by '?') and null terminate each.
while(*psz != 0) { if( L'?' == *psz ) { // null-terminate and assign to arg list
*psz = 0; // null terminate
if( iArg < ARRAYSIZE(rgArgs) ) { rgArgs[iArg] = pszArg; pszArg = ++psz; // advance past the delimiter and update arg token.
} iArg++; // advance current arg index
} else { psz++; // advance cmdline char ptr.
} }
// assign the last arg.
if( iArg < ARRAYSIZE(rgArgs) ) { rgArgs[iArg++] = pszArg; } // check arg count. Correct count should be == ARRAYSIZE(rgArgs)
if( ARRAYSIZE(rgArgs) == iArg ) { hr = S_OK;
HANDLE hSection = NULL; DWORD dwHash = 0;
// create the memory section
CThemeLoader *pLoader = new CThemeLoader; if (pLoader != NULL) { hr = pLoader->LoadTheme(rgArgs[iThemeFileArg], rgArgs[iColorVariantArg], rgArgs[iSizeVariantArg], &hSection, TRUE); delete pLoader; } else { hr = E_OUTOFMEMORY; }
// compute a hash to further secure the data
if( SUCCEEDED(hr) ) { ASSERT(hSection); #if 0
hr = HashThemeSection(hSection, Get GetCurrentProcessId() /* as hash parameter, which server possesses and can use for data validate */, &dwHash ); #endif
}
// unconditionally notify service of what happened so the result can be propagated to the
// LPC client of API_THEMES_PROCESSLOADTHEME:
NTSTATUS status; THEMESAPI_PORT_MESSAGE portMessageIn, portMessageOut; status = SendProcessAssignSection(hr, hSection, dwHash, &portMessageIn, &portMessageOut);
// if we were able to create the section and hash it...
if( SUCCEEDED(hr) ) { // check LPC result
hr = HRESULT_FROM_NT(status); if (NT_SUCCESS(status)) { // check service's high-level result
status = portMessageOut.apiThemes.apiGeneric.status; hr = HRESULT_FROM_NT(status);
if (NT_SUCCESS(status)) { // check service's specialized result*/
hr = portMessageOut.apiThemes.apiSpecific.apiProcessAssignSection.out.hr; } } }
if( hSection != NULL ) { // if we failed anywhere, we need to destroy stock objects
if( FAILED(hr) ) { ClearStockObjects( hSection, TRUE ); } // And whether we succeeded or failed, we close the section handle;
// we're done with this local read-write copy.
CloseHandle(hSection); } } else { hr = E_INVALIDARG; } delete [] pszCmdLine; } else { hr = E_OUTOFMEMORY; } }
return hr; }
|