/*++

 Copyright (c) 2000 Microsoft Corporation

 Module Name:

    IgnoreLoadLibrary.cpp

 Abstract:

    This shim allows the user to specified a list of libraries it tries to ignore and 
    optionally the return values of the LoadLibrary call. Some apps try to load libraries
    they don't use but expect the LoadLibrary call to succeed.

    Use ; as the delimeter of the item and optionally use : to specify the return value.
    If you don't specify a return value we'll make the return value NULL.
    Eg:

    video_3dfx.dll;video_3dfx
    helper32.dll:1234;helper.dll

 Notes:
    
    This is a general purpose shim.

 History:

    04/13/2000 a-jamd   Created
    10/11/2000 maonis   Added support for specifying return values and renamed it from 
                        FailLoadLibrary to IgnoreLoadLibrary.
    11/16/2000 linstev  Added SetErrorMode emulation

--*/

#include "precomp.h"

IMPLEMENT_SHIM_BEGIN(IgnoreLoadLibrary)
#include "ShimHookMacro.h"

// Globals are zero initialized by default. see c++ spec 3.6.2.
CString *   g_csIgnoreLib;
int         g_csIgnoreLibCount;
DWORD *     g_rgReturnValues;

APIHOOK_ENUM_BEGIN
    APIHOOK_ENUM_ENTRY(LoadLibraryA) 
    APIHOOK_ENUM_ENTRY(LoadLibraryExA) 
    APIHOOK_ENUM_ENTRY(LoadLibraryW) 
    APIHOOK_ENUM_ENTRY(LoadLibraryExW) 
APIHOOK_ENUM_END


/*++

 This function parses the COMMAND_LINE for the libraries you wish to ignore.

--*/

BOOL ParseCommandLine(LPCSTR lpszCommandLine)
{
    CSTRING_TRY
    {
        DPF(g_szModuleName, eDbgLevelInfo, "[ParseCommandLine] CommandLine(%s)\n", lpszCommandLine);

        CString csCl(lpszCommandLine);
        CStringParser csParser(csCl, L" ;");
    
        g_csIgnoreLibCount  = csParser.GetCount();
        g_csIgnoreLib       = csParser.ReleaseArgv();
        g_rgReturnValues    = (DWORD *)malloc(sizeof(*g_rgReturnValues) * g_csIgnoreLibCount);
    
        if (g_csIgnoreLibCount && !g_rgReturnValues)
        {
            return FALSE;
        }
    
        // Iterate over all strings looking for a return value
        for (int i = 0; i < g_csIgnoreLibCount; ++i)
        {
            CStringToken csIgnore(g_csIgnoreLib[i], L":");
            CString csLib;
            CString csValue;
            
            csIgnore.GetToken(csLib);
            csIgnore.GetToken(csValue);
            
            if (!csValue.IsEmpty())
            {
                WCHAR *unused;
    
                g_csIgnoreLib[i]    = csLib;   
                g_rgReturnValues[i] = wcstol(csValue, &unused, 10);
            }
            
            DPF(g_szModuleName, eDbgLevelInfo, "[ParseCommandLine] library (%S) return value(%d)\n", g_csIgnoreLib[i].Get(), g_rgReturnValues[i]);
        }

        return TRUE;
    }
    CSTRING_CATCH
    {
        // Do nothing.
    }
    return FALSE;
}


/*++

 These stub functions break into LoadLibrary and check to see if lpLibFileName equals 
 one of the specified dll's.  If so return the specified return value.  If not call LoadLibrary on it.

--*/

HINSTANCE 
APIHOOK(LoadLibraryA)(LPCSTR lpLibFileName)
{
    CSTRING_TRY
    {
        CString csFilePath(lpLibFileName);
        CString csFileName;
        csFilePath.GetLastPathComponent(csFileName);
    
        for (int i = 0; i < g_csIgnoreLibCount; i++)
        {
            if (g_csIgnoreLib[i].CompareNoCase(csFileName) == 0)
            {
                LOG(g_szModuleName,eDbgLevelError, "[LoadLibraryA] Caught attempt loading %s, return %d\n", g_csIgnoreLib[i].Get(), g_rgReturnValues[i]);
                return (HINSTANCE) g_rgReturnValues[i];
            }
        }
    }
    CSTRING_CATCH
    {
        // Do Nothing
    }

    DPF(g_szModuleName, eDbgLevelSpew, "LoadLibraryA Allow(%s)", lpLibFileName);
    
    UINT uLastMode;
    HINSTANCE hRet;
    uLastMode = SetErrorMode(SEM_NOGPFAULTERRORBOX | SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);

    hRet = ORIGINAL_API(LoadLibraryA)(lpLibFileName);
    
    SetErrorMode(uLastMode);
    return hRet;
}

