1484 lines
48 KiB
C++
1484 lines
48 KiB
C++
|
//---------------------------------------------------------------------------//
|
||
|
// 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");
|
||
|
}
|