/*++

 Copyright (c) 2000 Microsoft Corporation

 Module Name:

    IgnoreHungAppPaint.cpp

 Abstract:

    Setup the hollow brush to prevent user from trashing peoples windows.

 Notes:

    This is a general purpose shim.

 History:
    
    12/04/2000 linstev  Created

--*/

#include "precomp.h"
#include "LegalStr.h"

IMPLEMENT_SHIM_BEGIN(IgnoreHungAppPaint)
#include "ShimHookMacro.h"

APIHOOK_ENUM_BEGIN
    APIHOOK_ENUM_ENTRY(RegisterClassA)
    APIHOOK_ENUM_ENTRY(RegisterClassW)
    APIHOOK_ENUM_ENTRY(RegisterClassExA)
    APIHOOK_ENUM_ENTRY(RegisterClassExW)
APIHOOK_ENUM_END

struct HUNGCLASS
{
    char szClass[MAX_PATH];
    HUNGCLASS *next;
};
HUNGCLASS *g_lHungList = NULL;

BOOL g_bAll = FALSE;

/*++

 Check if a class needs a hollow brush.

--*/

BOOL
IsHungClassA(LPCSTR szClass)
{
    BOOL bRet = FALSE;

    if (szClass)
    {
        HUNGCLASS *h = g_lHungList;
        while (h)
        {
            if (_stricmp(szClass, h->szClass) == 0)
            {
                LOGN(eDbgLevelWarning, "Matched hung class: forcing HOLLOW_BRUSH");

                bRet = TRUE;
                break;
            }
            h = h->next;
        }
    }

    return bRet;
}

/*++

 Check if a class needs a hollow brush.

--*/

BOOL
IsHungClassW(LPCWSTR wszClass)
{
    CHAR szClass[MAX_PATH];

    WideCharToMultiByte(
        CP_ACP, 
        0, 
        (LPWSTR) wszClass, 
        MAX_PATH, 
        (LPSTR) szClass, 
        MAX_PATH, 
        0, 
        0);

    return IsHungClassA(szClass);
}

/*++

 Hook all possible calls that can initialize or change a window's
 WindowProc (or DialogProc)

--*/

ATOM
APIHOOK(RegisterClassA)(
    WNDCLASSA *lpWndClass  
    )
{
    if (lpWndClass && (g_bAll || IsHungClassA(lpWndClass->lpszClassName)))
    {
        lpWndClass->hbrBackground = (HBRUSH) GetStockObject(HOLLOW_BRUSH);
    }

    return ORIGINAL_API(RegisterClassA)(lpWndClass);
}

ATOM
APIHOOK(RegisterClassW)(
    WNDCLASSW *lpWndClass  
    )
{
    if (lpWndClass && IsHungClassW(lpWndClass->lpszClassName))
    {
        lpWndClass->hbrBackground = (HBRUSH) GetStockObject(HOLLOW_BRUSH);
    }

    return ORIGINAL_API(RegisterClassW)(lpWndClass);
}

ATOM
APIHOOK(RegisterClassExA)(
    WNDCLASSEXA *lpWndClass  
    )
{
    if (lpWndClass && IsHungClassA(lpWndClass->lpszClassName))
    {
        lpWndClass->hbrBackground = (HBRUSH) GetStockObject(HOLLOW_BRUSH);
    }

    return ORIGINAL_API(RegisterClassExA)(lpWndClass);
}

ATOM
APIHOOK(RegisterClassExW)(
    WNDCLASSEXW *lpWndClass  
    )
{
    if (lpWndClass && IsHungClassW(lpWndClass->lpszClassName))
    {
        lpWndClass->hbrBackground = (HBRUSH) GetStockObject(HOLLOW_BRUSH);
    }

    return ORIGINAL_API(RegisterClassExW)(lpWndClass);
}

/*++

 Parse the command line for fixes:

    CLASS1; CLASS2; CLASS3 ...

--*/

VOID
ParseCommandLineA(
    LPCSTR lpCommandLine
    )
{
    // Add all the defaults if no command line is specified
    if (!lpCommandLine || (lpCommandLine[0] == '\0'))
    {
        g_bAll = TRUE;
        DPFN(eDbgLevelInfo, "All classes will use HOLLOW_BRUSH");
        return;
    }

    char seps[] = " ,\t;";
    char *token = NULL;

    // Since strtok modifies the string, we need to copy it 
    LPSTR szCommandLine = (LPSTR) malloc(strlen(lpCommandLine) + 1);
    if (!szCommandLine) goto Exit;

    strcpy(szCommandLine, lpCommandLine);

    //
    // Run the string, looking for fix names
    //
    
    token = _strtok(szCommandLine, seps);
    while (token)
    {
        HUNGCLASS *h;

        h = (HUNGCLASS *) malloc(sizeof(HUNGCLASS));
        if (h)
        {
            h->next = g_lHungList;
            g_lHungList = h;

            strncpy(h->szClass, token, MAX_PATH);
            DPFN(eDbgLevelInfo, "Adding %s", token);

        }
        else
        {
            DPFN(eDbgLevelError, "Out of memory");
        }
    
        // Get the next token
        token = _strtok(NULL, seps);
    }

Exit:
    if (szCommandLine)
    {
        free(szCommandLine);
    }
}

/*++

 Register hooked functions

--*/

BOOL
NOTIFY_FUNCTION(
    DWORD fdwReason
    )
{
    if (fdwReason == DLL_PROCESS_ATTACH) {
        ParseCommandLineA(COMMAND_LINE);
    }

    return TRUE;
}


HOOK_BEGIN

    CALL_NOTIFY_FUNCTION
    APIHOOK_ENTRY(USER32.DLL, RegisterClassA)
    APIHOOK_ENTRY(USER32.DLL, RegisterClassW);
    APIHOOK_ENTRY(USER32.DLL, RegisterClassExA);
    APIHOOK_ENTRY(USER32.DLL, RegisterClassExW);

HOOK_END


IMPLEMENT_SHIM_END