HINSTANCE 
APIHOOK(LoadLibraryExA)(
    LPCSTR lpLibFileName,
    HANDLE hFile,
    DWORD dwFlags
    )
{
    CSTRING_TRY
    {
        CString csFilePath(lpLibFileName);
        CString csFileName;
        csFilePath.GetLastPathComponent(csFileName);
    
        for (int i = 0; i < g_csIgnoreLibCount; i++)
        {
            if (g_csIgnoreLib[i].CompareNoCase(csFileName) == 0)
            {
                LOG(g_szModuleName,eDbgLevelError, "[LoadLibraryExA] Caught attempt loading %s, return %d\n", g_csIgnoreLib[i].Get(), g_rgReturnValues[i]);
                return (HINSTANCE) g_rgReturnValues[i];
            }
        }
    }
    CSTRING_CATCH
    {
        // Do Nothing
    }

    DPF(g_szModuleName, eDbgLevelSpew, "LoadLibraryExA Allow(%s)", lpLibFileName);
    
    UINT uLastMode;
    HINSTANCE hRet;
    uLastMode = SetErrorMode(SEM_NOGPFAULTERRORBOX | SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);

    hRet = ORIGINAL_API(LoadLibraryExA)(lpLibFileName, hFile, dwFlags);

    SetErrorMode(uLastMode);
    return hRet;
}

HINSTANCE 
APIHOOK(LoadLibraryW)(LPCWSTR lpLibFileName)
{
    CSTRING_TRY
    {
        CString csFilePath(lpLibFileName);
        CString csFileName;
        csFilePath.GetLastPathComponent(csFileName);
    
        for (int i = 0; i < g_csIgnoreLibCount; i++)
        {
            if (g_csIgnoreLib[i].CompareNoCase(csFileName) == 0)
            {
                LOG(g_szModuleName,eDbgLevelError, "[LoadLibraryW] Caught attempt loading %s, return %d\n", g_csIgnoreLib[i].Get(), g_rgReturnValues[i]);
                return (HINSTANCE) g_rgReturnValues[i];
            }
        }
    }
    CSTRING_CATCH
    {
        // Do Nothing
    }

    DPF(g_szModuleName, eDbgLevelSpew,"LoadLibraryW Allow(%S)", lpLibFileName);
    
    UINT uLastMode;
    HINSTANCE hRet;
    uLastMode = SetErrorMode(SEM_NOGPFAULTERRORBOX | SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);

    hRet = ORIGINAL_API(LoadLibraryW)(lpLibFileName);

    SetErrorMode(uLastMode);
    return hRet;
}

HINSTANCE 
APIHOOK(LoadLibraryExW)(
    LPCWSTR lpLibFileName,
    HANDLE hFile,
    DWORD dwFlags
    )
{
    CSTRING_TRY
    {
        CString csFilePath(lpLibFileName);
        CString csFileName;
        csFilePath.GetLastPathComponent(csFileName);
    
        for (int i = 0; i < g_csIgnoreLibCount; i++)
        {
            if (g_csIgnoreLib[i].CompareNoCase(csFileName) == 0)
            {
                LOG(g_szModuleName,eDbgLevelError, "[LoadLibraryExW] Caught attempt loading %s, return %d\n", g_csIgnoreLib[i].Get(), g_rgReturnValues[i]);
                return (HINSTANCE) g_rgReturnValues[i];
            }
        }
    }
    CSTRING_CATCH
    {
        // Do Nothing
    }

    DPF(g_szModuleName, eDbgLevelSpew,"APIHook_LoadLibraryExW Allow(%S)", lpLibFileName);
    
    UINT uLastMode;
    HINSTANCE hRet;
    uLastMode = SetErrorMode(SEM_NOGPFAULTERRORBOX | SEM_FAILCRITICALERRORS | SEM_NOOPENFILEERRORBOX);

    hRet = ORIGINAL_API(LoadLibraryExW)(lpLibFileName, hFile, dwFlags);

    SetErrorMode(uLastMode);
    return hRet;
}

/*++

 Register hooked functions

--*/

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

    return TRUE;
}

HOOK_BEGIN

    CALL_NOTIFY_FUNCTION
    APIHOOK_ENTRY(KERNEL32.DLL, LoadLibraryA)
    APIHOOK_ENTRY(KERNEL32.DLL, LoadLibraryExA)
    APIHOOK_ENTRY(KERNEL32.DLL, LoadLibraryW)
    APIHOOK_ENTRY(KERNEL32.DLL, LoadLibraryExW)

HOOK_END

IMPLEMENT_SHIM_END