|
|
//---------------------------------------------------------------------------//
// sethook.cpp - Window and DefWindowProc hooking impl.
//---------------------------------------------------------------------------//
#include "stdafx.h"
#include "sethook.h"
#include "handlers.h"
#include "scroll.h"
#include "nctheme.h"
#include "scroll.h"
#include <uxthemep.h>
#include "info.h"
#include "services.h"
#include "appinfo.h"
#include "tmreg.h"
#include "globals.h"
#include "renderlist.h"
//---------------------------------------------------//
// statics
//---------------------------------------------------//
static int _fShouldEnableApiHooks = -1; // unitialized value
static BOOL _fUnhooking = FALSE; static LONG _cInitUAH = 0;
static BOOL _fSysMetCall = FALSE; // anti-recursion bit on classic sysmet calls.
static CRITICAL_SECTION _csSysMetCall = {0}; // serialize ClassicXXX calls when hooks inactive.
extern CRITICAL_SECTION _csThemeMet; // protects access to _nctmCurrent in nctheme.cpp
extern CRITICAL_SECTION _csNcSysMet; // protects access to _ncmCurrent in nctheme.cpp
extern CRITICAL_SECTION _csNcPaint; // protects thread-in-NCPAINT collection
typedef enum { PRE, DEF, POST } ePROCTYPE;
inline void ENTER_CLASSICSYSMETCALL() { if (IsAppThemed()) { EnterCriticalSection(&_csSysMetCall); _fSysMetCall = TRUE; } } inline void LEAVE_CLASSICSYSMETCALL() { if (_fSysMetCall) { _fSysMetCall = FALSE; LeaveCriticalSection(&_csSysMetCall); } } inline BOOL IN_CLASSICSYSMETCALL() { return _fSysMetCall; }
//---------------------------------------------------------------------------//
typedef struct { HINSTANCE hInst; // DLL hook instance
USERAPIHOOK uahReal; } UXTHEMEHOOKS, *PUXTHEMEHOOKS; //--------------------------------------------------------------------//
//---- Hook Instance static (unprotected - thread unsafe) ----
static UXTHEMEHOOKS _hookinf = {0}; // one-and-only instance.
//---------------------------------------------------------------------------//
// Module name for LoadLibrary
#define DLLNAME TEXT(".\\UxTheme.dll")
//---------------------------------------------------------------------------//
// UserApiHook callback functions
extern "C" { BOOL WINAPI ThemeInitApiHook( DWORD dwCmd, void * pvData ); LRESULT WINAPI ThemeDefWindowProcA( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); LRESULT CALLBACK ThemeDefWindowProcW( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ); int CALLBACK ThemeSetScrollInfoProc( HWND hwnd, int nBar, IN LPCSCROLLINFO psi, BOOL fRedraw ); BOOL CALLBACK ThemeGetScrollInfoProc( HWND hwnd, int nBar, IN OUT LPSCROLLINFO psi ); BOOL CALLBACK ThemeEnableScrollInfoProc( HWND hwnd, UINT nSBFlags, UINT nArrows ); BOOL CALLBACK ThemeAdjustWindowRectEx( LPRECT lprc, DWORD dwStyle, BOOL bMenu, DWORD dwExStyle); BOOL CALLBACK ThemeSetWindowRgn( HWND hwnd, HRGN hrgn, BOOL fRedraw); int CALLBACK ThemeGetSystemMetrics( int iMetric ); BOOL CALLBACK ThemeSystemParametersInfoA( UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni); BOOL CALLBACK ThemeSystemParametersInfoW( UINT uiAction, UINT uiParam, PVOID pvParam, UINT fWinIni); BOOL CALLBACK ThemeDrawFrameControl( IN HDC hdc, IN OUT LPRECT, IN UINT, IN UINT ); BOOL CALLBACK ThemeDrawCaption( IN HWND, IN HDC, IN CONST RECT *, IN UINT); VOID CALLBACK ThemeMDIRedrawFrame( IN HWND hwndChild, BOOL fAdd ); }
//---------------------------------------------------------------------------//
void OnHooksEnabled(); // forward
void OnHooksDisabled(BOOL fShutDown); // forward
BOOL NewThemeCheck(int iChangeNum, BOOL fMsgCheck); // forward
//---------------------------------------------------------------------------//
BOOL WINAPI ThemeHookStartup() { _hookinf.uahReal.cbSize = sizeof(_hookinf.uahReal);
HandlerTableInit();
InitializeCriticalSection( &_csSysMetCall ); InitializeCriticalSection( &_csThemeMet ); InitializeCriticalSection( &_csNcSysMet ); InitializeCriticalSection( &_csNcPaint );
InitNcThemeMetrics(NULL);
Log(LOG_TMCHANGE, L"UxTheme - ThemeHookStartup");
WindowDump(L"Startup");
return TRUE; }
//---------------------------------------------------------------------------//
BOOL WINAPI ThemeHookShutdown() { _fUnhooking = TRUE; if (HOOKSACTIVE()) // we are hooking USER msgs
{ //---- tell user that we gotta go ----
_hookinf.uahReal.pfnForceResetUserApiHook(g_hInst); InterlockedExchange( (LONG*)&g_eThemeHookState, HS_UNINITIALIZED ); OnHooksDisabled(TRUE); }
DeleteCriticalSection( &_csSysMetCall ); DeleteCriticalSection( &_csThemeMet ); DeleteCriticalSection( &_csNcSysMet ); DeleteCriticalSection( &_csNcPaint );
ClearNcThemeMetrics(); NcClearNonclientMetrics();
#ifdef DEBUG
CThemeWnd::SpewLeaks(); #endif DEBUG
return TRUE; }
//---------------------------------------------------------------------------//
// Loads a DLL instance and retrieves addresses of key hook exports.
BOOL LoadHookInstance() { if( _hookinf.hInst != NULL ) { #ifdef DEBUG
Log(LOG_ALWAYS, L"%s hook instance already protected; refcount mismatch. No-op'ing self-load\n", DLLNAME); #endif DEBUG
return TRUE; }
//-- Load a DLL instance
_hookinf.hInst = LoadLibrary(DLLNAME); if( !_hookinf.hInst ) { Log(LOG_ALWAYS, L"Cannot find dll: %s\r\r\n", DLLNAME); return FALSE; }
return TRUE; }
//---------------------------------------------------------------------------
inline BOOL IsTargetProcess(HWND hwnd = NULL) { //---- if not initialize, leave everything alone ----
if (! g_fUxthemeInitialized) return FALSE;
//---- ensure this window is in our process ----
return (HOOKSACTIVE() && (hwnd ? IsWindowProcess(hwnd, g_dwProcessId) : TRUE)); }
//---------------------------------------------------------------------------
inline void SpewHookExceptionInfo( LPCSTR pszMsg, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { #ifdef _ENABLE_HOOK_EXCEPTION_HANDLING_
Log(LOG_ERROR, L"*** Theme Hook Exception Handler ***" ); Log(LOG_ERROR, L"--- %s hwnd: %08lX, uMsg: %04lX, wParam: %08lX, lParam: %08lX.", pszMsg, hwnd, uMsg, wParam, lParam ); #endif _ENABLE_HOOK_EXCEPTION_HANDLING_
}
//---------------------------------------------------------------------------
// Helper: initializes THEME_MSG structure in prep for call to msg handler
inline void _InitThemeMsg( PTHEME_MSG ptm, MSGTYPE msgtype, BOOL bUnicode, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT lRet = 0, WNDPROC pfnDefProc = NULL ) { #ifdef DEBUG
if( MSGTYPE_DEFWNDPROC == msgtype ) { ASSERT( pfnDefProc != NULL ); // DWP, handlers require default processing
} else { ASSERT( NULL == pfnDefProc ); // no default processing for pre/post OWP, DDP callbacks
} #endif DEBUG
ptm->hwnd = hwnd; ptm->uMsg = uMsg; ptm->uCodePage = bUnicode ? CP_WINUNICODE : GetACP(); ptm->wParam = wParam; ptm->lParam = lParam; ptm->type = msgtype; ptm->lRet = lRet; ptm->pfnDefProc = pfnDefProc; ptm->fHandled = FALSE; }
//---------------------------------------------------------------------------
#ifdef UNICODE
const BOOL _fUnicode = TRUE; #else // UNICODE
const BOOL _fUnicode = FALSE; #endif // UNICODE
//---------------------------------------------------------------------------
void _PreprocessThemeChanged(HWND hwnd, WPARAM wParam, LPARAM lParam, ePROCTYPE eCallType, UINT *puDisposition) { //---- is this msg meant for this process? ----
if (IS_THEME_CHANGE_TARGET(lParam)) { BOOL fActive = ((lParam & WTC_THEMEACTIVE) != 0);
if (eCallType == PRE) // only do this on the Pre (once is enough)
{ //Log(LOG_TMCHANGE, L"hwnd=0x%x received WM_THEMECHANGED, changenum=0x%x",
// hwnd, wParam);
ClearExStyleBits(hwnd); }
//---- this part still needs to be done in both cases ----
if(! (fActive)) *puDisposition |= HMD_THEMEDETACH; else *puDisposition |= HMD_CHANGETHEME; } } //---------------------------------------------------------------------------
BOOL CALLBACK TriggerCallback(HWND hwnd, LPARAM lParam) { LPARAM *plParams = (LPARAM *)lParam; SafeSendMessage(hwnd, WM_THEMECHANGED, plParams[0], plParams[1]);
return TRUE; } //---------------------------------------------------------------------------
void _PreprocessThemeChangedTrigger(HWND hwnd, WPARAM wParam, LPARAM lParamMixed) { int iLoadId = (int(lParamMixed) >> 4); LPARAM lParamBits = (int(lParamMixed) & 0xf);
BOOL fFirstMsg = NewThemeCheck((int)wParam, TRUE); if (fFirstMsg) { //Log(LOG_TMLOAD, L"hwnd=0x%x received NEW WM_THEMECHANGED_TRIGGER, loadid=%d", hwnd,
// iLoadId);
//---- send WM_THEMECHANGED to all windows in this process ----
//---- so they let go of previous theme now ----
LPARAM lParams[2] = {wParam, lParamBits}; EnumProcessWindows(TriggerCallback, (LPARAM)&lParams);
if (iLoadId) // there was a previous theme
{ g_pRenderList->FreeRenderObjects(iLoadId); } } } //---------------------------------------------------------------------------
inline UINT _PreprocessHookedMsg( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, ePROCTYPE eCallType ) { UINT uDisposition = HMD_NIL; static bool s_fTriggerDone = false; // For some USER-owned windows, we don't get PRE, only DEF.
switch( uMsg ) { case WM_THEMECHANGED: { _PreprocessThemeChanged(hwnd, wParam, lParam, eCallType, &uDisposition); break; }
case WM_NCDESTROY: uDisposition |= HMD_WINDOWDESTROY; break;
case WM_STYLECHANGED: uDisposition |= HMD_REATTACH; break;
case WM_THEMECHANGED_TRIGGER: //---- NULL WPARAM means this is really a normal WM_UAHINIT msgs (shared msg num) ----
if (wParam) { if (eCallType == PRE // This is the normal case
|| (eCallType == DEF && !s_fTriggerDone)) // USER server-side window, we missed the PRE
{ Log(LOG_TMCHANGE, L"Recv'd: WM_THEMECHANGED_TRIGGER, Change Num=%d", wParam);
_PreprocessThemeChangedTrigger(hwnd, wParam, lParam); } if (eCallType == PRE) // Mark it done for the incoming DEF call
{ s_fTriggerDone = true; } else // After we're done, reset the flag for the next theme change
{ s_fTriggerDone = false; } } break; } return uDisposition; }
//---------------------------------------------------------------------------
// Pre-CallWndProc hook procedure
BOOL CALLBACK ThemePreWndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plRes, VOID** ppvParam ) { // Note: From this point until the point we invoke a message handler,
// We need to take care that we don't do anything (including DEBUG-only code)
// that causes a message to be sent to the window.
BOOL fHandled = FALSE;
//----------//
LogEntryMsg(L"ThemePreWndProc", hwnd, uMsg);
if( IsTargetProcess(hwnd) ) { // Retrieve window object from handle
CThemeWnd *pwnd = CThemeWnd::FromHwnd(hwnd);
// #443100 InstallShield installs a global CBT hook. Their hook handler
// generates messages prior to the window receiving WM_NCCREATE which
// causes us to exile the window prematurely because the window is temporarily
// parented by HWND_MESSAGE
BOOL fPrematureExile = (EXILED_THEMEWND(pwnd) && WM_NCCREATE == uMsg);
if ( (uMsg != WM_NCCREATE) || fPrematureExile ) { // Pre-process WM_THEMECHANGE message.
// Note: Pre-OWP does a detach only on theme removal. Post-OWP takes care
// of window death.
UINT uDisp = _PreprocessHookedMsg( hwnd, uMsg, wParam, lParam, PRE ); BOOL fLifeIsShort = TESTFLAG( uDisp, HMD_THEMEDETACH|HMD_WINDOWDESTROY ); BOOL fDetach = TESTFLAG( uDisp, HMD_THEMEDETACH );
if( _WindowHasTheme(hwnd) || fLifeIsShort ) { // On STYLECHANGED or WM_THEMECHANGE,
// try reattaching window that was previously rejected or failed, resp.
if( (REJECTED_THEMEWND(pwnd) && TESTFLAG(uDisp, HMD_REATTACH)) || (FAILED_THEMEWND(pwnd) && WM_THEMECHANGED == uMsg) || fPrematureExile ) { CThemeWnd::Detach(hwnd, FALSE); // remove rejection tag.
pwnd = NULL; } // Attach window object if applicable.
if( pwnd == THEMEWND_NIL && !(fLifeIsShort || _fUnhooking) ) { pwnd = CThemeWnd::Attach(hwnd); // NOTE: Handle -1 ThemeWnd
}
if( VALID_THEMEWND(pwnd) ) { // protect our themewnd pointer
pwnd->AddRef();
// set up a theme message block
THEME_MSG tm; _InitThemeMsg( &tm, MSGTYPE_PRE_WNDPROC, _fUnicode, hwnd, uMsg, wParam, lParam );
// is this a message we want to handle?
HOOKEDMSGHANDLER pfnPre; if( FindOwpHandler( uMsg, &pfnPre, NULL ) ) { // call the message handler
LRESULT lRetHandler = pfnPre( pwnd, &tm );
fHandled = tm.fHandled; if( fHandled ) { *plRes = lRetHandler; } }
// decrement themewnd ref
pwnd->Release(); } }
if( fDetach ) { CThemeWnd::Detach( hwnd, uDisp ); pwnd = NULL; } } }
LogExitMsg(L"ThemePreWndProc"); return fHandled; }
//---------------------------------------------------------------------------
// Post-CallWndProc hook procedure
BOOL CALLBACK ThemePostWndProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plRes, VOID** ppvParam ) { // Note: From this point until the point we invoke a message handler,
// We need to take care that we don't do anything (including DEBUG-only code)
// that causes a message to be sent to the window.
LogEntryMsg(L"ThemePostWndProc", hwnd, uMsg);
BOOL fHandled = FALSE; if( IsTargetProcess(hwnd) && WM_NCCREATE != uMsg ) { UINT uDisp = _PreprocessHookedMsg( hwnd, uMsg, wParam, lParam, POST ); BOOL fDetach = TESTFLAG(uDisp, HMD_WINDOWDESTROY); BOOL fRevoked = FALSE;
CThemeWnd* pwnd = CThemeWnd::FromHwnd(hwnd); if( _WindowHasTheme(hwnd) && VALID_THEMEWND(pwnd) ) { // protect our themewnd pointer
pwnd->AddRef(); // is this a message we want to handle?
HOOKEDMSGHANDLER pfnPost = NULL; if( FindOwpHandler( uMsg, NULL, &pfnPost ) ) { // set up a theme message block
THEME_MSG tm; _InitThemeMsg( &tm, MSGTYPE_POST_WNDPROC, _fUnicode, hwnd, uMsg, wParam, lParam, *plRes );
// call the message handler
LRESULT lRetHandler = pfnPost( pwnd, &tm );
fHandled = tm.fHandled; if( fHandled ) { *plRes = lRetHandler; } }
fRevoked = (pwnd->IsRevoked() && !pwnd->IsRevoked(RF_DEFER));
// decrement themewnd ref
pwnd->Release(); } else { // special back-end processing for non-themed windows.
fHandled = CThemeWnd::_PostWndProc( hwnd, uMsg, wParam, lParam, plRes ); }
if( fDetach ) { CThemeWnd::Detach( hwnd, uDisp ); pwnd = NULL; // don't touch
} else if( fRevoked ) { pwnd = CThemeWnd::FromHwnd(hwnd); if( VALID_THEMEWND(pwnd) ) { pwnd->Revoke(); pwnd = NULL; // don't touch
} } } LogExitMsg(L"ThemePostWndProc"); return fHandled; }
//---------------------------------------------------------------------------
BOOL CALLBACK ThemePreDefDlgProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plRes, VOID** ppvData) { LogEntryMsg(L"ThemePreDefDlgProc", hwnd, uMsg);
// Note: From this point until the point we invoke a message handler,
// We need to take care that we don't do anything (including DEBUG-only code)
// that causes a message to be sent to the window.
BOOL fHandled = FALSE; CThemeWnd* pwnd = CThemeWnd::FromHwnd(hwnd);
if( IsTargetProcess(hwnd) && g_pAppInfo->AppIsThemed() ) { if( VALID_THEMEWND(pwnd) ) { // protect our themewnd pointer
pwnd->AddRef(); // is this a message we want to handle?
HOOKEDMSGHANDLER pfnPre = NULL; if( FindDdpHandler( uMsg, &pfnPre, NULL ) ) { // set up a theme message block
THEME_MSG tm; _InitThemeMsg( &tm, MSGTYPE_PRE_DEFDLGPROC, _fUnicode, hwnd, uMsg, wParam, lParam, *plRes );
// call the message handler
LRESULT lRetHandler = pfnPre( pwnd, &tm ); fHandled = tm.fHandled; if( fHandled ) { *plRes = lRetHandler; } }
// decrement themewnd ref
pwnd->Release(); } }
LogExitMsg(L"ThemePreDefDlgProc"); return fHandled; }
//---------------------------------------------------------------------------
BOOL CALLBACK ThemePostDefDlgProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, LRESULT* plRes, VOID** ppvData) { LogEntryMsg(L"ThemePostDefDlgProc", hwnd, uMsg);
// Note: From this point until the point we invoke a message handler,
// We need to take care that we don't do anything (including DEBUG-only code)
// that causes a message to be sent to the window.
BOOL fHandled = FALSE; if( IsTargetProcess(hwnd) ) { CThemeWnd* pwnd = CThemeWnd::FromHwnd(hwnd);
if( _WindowHasTheme(hwnd) && VALID_THEMEWND(pwnd) ) { // protect our themewnd pointer
pwnd->AddRef(); // is this a message we want to handle?
HOOKEDMSGHANDLER pfnPost = NULL; if( FindDdpHandler( uMsg, NULL, &pfnPost ) ) { // set up a theme message block
THEME_MSG tm; _InitThemeMsg( &tm, MSGTYPE_POST_DEFDLGPROC, _fUnicode, hwnd, uMsg, wParam, lParam, *plRes );
// call the message handler
LRESULT lRetHandler = pfnPost( pwnd, &tm ); fHandled = tm.fHandled; if( fHandled ) { *plRes = lRetHandler; } }
// decrement themewnd ref
pwnd->Release(); } else { // special back-end processing for non-themed windows.
fHandled = CThemeWnd::_PostDlgProc( hwnd, uMsg, wParam, lParam, plRes ); } }
LogExitMsg(L"ThemePostDefDlgProc"); return fHandled; }
//---------------------------------------------------------------------------
BOOL _ShouldInitApiHook( DWORD dwCmd, void* pvData ) { if( -1 == _fShouldEnableApiHooks ) { _fShouldEnableApiHooks = TRUE;
if( IsDebuggerPresent() ) { BOOL fHookDebuggees = TRUE; HRESULT hr = GetCurrentUserThemeInt( L"ThemeDebuggees", TRUE, &fHookDebuggees );
if( SUCCEEDED(hr) && !fHookDebuggees ) { _fShouldEnableApiHooks = FALSE; } } } return _fShouldEnableApiHooks; }
//---------------------------------------------------------------------------
// ThemeInitApiHook() - USER API subclassing initialization callback.
// This is called by USER asynchronously after we call RegisterDefWindowProc().
BOOL CALLBACK ThemeInitApiHook( DWORD dwCmd, void * pvData ) { //Log(LOG_TMCHANGE, L"ThemeInitApiHook called with dwCmd=%d, ApiCallCount=%d", dwCmd, _cInitUAH);
BOOL fRetVal = FALSE;
//---- if wierd loading order has called us before DllMain(), deny hooking ----
if (! g_fUxthemeInitialized) { g_fEarlyHookRequest = TRUE; // remember that we denied at least one hook request
} else if( _ShouldInitApiHook( dwCmd, pvData ) ) { switch (dwCmd) { case UIAH_INITIALIZE: { if( !UNHOOKING() ) { int cInit = InterlockedIncrement(&_cInitUAH); if (cInit != 1) // another thread is taking (has taken) care of this
{ //Log(LOG_TMCHANGE, L"ThemeInitApiHook already called - will just exit");
InterlockedDecrement(&_cInitUAH); } else { PUSERAPIHOOK puah = (PUSERAPIHOOK)pvData; // stash 'real' defwindowproc functions
_hookinf.uahReal = *puah;
puah->pfnGetScrollInfo = ThemeGetScrollInfoProc; puah->pfnSetScrollInfo = ThemeSetScrollInfoProc; puah->pfnEnableScrollBar = ThemeEnableScrollInfoProc; puah->pfnSetWindowRgn = ThemeSetWindowRgn;
// DefWindowProc override hooks
puah->pfnDefWindowProcW = ThemeDefWindowProcW; puah->pfnDefWindowProcA = ThemeDefWindowProcA; puah->mmDWP.cb = GetDwpMsgMask( &puah->mmDWP.rgb );
// WndProc override hooks
puah->uoiWnd.pfnBeforeOWP = ThemePreWndProc; puah->uoiWnd.pfnAfterOWP = ThemePostWndProc; puah->uoiWnd.mm.cb = GetOwpMsgMask( &puah->uoiWnd.mm.rgb ); // OWP message bitmask
// DefDlgProc override hooks
puah->uoiDlg.pfnBeforeOWP = ThemePreDefDlgProc; puah->uoiDlg.pfnAfterOWP = ThemePostDefDlgProc; puah->uoiDlg.mm.cb = GetDdpMsgMask( &puah->uoiDlg.mm.rgb ); // OWP message bitmask
// System metrics hooks
puah->pfnGetSystemMetrics = ThemeGetSystemMetrics; puah->pfnSystemParametersInfoA = ThemeSystemParametersInfoA; puah->pfnSystemParametersInfoW = ThemeSystemParametersInfoW;
// Drawing hooks
puah->pfnDrawFrameControl = ThemeDrawFrameControl; puah->pfnDrawCaption = ThemeDrawCaption;
// MDI sysmenu hooks
puah->pfnMDIRedrawFrame = ThemeMDIRedrawFrame;
BOOL fNcThemed = g_pAppInfo ? TESTFLAG( g_pAppInfo->GetAppFlags(), STAP_ALLOW_NONCLIENT ) : FALSE;
if( !fNcThemed || !LoadHookInstance() || !ApiHandlerInit( g_szProcessName, puah, &_hookinf.uahReal ) ) { // restore 'Real' function table:
*puah = _hookinf.uahReal; } else { InterlockedExchange( (LONG*)&g_eThemeHookState, HS_INITIALIZED ); CThemeServices::ReestablishServerConnection(); OnHooksEnabled(); }
fRetVal = TRUE; // acknowledge out args
} } break; }
case UIAH_UNINITIALIZE: case UIAH_UNHOOK: // It is possible to be called on UIAH_INITIALIZED and UIAH_UNHOOK
// simultaneously on two separate threads.
// Here we allow only one thread to transition from INITIALIZED to UNHOOKING state, and racing threads
// will no-op. [scotthan]
if( HS_INITIALIZED == InterlockedCompareExchange( (LONG*)&g_eThemeHookState, HS_UNHOOKING, HS_INITIALIZED ) ) { //---- now that we are completely done, decrement the count ----
//Log(LOG_TMCHANGE, L"ThemeInitApiHook is now decrementing the CallCount");
int cInit; cInit = InterlockedDecrement(&_cInitUAH); ASSERT(0 == cInit);
//---- detach themed windows, revert global state, etc
OnHooksDisabled(FALSE);
// one thread transitions to UNITIALIZED state:
InterlockedExchange( (LONG*)&g_eThemeHookState, HS_UNINITIALIZED ); break; }
fRetVal = TRUE; // allow the hook/unhook
break; }
}
//Log(LOG_TMCHANGE, L"ThemeInitApiHook exiting with fRetVal=%d, ApiCallCount=%d",
// fRetVal, _cInitUAH);
return fRetVal; } //---------------------------------------------------------------------------
BOOL NewThemeCheck(int iChangeNum, BOOL fMsgCheck) { //---- return TRUE if this is the first WM_THEMECHANGEDTRIGGER msg of ----
//---- current theme change ----
Log(LOG_TMCHANGE, L"NewThemeCheck, iChangeNum=%d, fMsgCheck=%d", iChangeNum, fMsgCheck);
BOOL fFirstMsg = FALSE;
//---- update thememgr info now (don't wait for first WM_THEMECHANGED msg) ----
if (! g_pAppInfo->CustomAppTheme()) { //---- get real changenum to minimize redundant theme changes ----
if (iChangeNum == -1) { CThemeServices::GetCurrentChangeNumber(&iChangeNum); } //---- fThemeChanged is TRUE if this is the first time we have seen this ----
//---- change number or we recently found a new theme handle ----
BOOL fThemeChanged; g_pAppInfo->ResetAppTheme(iChangeNum, fMsgCheck, &fThemeChanged, &fFirstMsg);
if (fThemeChanged) { //---- see if theme services has died and been reborn ----
if( S_FALSE == CThemeServices::ReestablishServerConnection() ) { //---- services are back up - simulate a reset ----
Log(LOG_ALWAYS, L"Recovering from Themes service restart"); }
//---- refresh theme metrics cache ----
AcquireNcThemeMetrics(); } }
return fFirstMsg; } //---------------------------------------------------------------------------
void OnHooksEnabled() { WindowDump(L"OnHooksEnabled");
//---- hooking is turned on now ----
Log(LOG_TMCHANGE, L"*** LOCAL Hooks installed ***");
//---- load app's custom theme file, if one is registered ----
//---- for now, comment this out since its not needed & causes problems if advapi32.dll not yet init-ed ----
// g_pAppInfo->LoadCustomAppThemeIfFound();
//---- we may have started this process with themes already on; in this case, we ----
//---- don't get a WM_THEMECHANGED msg, so we better check for a theme now ----
NewThemeCheck(-1, FALSE); } //---------------------------------------------------------------------------
void OnHooksDisabled(BOOL fShutDown) { DWORD dwStartTime = StartTimer();
WindowDump(L"OnHooksDisabled");
//---- reset the AppTheme info to OFF ----
g_pAppInfo->ResetAppTheme(-1, FALSE, NULL, NULL);
g_pAppInfo->SetPreviewThemeFile(NULL, NULL);
//---- keep the static theme info in sync ----
AcquireNcThemeMetrics();
// NOTE: this function called from ThemeInitApiHook()( & ThemeHookShutdown()
// We need to release all nctheme state objects from windows in this process
// in two cases:
// (1) normal process shutdown.
// (2) Themes being turned off (this case). Here, we're relying on notification
// from USER that hooks are coming off this process.
if (fShutDown) CThemeWnd::DetachAll( HMD_PROCESSDETACH ); else CThemeWnd::DetachAll( HMD_THEMEDETACH );
#ifdef DEBUG
//---- all nonclient & client code should have closed their HTHEME's by now ----
g_pAppInfo->DumpFileHolders(); g_pRenderList->DumpFileHolders(); #endif
//---- force this process to remove its refcount on the global theme ----
//---- this is allowed because hTheme's are no longer directly connected ----
//---- to a CRenderObj ----
g_pRenderList->FreeRenderObjects(-1);
// free hook instance
if( _hookinf.hInst ) { FreeLibrary( _hookinf.hInst ); _hookinf.hInst = NULL; }
if (LogOptionOn(LO_TMLOAD)) { DWORD dwTicks; dwTicks = StopTimer(dwStartTime);
WCHAR buff[100]; TimeToStr(dwTicks, buff); Log(LOG_TMLOAD, L"OnHooksDisabled took: %s", buff); }
Log(LOG_TMCHANGE, L"*** LOCAL Hooks removed ***"); } //---------------------------------------------------------------------------
// _ThemeDefWindowProc() - defwindowproc worker
LRESULT CALLBACK _ThemeDefWindowProc( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam, BOOL bUnicode ) { // Note: From this point until the point we invoke a message handler,
// We need to take care that we don't do anything that causes
// a message to be sent to the window.
LRESULT lRet = 0L;
LogEntryMsg(L"_ThemeDefWindowProc", hwnd, uMsg);
BOOL fHandled = FALSE; WNDPROC pfnDefault = bUnicode ? _hookinf.uahReal.pfnDefWindowProcW : _hookinf.uahReal.pfnDefWindowProcA;
// Pre-process WM_THEMECHANGE message
if( IsTargetProcess(hwnd) ) { UINT uDisp = _PreprocessHookedMsg( hwnd, uMsg, wParam, lParam, DEF ); BOOL fLifeIsShort = TESTFLAG(uDisp, HMD_THEMEDETACH|HMD_WINDOWDESTROY); BOOL fDetach = TESTFLAG(uDisp, HMD_WINDOWDESTROY) && IsServerSideWindow(hwnd);
// Try handling message
CThemeWnd* pwnd = CThemeWnd::FromHwnd(hwnd);
// special back-end processing for non-themed windows.
fHandled = CThemeWnd::_PreDefWindowProc( hwnd, uMsg, wParam, lParam, &lRet );
if(fHandled == FALSE && (_WindowHasTheme(hwnd) || fLifeIsShort)) { // On STYLECHANGED or WM_THEMECHANGE,
// try reattaching window that was previously rejected or failed, resp.
if( (REJECTED_THEMEWND(pwnd) && TESTFLAG(uDisp, HMD_REATTACH)) || (FAILED_THEMEWND(pwnd) && WM_THEMECHANGED == uMsg) ) { CThemeWnd::Detach(hwnd, FALSE); // remove rejection tag.
pwnd = NULL; }
// Attach window object if applicable.
if( pwnd == NULL && !(fLifeIsShort || _fUnhooking) ) { pwnd = CThemeWnd::Attach(hwnd); }
if( VALID_THEMEWND(pwnd) ) { // protect our themewnd pointer:
pwnd->AddRef();
// set up a theme message block
THEME_MSG tm; _InitThemeMsg( &tm, MSGTYPE_DEFWNDPROC, bUnicode, hwnd, uMsg, wParam, lParam, 0, pfnDefault );
// is this a message we want to handle?
HOOKEDMSGHANDLER pfnHandler = NULL; if( FindDwpHandler( uMsg, &pfnHandler )) { // call the message handler
LRESULT lRetHandler = pfnHandler( pwnd, &tm ); fHandled = tm.fHandled; if( fHandled ) { lRet = lRetHandler; } }
// decrement themewnd ref
pwnd->Release(); } }
if( fDetach ) { CThemeWnd::Detach( hwnd, uDisp ); pwnd = NULL; // don't touch
}
}
if( !fHandled ) lRet = pfnDefault( hwnd, uMsg, wParam, lParam );
LogExitMsg(L"_ThemeDefWindowProc"); return lRet; }
//---------------------------------------------------------------------------
// ThemeDefWindowProcA() - Themed ansi defwindowproc
LRESULT CALLBACK ThemeDefWindowProcA( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { return _ThemeDefWindowProc( hwnd, uMsg, wParam, lParam, FALSE ); }
//---------------------------------------------------------------------------
// ThemeDefWindowProcW() - Themed widechar defwindowproc
LRESULT CALLBACK ThemeDefWindowProcW( HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam ) { return _ThemeDefWindowProc( hwnd, uMsg, wParam, lParam, TRUE ); }
//---------------------------------------------------------------------------
BOOL IsEqualScrollInfo( LPCSCROLLINFO psi1, LPCSCROLLINFO psi2 ) { if( psi1->fMask != psi2->fMask ) return FALSE;
if( psi1->fMask & SIF_RANGE ) { if( (psi1->nMin != psi2->nMin) || (psi1->nMax != psi2->nMax) ) { return FALSE; } }
if( psi1->fMask & SIF_POS ) { if( psi1->nPos != psi2->nPos ) return FALSE; }
if( psi1->fMask & SIF_PAGE ) { if( psi1->nPage != psi2->nPage ) return FALSE; }
if( psi1->fMask & SIF_TRACKPOS ) { if( psi1->nTrackPos != psi2->nTrackPos ) return FALSE; }
return TRUE;
}
//---------------------------------------------------------------------------
int CALLBACK ThemeSetScrollInfoProc( HWND hwnd, int nBar, IN LPCSCROLLINFO psi, BOOL fRedraw ) { int nRet = 0;
if ( psi != NULL ) { LogEntryMsg(L"ThemeSetScrollInfoProc", hwnd, nBar);
BOOL fHandled = FALSE; if ( IsTargetProcess(hwnd) && _WindowHasTheme(hwnd) && (nBar != SB_CTL) ) { DWORD dwStyle; BOOL fStyleChanged;
CThemeWnd* pwnd = CThemeWnd::FromHwnd(hwnd);
//
// Call the real SetScrollInfo first to give user
// a chance to update their internal state. They can
// potentially set WS_VSCROLL/WS_HSCROLL without notifying
// anyone at all (eg. defview's listview)
//
// If they do, we'll need to redraw the entire
// scroll bar.
//
dwStyle = GetWindowLong(hwnd, GWL_STYLE); nRet = _hookinf.uahReal.pfnSetScrollInfo( hwnd, nBar, psi, FALSE ); fStyleChanged = (((dwStyle ^ GetWindowLong(hwnd, GWL_STYLE)) & (WS_VSCROLL|WS_HSCROLL)) != 0) ? TRUE : FALSE;
// If we previously rejected the host window, it's possible that it
// didn't have the WS_H/VSCROLL bits. Now it will, so we can re-attach.
if ( REJECTED_THEMEWND(pwnd) ) { CThemeWnd::Detach(hwnd, FALSE); pwnd = CThemeWnd::Attach(hwnd); }
if ( VALID_THEMEWND(pwnd) ) {
// SetScrollInfo can potentially change WS_VSCROLL/WS_HSCROLL but
// no style change message gets send. User does this by directly changing
// the wnd struct. We do this by calling SetWindowLong which will generated
// stylchanging and stylechanged. For compatability, we'll need to suppress
// these messages.
pwnd->SuppressStyleMsgs(); fHandled = TRUE;
#ifdef _ENABLE_SCROLL_SPEW_
SpewScrollInfo( "ThemeSetScrollInfoProc to RealSetScrollInfo:", hwnd, psi ); #endif // _ENABLE_SCROLL_SPEW_
SCROLLINFO si; si.cbSize = sizeof(si); si.fMask = psi->fMask | SIF_DISABLENOSCROLL; if ( _hookinf.uahReal.pfnGetScrollInfo( hwnd, nBar, &si ) ) { ThemeSetScrollInfo( hwnd, nBar, &si, fRedraw ); } else { ThemeSetScrollInfo( hwnd, nBar, psi, fRedraw ); }
if ( fRedraw && fStyleChanged ) { HDC hdc = GetWindowDC(hwnd);
if ( hdc ) { DrawScrollBar(hwnd, hdc, NULL, (nBar != SB_HORZ)); ReleaseDC(hwnd, hdc); } }
pwnd->AllowStyleMsgs(); } }
if( !fHandled ) { nRet = _hookinf.uahReal.pfnSetScrollInfo( hwnd, nBar, psi, fRedraw ); }
LogExitMsg(L"ThemeSetScrollInfoProc"); }
return nRet; }
//---------------------------------------------------------------------------
BOOL CALLBACK ThemeGetScrollInfoProc( HWND hwnd, int nBar, IN OUT LPSCROLLINFO psi ) { BOOL fRet = FALSE;
if ( psi != NULL ) { LogEntryMsg(L"ThemeGetScrollInfoProc", hwnd, nBar);
if( IsTargetProcess(hwnd) && _WindowHasTheme(hwnd) ) { CThemeWnd* pwnd = CThemeWnd::FromHwnd(hwnd); if( VALID_THEMEWND(pwnd) ) { fRet = ThemeGetScrollInfo( hwnd, nBar, psi ); } }
if( !fRet ) { fRet = _hookinf.uahReal.pfnGetScrollInfo( hwnd, nBar, psi ); }
LogExitMsg(L"ThemeGetScrollInfoProc"); } else { SetLastError(ERROR_INVALID_PARAMETER); }
return fRet; }
//---------------------------------------------------------------------------
BOOL CALLBACK ThemeEnableScrollInfoProc( HWND hwnd, UINT nSBFlags, UINT nArrows ) { LogEntryMsg(L"ThemeEnableScrollInfoProc", 0, 0);
BOOL fRet = _hookinf.uahReal.pfnEnableScrollBar( hwnd, nSBFlags, nArrows );
if( fRet ) { if( IsTargetProcess(hwnd) && _WindowHasTheme(hwnd)) { CThemeWnd* pwnd = CThemeWnd::FromHwnd(hwnd); if( VALID_THEMEWND(pwnd) ) { ThemeEnableScrollBar( hwnd, nSBFlags, nArrows ); } } }
LogExitMsg(L"ThemeEnableScrollInfoProc");
return fRet; }
//---------------------------------------------------------------------------
int CALLBACK ThemeGetSystemMetrics( int iMetric ) { LogEntryMsg(L"ThemeGetSystemMetrics", 0, 0);
int iRet;
if( IsTargetProcess() && g_pAppInfo->AppIsThemed() && !IN_CLASSICSYSMETCALL() ) { BOOL fHandled = FALSE; iRet = _InternalGetSystemMetrics( iMetric, fHandled ); if( fHandled ) return iRet; } iRet = _hookinf.uahReal.pfnGetSystemMetrics(iMetric);
LogExitMsg(L"ThemeGetSystemMetrics"); return iRet; }
//---------------------------------------------------------------------------
THEMEAPI_(int) ClassicGetSystemMetrics( int iMetric ) { LogEntryMsg(L"ThemeGetSystemMetrics", 0, 0);
if( HOOKSACTIVE() && _hookinf.uahReal.pfnGetSystemMetrics != NULL ) { return _hookinf.uahReal.pfnGetSystemMetrics( iMetric ); }
ENTER_CLASSICSYSMETCALL(); int nRet = GetSystemMetrics( iMetric ); LEAVE_CLASSICSYSMETCALL();
LogExitMsg(L"ThemeGetSystemMetrics"); return nRet; }
//---------------------------------------------------------------------------
BOOL CALLBACK ThemeSystemParametersInfoA( IN UINT uiAction, IN UINT uiParam, IN OUT PVOID pvParam, IN UINT fWinIni) { LogEntryMsg(L"ThemeSystemParametersInfoA", 0, 0);
BOOL fRet = FALSE;
if( IsTargetProcess() && g_pAppInfo->AppIsThemed() && !IN_CLASSICSYSMETCALL() ) { BOOL fHandled = FALSE;
fRet = _InternalSystemParametersInfo( uiAction, uiParam, pvParam, fWinIni, FALSE, fHandled ); if( fHandled ) return fRet; } fRet = _hookinf.uahReal.pfnSystemParametersInfoA( uiAction, uiParam, pvParam, fWinIni );
LogExitMsg(L"ThemeSystemParametersInfoA");
return fRet; }
//---------------------------------------------------------------------------
BOOL CALLBACK ThemeSystemParametersInfoW( IN UINT uiAction, IN UINT uiParam, IN OUT PVOID pvParam, IN UINT fWinIni) { LogEntryMsg(L"ThemeSystemParametersInfoA", 0, 0);
BOOL fRet = FALSE;
if( IsTargetProcess() && g_pAppInfo->AppIsThemed() && !IN_CLASSICSYSMETCALL() ) { BOOL fHandled = FALSE;
fRet = _InternalSystemParametersInfo( uiAction, uiParam, pvParam, fWinIni, TRUE, fHandled ); if( fHandled ) return fRet; } fRet = _hookinf.uahReal.pfnSystemParametersInfoW( uiAction, uiParam, pvParam, fWinIni );
LogExitMsg(L"ThemeSystemParametersInfoA");
return fRet; }
//---------------------------------------------------------------------------
THEMEAPI_(BOOL) ClassicSystemParametersInfoA( IN UINT uiAction, IN UINT uiParam, IN OUT PVOID pvParam, IN UINT fWinIni) { if( HOOKSACTIVE() && _hookinf.uahReal.pfnSystemParametersInfoA ) { return _hookinf.uahReal.pfnSystemParametersInfoA( uiAction, uiParam, pvParam, fWinIni ); } ENTER_CLASSICSYSMETCALL(); BOOL fRet = SystemParametersInfoA( uiAction, uiParam, pvParam, fWinIni ); LEAVE_CLASSICSYSMETCALL(); return fRet; }
//---------------------------------------------------------------------------
THEMEAPI_(BOOL) ClassicSystemParametersInfoW( IN UINT uiAction, IN UINT uiParam, IN OUT PVOID pvParam, IN UINT fWinIni) { if( HOOKSACTIVE() && _hookinf.uahReal.pfnSystemParametersInfoW ) { return _hookinf.uahReal.pfnSystemParametersInfoW( uiAction, uiParam, pvParam, fWinIni ); } ENTER_CLASSICSYSMETCALL(); BOOL fRet = SystemParametersInfoW( uiAction, uiParam, pvParam, fWinIni ); LEAVE_CLASSICSYSMETCALL(); return fRet; }
//---------------------------------------------------------------------------
THEMEAPI_(BOOL) ClassicAdjustWindowRectEx( LPRECT prcWnd, DWORD dwStyle, BOOL fMenu, DWORD dwExStyle ) { // If hooks are active, simply call user32!RealAdjustWindowRectEx.
if( HOOKSACTIVE() && _hookinf.uahReal.pfnAdjustWindowRectEx ) { return _hookinf.uahReal.pfnAdjustWindowRectEx( prcWnd, dwStyle, fMenu, dwExStyle ); }
ENTER_CLASSICSYSMETCALL(); BOOL fRet = AdjustWindowRectEx( prcWnd, dwStyle, fMenu, dwExStyle ); LEAVE_CLASSICSYSMETCALL();
return fRet; }
//---------------------------------------------------------------------------
BOOL CALLBACK ThemeSetWindowRgn( HWND hwnd, HRGN hrgn, BOOL fRedraw) { LogEntryMsg(L"ThemeSetWindowRgn", hwnd, 0); BOOL fHandled = FALSE;
if( IsTargetProcess(hwnd) ) { CThemeWnd* pwnd = CThemeWnd::FromHwnd(hwnd); if( VALID_THEMEWND(pwnd) ) { if( _WindowHasTheme(hwnd) ) { if( hrgn != NULL && pwnd->IsFrameThemed() && !pwnd->AssigningFrameRgn() /* don't hook our own call */ ) { // If we're executing here, the window is being assigned a
// region externally or by the app. We'll want to revoke theming
// of this window from this point forward.
pwnd->AddRef(); // Disown our theme window region without directly removing it;
// we'll simply fall through and let the theme region get stomped.
if( pwnd->AssignedFrameRgn() ) pwnd->AssignFrameRgn( FALSE, FTF_NOMODIFYRGN );
// Exile the window.
pwnd->Revoke();
pwnd->Release(); } } } else if( NULL == hrgn && !IsWindowInDestroy(hwnd) ) { if( TESTFLAG( CThemeWnd::EvaluateWindowStyle( hwnd ), TWCF_FRAME|TWCF_TOOLFRAME ) ) { if( pwnd ) RemoveProp( hwnd, MAKEINTATOM(GetThemeAtom(THEMEATOM_NONCLIENT)) ); NCEVALUATE nce = {0}; nce.fIgnoreWndRgn = TRUE; pwnd = CThemeWnd::Attach(hwnd, &nce);
if( VALID_THEMEWND(pwnd) ) { ASSERT(pwnd->TestCF(TWCF_FRAME|TWCF_TOOLFRAME)); fHandled = TRUE; pwnd->SetFrameTheme( FTF_REDRAW, NULL ); } } } }
BOOL fRet = TRUE; if( !fHandled ) { ASSERT(_hookinf.uahReal.pfnSetWindowRgn); fRet = _hookinf.uahReal.pfnSetWindowRgn( hwnd, hrgn, fRedraw ); }
LogExitMsg(L"ThemeSetWindowRgn"); return fRet; }
//---------------------------------------------------------------------------
BOOL CALLBACK ThemeDrawFrameControl( IN HDC hdc, IN OUT LPRECT prc, IN UINT uType, IN UINT uState ) { LogEntryMsg(L"ThemeDrawFrameControl", NULL, 0);
if( IsTargetProcess() ) { CThemeWnd* pwnd = CThemeWnd::FromHdc(hdc); if( NULL == pwnd) // HDC is a memory DC
{ // Find the window in this thread that is processing WM_NCPAINT
HWND hwnd = NcPaintWindow_Find(); if( hwnd ) { pwnd = CThemeWnd::FromHwnd(hwnd); } } if( VALID_THEMEWND(pwnd) && _WindowHasTheme(*pwnd) ) { if( pwnd->IsFrameThemed() && pwnd->InNcPaint() && !pwnd->InNcThemePaint() ) { DWORD dwFlags = RF_NORMAL|RF_DEFER; if( pwnd->AssignedFrameRgn() ) { dwFlags |= RF_REGION; } pwnd->SetRevokeFlags(dwFlags); } } }
ASSERT(_hookinf.uahReal.pfnDrawFrameControl); BOOL fRet = _hookinf.uahReal.pfnDrawFrameControl( hdc, prc, uType, uState );
LogExitMsg(L"ThemeDrawFrameControl"); return fRet; }
//---------------------------------------------------------------------------
BOOL CALLBACK ThemeDrawCaption( IN HWND hwnd, IN HDC hdc, IN CONST RECT *prc, IN UINT uType) { LogEntryMsg(L"ThemeDrawFrameControl", NULL, 0);
if( IsTargetProcess() ) { CThemeWnd* pwnd = CThemeWnd::FromHwnd(hwnd); if( VALID_THEMEWND(pwnd) && _WindowHasTheme(*pwnd) ) { if( pwnd->IsFrameThemed() && pwnd->InNcPaint() && !pwnd->InNcThemePaint() ) { DWORD dwFlags = RF_NORMAL|RF_DEFER; if( pwnd->AssignedFrameRgn() ) { dwFlags |= RF_REGION; } pwnd->SetRevokeFlags(dwFlags); } } }
ASSERT(_hookinf.uahReal.pfnDrawCaption); BOOL fRet = _hookinf.uahReal.pfnDrawCaption( hwnd, hdc, prc, uType );
LogExitMsg(L"ThemeDrawFrameControl"); return fRet; }
//---------------------------------------------------------------------------
VOID CALLBACK ThemeMDIRedrawFrame( IN HWND hwndChild, BOOL fAdd ) { LogEntryMsg(L"ThemeMDIRedrawFrame", NULL, 0);
if( IsTargetProcess() ) { HWND hwndClient = GetParent(hwndChild); HWND hwndFrame = GetParent(hwndClient);
if( hwndFrame ) { CThemeWnd* pwnd = CThemeWnd::FromHwnd(hwndFrame);
if( VALID_THEMEWND(pwnd) ) { pwnd->ModifyMDIMenubar( fAdd, FALSE ); } } } ASSERT(_hookinf.uahReal.pfnMDIRedrawFrame); _hookinf.uahReal.pfnMDIRedrawFrame( hwndChild, fAdd ); LogExitMsg(L"ThemeMDIRedrawFrame"); }
|