WindowsXP-SP1/shell/themes/uxtheme/appinfo.cpp
2020-09-30 16:53:49 +02:00

691 lines
19 KiB
C++

//---------------------------------------------------------------------------
// AppInfo.cpp - manages app-level theme information
//---------------------------------------------------------------------------
#include "stdafx.h"
#include "info.h"
#include "AppInfo.h"
#include "sethook.h"
#include "services.h"
#include "themefile.h"
#include "tmreg.h"
#include "renderlist.h"
#include "nctheme.h"
#include "loader.h"
#include "tmutils.h"
//---------------------------------------------------------------------------
//---- values for _pThemeFile, besides valid ptrs ----
//---- if we have no windows open, we cannot track if theme is active ----
#define THEME_UNKNOWN NULL
//---- if we are unhooked, we no that no theme file is avail for us ----
#define THEME_NONE (CUxThemeFile *)(-1)
//---------------------------------------------------------------------------
CAppInfo::CAppInfo()
{
_fCustomAppTheme = FALSE;
_hwndPreview = NULL;
_pPreviewThemeFile = NULL;
_fFirstTimeHooksOn = TRUE;
_fNewThemeDiscovered = FALSE;
_pAppThemeFile = THEME_NONE; // no hooks
_iChangeNum = -1;
_dwAppFlags = (STAP_ALLOW_NONCLIENT | STAP_ALLOW_CONTROLS);
//---- compositing ON by default ----
_fCompositing = TRUE;
GetCurrentUserThemeInt(THEMEPROP_COMPOSITING, TRUE, &_fCompositing);
InitializeCriticalSection(&_csAppInfo);
}
//---------------------------------------------------------------------------
CAppInfo::~CAppInfo()
{
ClosePreviewThemeFile();
//---- ignore iRefCount here - force elements to be deleted ----
for (int i=0; i < _ThemeEntries.m_nSize; i++)
{
_ThemeEntries[i].pThemeFile->ValidateObj();
delete _ThemeEntries[i].pThemeFile;
}
DeleteCriticalSection(&_csAppInfo);
}
//---------------------------------------------------------------------------
void CAppInfo::ResetAppTheme(int iChangeNum, BOOL fMsgCheck, BOOL *pfChanged, BOOL *pfFirstMsg)
{
CAutoCS cs(&_csAppInfo);
if (pfChanged)
*pfChanged = FALSE;
//---- NOTE: "_pAppThemeFile" doesn't hold a refcount on the shared memory map file ----
//---- this is done so that, processes who close all of their windows but continue ----
//---- to run (like WinLogon), will not hold a refcount on old themes (since ----
//---- they never receive any more WM_THEMECHANGED msgs until they create ----
//---- another window. If we were to remove HOOKS between every theme change, ----
//---- we could use the OnHooksDisableld code to remove the theme file hold ----
//---- but design is to let hooks stay ON why we apply and unapply themes. ----
if ((iChangeNum == -1) || (_iChangeNum != iChangeNum) || (_fNewThemeDiscovered))
{
//---- new change number for this process ----
if (HOOKSACTIVE())
_pAppThemeFile = THEME_UNKNOWN;
else
_pAppThemeFile = THEME_NONE;
Log(LOG_TMCHANGE, L"ResetAppTheme - CHANGE: iChangeNum=0x%x, _pAppThemeFile=%d",
iChangeNum, _pAppThemeFile);
_iChangeNum = iChangeNum;
_fNewThemeDiscovered = FALSE;
//---- update caller's info ----
if (pfChanged)
*pfChanged = TRUE;
}
if (fMsgCheck)
{
*pfFirstMsg = FALSE;
if ((iChangeNum != -1) && (_iFirstMsgChangeNum != iChangeNum))
{
//---- new WM_THEMECHANGED_TRIGGER msg for this process ----
_iFirstMsgChangeNum = iChangeNum;
//---- update caller's info ----
*pfFirstMsg = TRUE;
}
}
}
//---------------------------------------------------------------------------
BOOL CAppInfo::HasThemeChanged()
{
CAutoCS cs(&_csAppInfo);
BOOL fChanged = _fNewThemeDiscovered;
_fNewThemeDiscovered = FALSE;
return fChanged;
}
//---------------------------------------------------------------------------
void CAppInfo::ClosePreviewThemeFile()
{
CAutoCS cs(&_csAppInfo);
if (_pPreviewThemeFile)
{
CloseThemeFile(_pPreviewThemeFile);
_pPreviewThemeFile = NULL;
}
_hwndPreview = NULL;
}
//---------------------------------------------------------------------------
BOOL CAppInfo::CompositingEnabled()
{
CAutoCS cs(&_csAppInfo);
return (_fCompositing);
}
//---------------------------------------------------------------------------
HRESULT CAppInfo::LoadCustomAppThemeIfFound()
{
CAutoCS cs(&_csAppInfo);
CCurrentUser hkeyCurrentUser(KEY_READ);
RESOURCE HKEY hklm = NULL;
HTHEMEFILE hThemeFile = NULL;
HRESULT hr = S_OK;
int code32;
if (! _fFirstTimeHooksOn)
goto exit;
_fFirstTimeHooksOn = FALSE;
//---- see if this app has custom theme ----
WCHAR szCustomKey[2*MAX_PATH];
wsprintf(szCustomKey, L"%s\\%s\\%s", THEMEMGR_REGKEY,
THEMEPROP_CUSTOMAPPS, g_szProcessName);
//---- open hkcu ----
code32 = RegOpenKeyEx(hkeyCurrentUser, szCustomKey, 0, KEY_READ, &hklm);
if (code32 != ERROR_SUCCESS)
goto exit;
//---- read the "DllValue" value ----
WCHAR szDllName[MAX_PATH];
hr = RegistryStrRead(hklm, THEMEPROP_DLLNAME, szDllName, ARRAYSIZE(szDllName));
if (FAILED(hr))
goto exit;
//---- read the "color" value ----
WCHAR szColorName[MAX_PATH];
hr = RegistryStrRead(hklm, THEMEPROP_COLORNAME, szColorName, ARRAYSIZE(szColorName));
if (FAILED(hr))
*szColorName = 0;
//---- read the "size" value ----
WCHAR szSizeName[MAX_PATH];
hr = RegistryStrRead(hklm, THEMEPROP_SIZENAME, szSizeName, ARRAYSIZE(szSizeName));
if (FAILED(hr))
*szSizeName = 0;
Log(LOG_TMCHANGE, L"Custom app theme found: %s, %s, %s", szDllName, szColorName, szSizeName);
hr = ::OpenThemeFile(szDllName, szColorName, szSizeName, &hThemeFile, FALSE);
if (FAILED(hr))
goto exit;
_fCustomAppTheme = TRUE;
//---- tell every window in our process that theme has changed ----
hr = ApplyTheme(hThemeFile, AT_PROCESS, NULL);
if (FAILED(hr))
goto exit;
exit:
if (FAILED(hr))
{
if (hThemeFile)
::CloseThemeFile(hThemeFile);
}
if (hklm)
RegCloseKey(hklm);
return hr;
}
//---------------------------------------------------------------------------
BOOL CAppInfo::AppIsThemed()
{
CAutoCS cs(&_csAppInfo);
return HOOKSACTIVE();
}
//---------------------------------------------------------------------------
BOOL CAppInfo::CustomAppTheme()
{
CAutoCS cs(&_csAppInfo);
return _fCustomAppTheme;
}
//---------------------------------------------------------------------------
BOOL CAppInfo::IsSystemThemeActive()
{
HANDLE handle;
BOOL fActive = FALSE;
HRESULT hr = CThemeServices::GetGlobalTheme(&handle);
if (SUCCEEDED(hr))
{
if (handle)
{
fActive = TRUE;
CloseHandle(handle);
}
}
return fActive;
}
//---------------------------------------------------------------------------
BOOL CAppInfo::WindowHasTheme(HWND hwnd)
{
//---- keep this logic in sync with "OpenWindowThemeFile()" ----
CAutoCS cs(&_csAppInfo);
BOOL fHasTheme = FALSE;
if (HOOKSACTIVE())
{
//---- check for preview window match ----
if ((ISWINDOW(hwnd)) && (ISWINDOW(_hwndPreview)))
{
if ((hwnd == _hwndPreview) || (IsChild(_hwndPreview, hwnd)))
{
if (_pPreviewThemeFile)
fHasTheme = TRUE;
}
}
//---- if not preview, just use app theme file ----
if ((! fHasTheme) && (_pAppThemeFile != THEME_NONE))
{
fHasTheme = TRUE;
}
}
return fHasTheme;
}
//---------------------------------------------------------------------------
HRESULT CAppInfo::OpenWindowThemeFile(HWND hwnd, CUxThemeFile **ppThemeFile)
{
//---- keep this logic in sync with "WindowHasTheme()" ----
HRESULT hr = S_OK;
CUxThemeFile *pThemeFile = NULL;
CAutoCS cs(&_csAppInfo);
if (hwnd)
TrackForeignWindow(hwnd);
if (HOOKSACTIVE())
{
//---- check for preview window match ----
if ((ISWINDOW(hwnd)) && (ISWINDOW(_hwndPreview)))
{
if ((hwnd == _hwndPreview) || (IsChild(_hwndPreview, hwnd)))
{
if (_pPreviewThemeFile)
{
//---- bump ref count ----
hr = BumpRefCount(_pPreviewThemeFile);
if (FAILED(hr))
goto exit;
pThemeFile = _pPreviewThemeFile;
}
}
}
//---- if not preview, just use app theme file ----
if ((! pThemeFile) && (_pAppThemeFile != THEME_NONE))
{
if (_pAppThemeFile == THEME_UNKNOWN || !_pAppThemeFile->IsReady())
{
HANDLE handle = NULL;
hr = CThemeServices::GetGlobalTheme(&handle);
if (FAILED(hr))
goto exit;
Log(LOG_TMCHANGE, L"New App Theme handle=0x%x", handle);
if (handle)
{
//---- get a shared CUxThemeFile object for the handle ----
hr = OpenThemeFile(handle, &pThemeFile);
if (FAILED(hr))
{
// Since it's the global theme, no need to clean stock objects
CloseHandle(handle);
goto exit;
}
//---- set our app theme file ----
_pAppThemeFile = pThemeFile;
//---- update our cached change number to match ----
_iChangeNum = GetLoadIdFromTheme(_pAppThemeFile);
_fNewThemeDiscovered = TRUE;
}
}
else
{
//---- bump ref count ----
hr = BumpRefCount(_pAppThemeFile);
if (FAILED(hr))
goto exit;
pThemeFile = _pAppThemeFile;
}
}
}
exit:
if (pThemeFile)
{
*ppThemeFile = pThemeFile;
}
else
{
hr = MakeError32(ERROR_NOT_FOUND);
}
return hr;
}
//---------------------------------------------------------------------------
DWORD CAppInfo::GetAppFlags()
{
CAutoCS cs(&_csAppInfo);
return _dwAppFlags;
}
//---------------------------------------------------------------------------
void CAppInfo::SetAppFlags(DWORD dwFlags)
{
CAutoCS cs(&_csAppInfo);
_dwAppFlags = dwFlags;
}
//---------------------------------------------------------------------------
void CAppInfo::SetPreviewThemeFile(HANDLE handle, HWND hwnd)
{
CAutoCS cs(&_csAppInfo);
ClosePreviewThemeFile();
//---- set new file ----
if (handle)
{
HRESULT hr = OpenThemeFile(handle, &_pPreviewThemeFile);
if (FAILED(hr))
{
// We don't own the handle, so no clean up
Log(LOG_ALWAYS, L"Failed to add theme file to list");
_pPreviewThemeFile = NULL;
}
}
_hwndPreview = hwnd;
}
//---------------------------------------------------------------------------
HWND CAppInfo::PreviewHwnd()
{
CAutoCS cs(&_csAppInfo);
return _hwndPreview;
}
//---------------------------------------------------------------------------
//---------------------------------------------------------------------------
// If we fail, dont return a theme file and let the caller clean up
//---------------------------------------------------------------------------
HRESULT CAppInfo::OpenThemeFile(HANDLE handle, CUxThemeFile **ppThemeFile)
{
CAutoCS autoCritSect(&_csAppInfo);
CUxThemeFile *pFile = NULL;
HRESULT hr = S_OK;
BOOL fGotit = FALSE;
if (! handle)
{
hr = MakeError32(ERROR_INVALID_HANDLE);
goto exit;
}
for (int i=0; i < _ThemeEntries.m_nSize; i++)
{
THEME_FILE_ENTRY *pEntry = &_ThemeEntries[i];
pEntry->pThemeFile->ValidateObj();
if (pEntry->pThemeFile->_hMemoryMap == handle)
{
pEntry->iRefCount++;
fGotit = TRUE;
*ppThemeFile = pEntry->pThemeFile;
break;
}
}
if (! fGotit)
{
pFile = new CUxThemeFile;
if (! pFile)
{
hr = MakeError32(E_OUTOFMEMORY);
goto exit;
}
hr = pFile->OpenFromHandle(handle);
if (FAILED(hr))
{
goto exit;
}
THEME_FILE_ENTRY entry = {1, pFile};
if (! _ThemeEntries.Add(entry))
{
hr = MakeError32(E_OUTOFMEMORY);
goto exit;
}
pFile->ValidateObj();
*ppThemeFile = pFile;
}
exit:
if ((FAILED(hr)) && (pFile))
{
delete pFile;
}
return hr;
}
//---------------------------------------------------------------------------
HRESULT CAppInfo::BumpRefCount(CUxThemeFile *pThemeFile)
{
HRESULT hr = S_OK;
CAutoCS autoCritSect(&_csAppInfo);
pThemeFile->ValidateObj();
BOOL fGotit = FALSE;
for (int i=0; i < _ThemeEntries.m_nSize; i++)
{
THEME_FILE_ENTRY *pEntry = &_ThemeEntries[i];
pEntry->pThemeFile->ValidateObj();
if (pEntry->pThemeFile == pThemeFile)
{
pEntry->iRefCount++;
fGotit = TRUE;
break;
}
}
if (! fGotit)
hr = MakeError32(ERROR_NOT_FOUND);
return hr;
}
//---------------------------------------------------------------------------
void CAppInfo::CloseThemeFile(CUxThemeFile *pThemeFile)
{
CAutoCS autoCritSect(&_csAppInfo);
BOOL fGotit = FALSE;
pThemeFile->ValidateObj();
for (int i=0; i < _ThemeEntries.m_nSize; i++)
{
THEME_FILE_ENTRY *pEntry = &_ThemeEntries[i];
pEntry->pThemeFile->ValidateObj();
if (pEntry->pThemeFile == pThemeFile)
{
pEntry->iRefCount--;
fGotit = TRUE;
if (! pEntry->iRefCount)
{
//---- clear app themefile? ----
if (pEntry->pThemeFile == _pAppThemeFile)
{
_pAppThemeFile = THEME_UNKNOWN;
}
delete pEntry->pThemeFile;
_ThemeEntries.RemoveAt(i);
}
break;
}
}
if (! fGotit)
Log(LOG_ERROR, L"Could not find ThemeFile in list: 0x%x", pThemeFile);
}
//---------------------------------------------------------------------------
#ifdef DEBUG
void CAppInfo::DumpFileHolders()
{
CAutoCS autoCritSect(&_csAppInfo);
if (LogOptionOn(LO_TMHANDLE))
{
int iCount = _ThemeEntries.m_nSize;
if (! iCount)
{
Log(LOG_TMHANDLE, L"---- No CUxThemeFile objects ----");
}
else
{
Log(LOG_TMHANDLE, L"---- Dump of %d CUxThemeFile objects ----", iCount);
for (int i=0; i < _ThemeEntries.m_nSize; i++)
{
THEME_FILE_ENTRY *pEntry = &_ThemeEntries[i];
pEntry->pThemeFile->ValidateObj();
if (pEntry->pThemeFile)
{
CUxThemeFile *tf = pEntry->pThemeFile;
THEMEHDR *th = (THEMEHDR *)tf->_pbThemeData;
Log(LOG_TMHANDLE, L"CUxThemeFile[%d]: refcnt=%d, memfile=%d",
i, pEntry->iRefCount, th->iLoadId);
}
}
}
}
}
#endif
//---------------------------------------------------------------------------
BOOL CAppInfo::TrackForeignWindow(HWND hwnd)
{
CAutoCS autoCritSect(&_csAppInfo);
WCHAR szDeskName[MAX_PATH] = {0};
BOOL fForeign = TRUE;
//---- get desktop name for window ----
if (GetWindowDesktopName(hwnd, szDeskName, ARRAYSIZE(szDeskName)))
{
if (AsciiStrCmpI(szDeskName, L"default")==0)
{
fForeign = FALSE;
}
}
if (fForeign)
{
BOOL fNeedToAdd = TRUE;
//---- see if we already know about this window ----
for (int i=0; i < _ForeignWindows.m_nSize; i++)
{
if (_ForeignWindows[i] == hwnd)
{
fNeedToAdd = FALSE;
break;
}
}
if (fNeedToAdd)
{
if (_ForeignWindows.Add(hwnd))
{
//Log(LOG_TMHANDLE, L"**** ADDED Foreign Window: hwnd=0x%x, desktop=%s ****", hwnd, szDeskName);
}
else
{
Log(LOG_TMHANDLE, L"Could not add foreign window=0x%x to tracking list", hwnd);
}
}
}
return fForeign;
}
//---------------------------------------------------------------------------
BOOL CAppInfo::OnWindowDestroyed(HWND hwnd)
{
CAutoCS autoCritSect(&_csAppInfo);
BOOL fFound = FALSE;
//---- remove from the foreign list, if present ----
for (int i=0; i < _ForeignWindows.m_nSize; i++)
{
if (_ForeignWindows[i] == hwnd)
{
_ForeignWindows.RemoveAt(i);
fFound = TRUE;
//Log(LOG_TMHANDLE, L"**** REMOVED Foreign Window: hwnd=0x%x", hwnd);
break;
}
}
//---- see if preview window went away ----
if ((_hwndPreview) && (hwnd == _hwndPreview))
{
ClosePreviewThemeFile();
}
return fFound;
}
//---------------------------------------------------------------------------
BOOL CAppInfo::GetForeignWindows(HWND **ppHwnds, int *piCount)
{
CAutoCS autoCritSect(&_csAppInfo);
//---- note: we don't see window creates (OpenThemeData) and ----
//---- destroys (WM_NCDESTROY) when hooks are off; therefore ----
//---- this data may be incomplete. hopefully, vtan or USER ----
//---- can give us a more reliable way to enumerate windows ----
//---- on secured desktops ----
//---- validate windows in list, from last to first ----
int i = _ForeignWindows.m_nSize;
while (--i >= 0)
{
if (! IsWindow(_ForeignWindows[i]))
{
_ForeignWindows.RemoveAt(i);
}
}
BOOL fOk = FALSE;
int iCount = _ForeignWindows.m_nSize;
if (iCount)
{
//---- allocate memory to hold window list ----
HWND *pHwnds = new HWND[iCount];
if (pHwnds)
{
//---- copy windows to caller's new list ----
for (int i=0; i < iCount; i++)
{
pHwnds[i] = _ForeignWindows[i];
}
*ppHwnds = pHwnds;
*piCount = iCount;
fOk = TRUE;
}
}
return fOk;
}
//---------------------------------------------------------------------------