/*++

 Copyright (c) 2000 Microsoft Corporation

 Module Name:

    HeroSDVD.cpp

 Abstract:
    Background: clicking the nonclient close button when a movie clip is playing causes the app 
    to hang and then AV when themes are active.   The DisableThemes shim has no effect.
 
    This is related to the app's repeated calls to SetClassLong(hwnd, GCL_HICON) in order to produce 
    the effect of an animated window icon.   This generates frequent requests to redraw the window's icon, 
    which in turn generates the NCUAHDRAWCAPTION.   This is a huge perf hit to the app in any case, 
    but particularly if the SetClassLong call makes an extra round trip to user mode and back as a 
    result of the SendMessage.    
 
    When the user hits the Close button, the app's WM_SYSCOMMAND handler resets an event that is waited 
    on by the icon-transitioning thread, and then puts himself (the UI thread) to sleep.    
    Then he calls SetClassLong(..., GCL_ICON) one last time from the icon-switching thread, 
    which hangs the app because the message-pumping thread is sleeping. This does not repro when win32k 
    doesn't send the NCUAHDRAWCAPTION message; i.e., when user API hooks are not active.
 
    The DisableTheme does not work because this shim operates in user mode on a per-process basis.
    This shim has no effect on win32k, which does special processing on a session-wide basis when themes are enabled.
 
    To address this, we shim this app to nop on SetClassLong(..., GCL_HICON), which means the app at 
    best loses animation of the icon and at worst display a bogus icon when themes are active, 


 Notes:
    
    This is an app specific shim.

 History:

    05/11/2001 scotthan  Created

--*/

#include "precomp.h"

IMPLEMENT_SHIM_BEGIN(HeroSDVD)
#include "ShimHookMacro.h"

APIHOOK_ENUM_BEGIN
    APIHOOK_ENUM_ENTRY(SetClassLongA) 
APIHOOK_ENUM_END

typedef BOOL (STDAPICALLTYPE * PFNTHEMEACTIVE)(void);
PFNTHEMEACTIVE g_pfnThemeActive;
HINSTANCE      g_hinstUxtheme;

HINSTANCE LoadUxTheme()
{
    if( NULL == g_hinstUxtheme )
    {
        HINSTANCE hinst = LoadLibrary(TEXT("UxTheme.dll"));
        if( NULL != hinst )
        {
            if( InterlockedCompareExchangePointer( (PVOID*)&g_hinstUxtheme, hinst, NULL ) )
            {
                FreeLibrary(hinst); // already loaded.
            }
        }
    }
    return g_hinstUxtheme;
}

DWORD 
APIHOOK(SetClassLongA)(
    IN HWND hwnd,
    IN int nIndex,
    IN LONG dwNewLong 
    )
{
    if( GCL_HICON == nIndex )
    {
        if( NULL == g_pfnThemeActive )
        {
            HINSTANCE hinst = LoadUxTheme();
            if( hinst )
            {
                g_pfnThemeActive = (PFNTHEMEACTIVE)GetProcAddress( hinst, "IsThemeActive" );
            }
        }

        if( g_pfnThemeActive && g_pfnThemeActive() )
        {
            //  no-op the request to change icon, and return the current one.
            return GetClassLongA(hwnd, nIndex);
        }
    }

    return ORIGINAL_API(SetClassLongA)(hwnd, nIndex, dwNewLong);
}

/*++

 Register hooked functions

--*/

BOOL
NOTIFY_FUNCTION(
    DWORD fdwReason)
{
    if( DLL_PROCESS_ATTACH == fdwReason )
    {
        g_hinstUxtheme   = NULL;
        g_pfnThemeActive = NULL;
    }
    else if( DLL_PROCESS_DETACH == fdwReason )
    {
        if( g_hinstUxtheme )
        {
            FreeLibrary(g_hinstUxtheme);
            g_hinstUxtheme = NULL;
        }
        g_pfnThemeActive = NULL;
    }

    return TRUE;
}

HOOK_BEGIN
    CALL_NOTIFY_FUNCTION
    APIHOOK_ENTRY(USER32.DLL, SetClassLongA )
HOOK_END

IMPLEMENT_SHIM_END