windows-nt/Source/XPSP1/NT/shell/themes/uxtheme/sethook.cpp
2020-09-26 16:20:57 +08:00

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");
}