#include "stdinc.h"
#include <limits.h>
#include "fusiontrace.h"
#include <stdio.h>
#include "fusionheap.h"
#include "imagehlp.h"
#include "debmacro.h"
#include "util.h"

#if !defined(FUSION_BREAK_ON_BAD_PARAMETERS)
#define FUSION_BREAK_ON_BAD_PARAMETERS false
#endif // !defined(FUSION_BREAK_ON_BAD_PARAMETERS);

bool g_FusionBreakOnBadParameters = FUSION_BREAK_ON_BAD_PARAMETERS;

static HANDLE s_hFile; // trace file handle

#if DBG
#define FUSIONP_SUPPRESS_ERROR_REPORT_IN_OS_SETUP(hr) /* nothing */
#else
bool FusionpSuppressErrorReportInOsSetup(HRESULT hr)
{
    //
    // Some of these are unfortunately expected (early) in guimode setup, actually
    // concurrent with early guimode setup, but not otherwise.
    //
    if (   hr != HRESULT_FROM_WIN32(ERROR_SXS_ROOT_MANIFEST_DEPENDENCY_NOT_INSTALLED)
        && hr != HRESULT_FROM_WIN32(ERROR_SXS_LEAF_MANIFEST_DEPENDENCY_NOT_INSTALLED)
        )
        return false;
    BOOL fAreWeInOSSetupMode = FALSE;
    //
    // If we can't determine this, then let the first error through.
    //
    if (!::FusionpAreWeInOSSetupMode(&fAreWeInOSSetupMode))
        return false;
    if (!fAreWeInOSSetupMode)
        return false;
    return true;
}
#define FUSIONP_SUPPRESS_ERROR_REPORT_IN_OS_SETUP(hr) if (FusionpSuppressErrorReportInOsSetup(hr)) return;
#endif

bool
__fastcall
FusionpGetActiveFrameInfo(
    FRAME_INFO &rFrameInfo
    )
{
    bool fFoundAnyData = false;

    rFrameInfo.pszFile = "";
    rFrameInfo.pszFunction = "";
    rFrameInfo.nLine = 0;

    const PTEB_ACTIVE_FRAME ptaf =
#if FUSION_WIN
        ::RtlGetFrame();
#else
        NULL;
#endif

    const PTEB_ACTIVE_FRAME_EX ptafe =
        ((ptaf != NULL) && (ptaf->Flags & TEB_ACTIVE_FRAME_FLAG_EXTENDED)) ? 
            reinterpret_cast<PTEB_ACTIVE_FRAME_EX>(ptaf) : NULL;

    if (ptaf != NULL)
    {
        if (ptaf->Context != NULL)
        {
            if (ptaf->Context->FrameName != NULL)
            {
                rFrameInfo.pszFunction = ptaf->Context->FrameName;
                fFoundAnyData = true;
            }

            if (ptaf->Context->Flags & TEB_ACTIVE_FRAME_CONTEXT_FLAG_EXTENDED)
            {
                const PCTEB_ACTIVE_FRAME_CONTEXT_EX ptafce =
                    reinterpret_cast<PCTEB_ACTIVE_FRAME_CONTEXT_EX>(ptaf->Context);

                if (ptafce->SourceLocation != NULL)
                {
                    rFrameInfo.pszFile = ptafce->SourceLocation;
                    fFoundAnyData = true;
                }
            }
        }
    }

    // If this is one of our frames, we can even downcast and get the line number...
    if ((ptafe != NULL) && (ptafe->ExtensionIdentifier == (PVOID) (' sxS')))
    {
        const CFrame *pFrame = static_cast<CFrame *>(ptafe);
        if (pFrame->m_nLine != 0)
        {
            rFrameInfo.nLine = pFrame->m_nLine;
            fFoundAnyData = true;
        }
    }

    return fFoundAnyData;
}

bool
__fastcall
FusionpPopulateFrameInfo(
    FRAME_INFO &rFrameInfo,
    PCTEB_ACTIVE_FRAME ptaf
    )
{
    bool fFoundAnyData = false;

    rFrameInfo.pszFile = "";
    rFrameInfo.pszFunction = "";
    rFrameInfo.nLine = 0;

    const PCTEB_ACTIVE_FRAME_EX ptafe =
        ((ptaf != NULL) && (ptaf->Flags & TEB_ACTIVE_FRAME_FLAG_EXTENDED)) ? 
            reinterpret_cast<PCTEB_ACTIVE_FRAME_EX>(ptaf) : NULL;

    if (ptaf != NULL)
    {
        if (ptaf->Context != NULL)
        {
            if (ptaf->Context->FrameName != NULL)
            {
                rFrameInfo.pszFunction = ptaf->Context->FrameName;
                fFoundAnyData = true;
            }

            if (ptaf->Context->Flags & TEB_ACTIVE_FRAME_CONTEXT_FLAG_EXTENDED)
            {
                const PCTEB_ACTIVE_FRAME_CONTEXT_EX ptafce =
                    reinterpret_cast<PCTEB_ACTIVE_FRAME_CONTEXT_EX>(ptaf->Context);

                if (ptafce->SourceLocation != NULL)
                {
                    rFrameInfo.pszFile = ptafce->SourceLocation;
                    fFoundAnyData = true;
                }
            }
        }
    }

    // If this is one of our frames, we can even downcast and get the line number...
    if ((ptafe != NULL) && (ptafe->ExtensionIdentifier == ((PVOID) (' sxS'))))
    {
        const CFrame *pFrame = static_cast<const CFrame *>(ptafe);
        if (pFrame->m_nLine != 0)
        {
            rFrameInfo.nLine = pFrame->m_nLine;
            fFoundAnyData = true;
        }
    }

    return fFoundAnyData;
}

bool
FusionpPopulateFrameInfo(
    FRAME_INFO &rFrameInfo,
    PCSTR pszFile,
    PCSTR pszFunction,
    INT nLine
    )
{
    bool fFoundAnyData = false;

    if (pszFile != NULL)
    {
        rFrameInfo.pszFile = pszFile;
        fFoundAnyData = true;
    }
    else
        rFrameInfo.pszFile = NULL;

    if (nLine != 0)
        fFoundAnyData = true;

    rFrameInfo.nLine = nLine;

    if (pszFunction != NULL)
    {
        rFrameInfo.pszFunction = pszFunction;
        fFoundAnyData = true;
    }
    else
        rFrameInfo.pszFunction = NULL;

    return fFoundAnyData;
}

int STDAPIVCALLTYPE _DebugTraceA(LPCSTR pszMsg, ...)
{
    int iResult;
    va_list ap;
    va_start(ap, pszMsg);
    iResult = _DebugTraceExVaA(0, TRACETYPE_INFO, NOERROR, pszMsg, ap);
    va_end(ap);
    return iResult;
}

int STDAPICALLTYPE
_DebugTraceVaA(LPCSTR pszMsg, va_list ap)
{
    return _DebugTraceExVaA(0, TRACETYPE_INFO, NOERROR, pszMsg, ap);
}

int STDAPIVCALLTYPE
_DebugTraceExA(DWORD dwFlags, TRACETYPE tt, HRESULT hr, LPCSTR pszMsg, ...)
{
    int iResult;
    va_list ap;
    va_start(ap, pszMsg);
    iResult = _DebugTraceExVaA(dwFlags, tt, hr, pszMsg, ap);
    va_end(ap);
    return iResult;
}

int STDAPICALLTYPE
_DebugTraceExVaA(DWORD dwFlags, TRACETYPE tt, HRESULT hr, LPCSTR pszMsg, va_list ap)
{
    CSxsPreserveLastError ple;
    CHAR szBuffer[512];
    CHAR szMsgBuffer[512];
    static const char szFormat_Info_NoFunc[] = "%s(%d): Message: \"%s\"\n";
    static const char szFormat_Info_Func[] = "%s(%d): Function %s. Message: \"%s\"\n";
    static const char szFormat_CallEntry[] = "%s(%d): Entered %s\n";
    static const char szFormat_CallExitVoid[] = "%s(%d): Exited %s\n";
    static const char szFormat_CallExitHRESULT[] = "%s(%d): Exited %s with HRESULT 0x%08lx\n";

    FRAME_INFO FrameInfo;

    szMsgBuffer[0] = '\0';

    if (pszMsg != NULL)
    {
        ::_snprintf(szMsgBuffer, NUMBER_OF(szMsgBuffer), pszMsg, ap);
        szMsgBuffer[NUMBER_OF(szMsgBuffer) - 1] = '\0';
    }

    ::FusionpGetActiveFrameInfo(FrameInfo);

    switch (tt)
    {
    default:
    case TRACETYPE_INFO:
        if (FrameInfo.pszFunction != NULL)
            ::_snprintf(szBuffer, NUMBER_OF(szBuffer), szFormat_Info_Func, FrameInfo.pszFile, FrameInfo.nLine, FrameInfo.pszFunction, szMsgBuffer);
        else
            ::_snprintf(szBuffer, NUMBER_OF(szBuffer), szFormat_Info_NoFunc, FrameInfo.pszFile, FrameInfo.nLine, szMsgBuffer);
        break;

    case TRACETYPE_CALL_START:
        ::_snprintf(szBuffer, NUMBER_OF(szBuffer), szFormat_CallEntry, FrameInfo.pszFile, FrameInfo.nLine, FrameInfo.pszFunction);
        break;

    case TRACETYPE_CALL_EXIT_NOHRESULT:
        ::_snprintf(szBuffer, NUMBER_OF(szBuffer), szFormat_CallExitVoid, FrameInfo.pszFile, FrameInfo.nLine, FrameInfo.pszFunction);
        break;

    case TRACETYPE_CALL_EXIT_HRESULT:
        ::_snprintf(szBuffer, NUMBER_OF(szBuffer), szFormat_CallExitHRESULT, FrameInfo.pszFile, FrameInfo.nLine, FrameInfo.pszFunction, hr);
        break;
    }

    szBuffer[NUMBER_OF(szBuffer) - 1] = '\0';

    ::OutputDebugStringA(szBuffer);

    ple.Restore();
    return 0;
}

VOID
FusionpTraceAllocFailure(
    PCSTR pszFile,
    int nLine,
    PCSTR pszFunction,
    PCSTR pszExpression
    )
{
    CSxsPreserveLastError ple;
    CHAR szBuffer[512];
    ::_snprintf(szBuffer, NUMBER_OF(szBuffer), "%s(%d): Memory allocation failed in function %s\n   Expression: %s\n", pszFile, nLine, pszFunction, pszExpression);
    szBuffer[NUMBER_OF(szBuffer) - 1] = '\0';
    ::OutputDebugStringA(szBuffer);
    ple.Restore();
}

VOID
FusionpTraceAllocFailure(
    PCSTR pszExpression
    )
{
    FRAME_INFO FrameInfo;
    ::FusionpGetActiveFrameInfo(FrameInfo);
    ::FusionpTraceAllocFailure(FrameInfo.pszFile, FrameInfo.nLine, FrameInfo.pszFunction, pszExpression);
}

VOID
FusionpTraceNull(
    PCSTR pszFile,
    int nLine,
    PCSTR pszFunction,
    PCSTR pszExpression
    )
{
    CSxsPreserveLastError ple;
    CHAR szBuffer[512];
    ::_snprintf(szBuffer, NUMBER_OF(szBuffer), "%s(%d): Expression evaluated to NULL in function %s\n   Expression: %s\n", pszFile, nLine, pszFunction, pszExpression);
    szBuffer[NUMBER_OF(szBuffer) - 1] = '\0';
    ::OutputDebugStringA(szBuffer);
    ple.Restore();
}

VOID
FusionpTraceNull(
    PCSTR pszExpression
    )
{
    FRAME_INFO FrameInfo;
    ::FusionpGetActiveFrameInfo(FrameInfo);
    ::FusionpTraceNull(FrameInfo.pszFile, FrameInfo.nLine, FrameInfo.pszFunction, pszExpression);
}

VOID
FusionpTraceZero(
    PCSTR pszFile,
    int nLine,
    PCSTR pszFunction,
    PCSTR pszExpression
    )
{
    CSxsPreserveLastError ple;
    CHAR szBuffer[512];
    ::_snprintf(szBuffer, NUMBER_OF(szBuffer), "%s(%d): Expression evaluated to zero in function %s\n   Expression: %s\n", pszFile, nLine, pszFunction, pszExpression);
    szBuffer[NUMBER_OF(szBuffer) - 1] = '\0';
    ::OutputDebugStringA(szBuffer);
    ple.Restore();
}

VOID
FusionpTraceZero(
    PCSTR pszExpression
    )
{
    FRAME_INFO FrameInfo;
    ::FusionpGetActiveFrameInfo(FrameInfo);
    ::FusionpTraceZero(FrameInfo.pszFile, FrameInfo.nLine, FrameInfo.pszFunction, pszExpression);
}

VOID
FusionpTraceParameterCheck(
    const FRAME_INFO &rFrameInfo,
    PCSTR pszExpression
    )
{
    CSxsPreserveLastError ple;
    CHAR szBuffer[512];
    ::_snprintf(
        szBuffer,
        NUMBER_OF(szBuffer),
        "%s(%d): Input parameter validation failed in function %s\n   Validation expression: %s\n",
        rFrameInfo.pszFile,
        rFrameInfo.nLine,
        rFrameInfo.pszFunction,
        pszExpression);
    szBuffer[NUMBER_OF(szBuffer) - 1] = '\0';
    ::OutputDebugStringA(szBuffer);
    if (g_FusionBreakOnBadParameters)
        FUSION_DEBUG_BREAK_IN_FREE_BUILD();
    ple.Restore();
}

VOID
FusionpTraceParameterCheck(
    PCSTR pszFile,
    PCSTR pszFunction,
    int nLine,
    PCSTR pszExpression
    )
{
    FRAME_INFO FrameInfo;
    ::FusionpPopulateFrameInfo(FrameInfo, pszFile, pszFunction, nLine);
    ::FusionpTraceParameterCheck(FrameInfo, pszExpression);
}

VOID
FusionpTraceParameterCheck(
    PCSTR pszExpression
    )
{
    FRAME_INFO FrameInfo;
    ::FusionpGetActiveFrameInfo(FrameInfo);
    ::FusionpTraceParameterCheck(FrameInfo, pszExpression);
}

VOID
FusionpTraceInvalidFlags(
    const FRAME_INFO &rFrameInfo,
    DWORD dwFlagsPassed,
    DWORD dwFlagsExpected
    )
{
    CSxsPreserveLastError ple;
    CHAR szBuffer[512];

    ::_snprintf(
        szBuffer,
        NUMBER_OF(szBuffer),
        "%s(%d): Function %s received invalid flags\n"
        "   Flags passed:  0x%08lx\n"
        "   Flags allowed: 0x%08lx\n",
        rFrameInfo.pszFile, rFrameInfo.nLine, rFrameInfo.pszFunction,
        dwFlagsPassed,
        dwFlagsExpected);

    szBuffer[NUMBER_OF(szBuffer) - 1] = '\0';

    ::OutputDebugStringA(szBuffer);

    ple.Restore();
}

VOID
FusionpTraceInvalidFlags(
    PCSTR pszFile,
    PCSTR pszFunction,
    INT nLine,
    DWORD dwFlagsPassed,
    DWORD dwFlagsExpected
    )
{
    FRAME_INFO FrameInfo;
    ::FusionpPopulateFrameInfo(FrameInfo, pszFile, pszFunction, nLine);
    ::FusionpTraceInvalidFlags(FrameInfo, dwFlagsPassed, dwFlagsExpected);
}

VOID
FusionpTraceInvalidFlags(
    DWORD dwFlagsPassed,
    DWORD dwFlagsExpected
    )
{
    FRAME_INFO FrameInfo;
    ::FusionpGetActiveFrameInfo(FrameInfo);
    ::FusionpTraceInvalidFlags(FrameInfo, dwFlagsPassed, dwFlagsExpected);
}

void
FusionpGetProcessImageFileName(
    PUNICODE_STRING ProcessImageFileName
    )
{
#if !defined(FUSION_WIN)
    ProcessImageFileName->Length = 0;
#else
    USHORT PrefixLength;
    *ProcessImageFileName = NtCurrentPeb()->ProcessParameters->ImagePathName;

    if (NT_SUCCESS(RtlFindCharInUnicodeString(
            RTL_FIND_CHAR_IN_UNICODE_STRING_START_AT_END,
            ProcessImageFileName,
            &RtlDosPathSeperatorsString,
            &PrefixLength)))
    {
        PrefixLength += sizeof(ProcessImageFileName->Buffer[0]);
        ProcessImageFileName->Length = static_cast<USHORT>(ProcessImageFileName->Length - PrefixLength);
        ProcessImageFileName->Buffer += PrefixLength / sizeof(ProcessImageFileName->Buffer[0]);
    }
#endif
}

class CFusionProcessImageFileName : public UNICODE_STRING
{
public:
    CFusionProcessImageFileName()
    {
        ::FusionpGetProcessImageFileName(this);
    }
};

void __fastcall FusionpTraceWin32LastErrorFailureExV(const CALL_SITE_INFO &rCallSiteInfo, PCSTR Format, va_list Args)
{
    CSxsPreserveLastError ple;
    CHAR Buffer[256];
    CALL_SITE_INFO CallSiteInfo = rCallSiteInfo;
    CallSiteInfo.pszApiName = Buffer;
    SIZE_T i = 1;

    Buffer[0] = '\0';
    if (rCallSiteInfo.pszApiName != NULL && rCallSiteInfo.pszApiName[0] != '\0')
    {
        ::_snprintf(Buffer, NUMBER_OF(Buffer) - i, "%s", rCallSiteInfo.pszApiName);
        Buffer[NUMBER_OF(Buffer) - 1] = '\0';
        i = 1 + ::StringLength(Buffer);
    }
    if (i < NUMBER_OF(Buffer))
    {
        ::_vsnprintf(&Buffer[i - 1], NUMBER_OF(Buffer) - i, Format, Args);
        Buffer[NUMBER_OF(Buffer) - 1] = '\0';
    }

    ::FusionpTraceWin32LastErrorFailure(CallSiteInfo);

    ple.Restore();
}

void __fastcall FusionpTraceWin32LastErrorFailureOriginationExV(const CALL_SITE_INFO &rCallSiteInfo, PCSTR Format, va_list Args)
{
    CSxsPreserveLastError ple;
    CHAR Buffer[128];
    CALL_SITE_INFO CallSiteInfo = rCallSiteInfo;
    CallSiteInfo.pszApiName = Buffer;

    Buffer[0] = '\0';
    ::_vsnprintf(Buffer, NUMBER_OF(Buffer) - 1, Format, Args);
    Buffer[NUMBER_OF(Buffer) - 1] = '\0';

    ::FusionpTraceWin32LastErrorFailureOrigination(CallSiteInfo);

    ple.Restore();
}

void __fastcall
FusionpTraceWin32LastErrorFailure(
    const CALL_SITE_INFO &rSite
    )
{
    CSxsPreserveLastError ple;
    CHAR szErrorBuffer[128];
    PCSTR pszFormatString = NULL;
    const DWORD dwThreadId = ::GetCurrentThreadId();
    const DWORD dwWin32Status = ::FusionpGetLastWin32Error();
    FUSIONP_SUPPRESS_ERROR_REPORT_IN_OS_SETUP(HRESULT_FROM_WIN32(dwWin32Status));
    CFusionProcessImageFileName ProcessImageFileName;

    szErrorBuffer[0] = '\0';

    DWORD dwTemp = ::FormatMessageA(
                            FORMAT_MESSAGE_FROM_SYSTEM |
                                FORMAT_MESSAGE_MAX_WIDTH_MASK,     // dwFlags
                            NULL,                           // lpSource - not used with system messages
                            dwWin32Status,                  // dwMessageId
                            0,                              // langid - 0 uses system default search path of languages
                            szErrorBuffer,                  // lpBuffer
                            NUMBER_OF(szErrorBuffer),       // nSize
                            NULL);                          // Arguments
    if (dwTemp == 0)
    {
        ::_snprintf(
            szErrorBuffer,
            NUMBER_OF(szErrorBuffer),
            "<Untranslatable Win32 status %d (0x%08lx)>",
            dwWin32Status, dwWin32Status);
        szErrorBuffer[NUMBER_OF(szErrorBuffer) - 1] = '\0';
    }

    if (rSite.pszApiName != NULL)
        pszFormatString = "%s(%lu): [function %s %Iubit process %wZ tid 0x%lx] Win32 Error %d (%s) %s\n";
    else
        pszFormatString = "%s(%lu): [function %s %Iubit process %wZ tid 0x%lx] Win32 Error %d (%s)\n";

    ::FusionpDbgPrintEx(FUSION_DBG_LEVEL_ERROREXITPATH, pszFormatString, rSite.pszFile, rSite.nLine,
        rSite.pszFunction, sizeof(PVOID) * CHAR_BIT, &ProcessImageFileName, dwThreadId,
        dwWin32Status, szErrorBuffer, rSite.pszApiName);

    ple.Restore();
}

void
__fastcall
FusionpTraceWin32LastErrorFailureOrigination(
    const CALL_SITE_INFO &rSite
    )
{
    CSxsPreserveLastError ple;
    CHAR szErrorBuffer[128];
    PCSTR pszFormatString = NULL;
    const DWORD dwThreadId = ::GetCurrentThreadId();
    const DWORD dwWin32Status = ::FusionpGetLastWin32Error();
    FUSIONP_SUPPRESS_ERROR_REPORT_IN_OS_SETUP(HRESULT_FROM_WIN32(dwWin32Status));
    CFusionProcessImageFileName ProcessImageFileName;

    szErrorBuffer[0] = '\0';

    DWORD dwTemp = ::FormatMessageA(
                            FORMAT_MESSAGE_FROM_SYSTEM |
                                FORMAT_MESSAGE_MAX_WIDTH_MASK,     // dwFlags
                            NULL,                           // lpSource - not used with system messages
                            dwWin32Status,                  // dwMessageId
                            0,                              // langid - 0 uses system default search path of languages
                            szErrorBuffer,                  // lpBuffer
                            NUMBER_OF(szErrorBuffer),       // nSize
                            NULL);                          // Arguments
    if (dwTemp == 0)
    {
        ::_snprintf(
            szErrorBuffer,
            NUMBER_OF(szErrorBuffer),
            "<Untranslatable Win32 status %d (0x%08lx)>",
            dwWin32Status, dwWin32Status);
        szErrorBuffer[NUMBER_OF(szErrorBuffer) - 1] = '\0';
    }

    if (rSite.pszApiName != NULL)
        pszFormatString = "%s(%lu): [function %s %Iubit process %wZ tid 0x%lx] Win32 Error %d (%s) %s\n";
    else
        pszFormatString = "%s(%lu): [function %s %Iubit process %wZ tid 0x%lx] Win32 Error %d (%s)\n";

    ::FusionpDbgPrintEx(
        FUSION_DBG_LEVEL_ERROR, // FUSION_DBG_LEVEL_ERROR vs. FUSION_DBG_LEVEL_ERROREXITPATH
                                // is the difference between "origination" or not
                                // origination always prints in DBG, the point is to get only one line
                                // or the stack trace only one
        pszFormatString,
        rSite.pszFile,
        rSite.nLine,
        rSite.pszFunction,
        sizeof(PVOID)*CHAR_BIT,
        &ProcessImageFileName,
        dwThreadId,
        dwWin32Status,
        szErrorBuffer,
        rSite.pszApiName);

    ple.Restore();
}

void
FusionpTraceWin32FailureNoFormatting(
    const FRAME_INFO &rFrameInfo,
    DWORD dwWin32Status,
    PCSTR pszMessage
    )
{
    CSxsPreserveLastError ple;
    FUSIONP_SUPPRESS_ERROR_REPORT_IN_OS_SETUP(HRESULT_FROM_WIN32(dwWin32Status));
    CHAR szErrorBuffer[128];
    PCSTR pszFormatString = NULL;
    const DWORD dwThreadId = ::GetCurrentThreadId();
    CFusionProcessImageFileName ProcessImageFileName;

    szErrorBuffer[0] = '\0';

    DWORD dwTemp = ::FormatMessageA(
                            FORMAT_MESSAGE_FROM_SYSTEM |
                                FORMAT_MESSAGE_MAX_WIDTH_MASK,     // dwFlags
                            NULL,                           // lpSource - not used with system messages
                            dwWin32Status,                  // dwMessageId
                            0,                              // langid - 0 uses system default search path of languages
                            szErrorBuffer,                  // lpBuffer
                            NUMBER_OF(szErrorBuffer),       // nSize
                            NULL);                          // Arguments
    if (dwTemp == 0)
    {
        ::_snprintf(
            szErrorBuffer,
            NUMBER_OF(szErrorBuffer),
            "<Untranslatable Win32 status %d (0x%08lx)>",
            dwWin32Status, dwWin32Status);
        szErrorBuffer[NUMBER_OF(szErrorBuffer) - 1] = '\0';
    }

    if (pszMessage != NULL)
        pszFormatString = "%s(%lu): [function %s %Iubit process %wZ tid 0x%lx] Win32 Error %d (%s) %s\n";
    else
        pszFormatString = "%s(%lu): [function %s %Iubit process %wZ tid 0x%lx] Win32 Error %d (%s)\n";

    ::FusionpDbgPrintEx(
        FUSION_DBG_LEVEL_ERROREXITPATH, pszFormatString, rFrameInfo.pszFile, rFrameInfo.nLine,
        rFrameInfo.pszFunction, sizeof(PVOID)*CHAR_BIT, &ProcessImageFileName, dwThreadId, dwWin32Status,
        szErrorBuffer, pszMessage
        );

    ple.Restore();
}

void
FusionpTraceWin32FailureOriginationNoFormatting(
    const FRAME_INFO &rFrameInfo,
    DWORD dwWin32Status,
    PCSTR pszMessage
    )
{
    CSxsPreserveLastError ple;
    FUSIONP_SUPPRESS_ERROR_REPORT_IN_OS_SETUP(HRESULT_FROM_WIN32(dwWin32Status));
    CHAR szErrorBuffer[128];
    PCSTR pszFormatString = NULL;
    const DWORD dwThreadId = ::GetCurrentThreadId();
    CFusionProcessImageFileName ProcessImageFileName;

    szErrorBuffer[0] = '\0';

    DWORD dwTemp = ::FormatMessageA(
                            FORMAT_MESSAGE_FROM_SYSTEM |
                                FORMAT_MESSAGE_MAX_WIDTH_MASK,     // dwFlags
                            NULL,                           // lpSource - not used with system messages
                            dwWin32Status,                  // dwMessageId
                            0,                              // langid - 0 uses system default search path of languages
                            szErrorBuffer,                  // lpBuffer
                            NUMBER_OF(szErrorBuffer),       // nSize
                            NULL);                          // Arguments
    if (dwTemp == 0)
    {
        ::_snprintf(
            szErrorBuffer,
            NUMBER_OF(szErrorBuffer),
            "<Untranslatable Win32 status %d (0x%08lx)>",
            dwWin32Status, dwWin32Status);
        szErrorBuffer[NUMBER_OF(szErrorBuffer) - 1] = '\0';
    }

    if (pszMessage != NULL)
        pszFormatString = "%s(%lu): [function %s %Iubit process %wZ tid 0x%lx] Win32 Error %d (%s) %s\n";
    else
        pszFormatString = "%s(%lu): [function %s %Iubit process %wZ tid 0x%lx] Win32 Error %d (%s)\n";

    ::FusionpDbgPrintEx(FUSION_DBG_LEVEL_ERROR, pszFormatString, rFrameInfo.pszFile, rFrameInfo.nLine,
        rFrameInfo.pszFunction, sizeof(PVOID) * CHAR_BIT, &ProcessImageFileName, dwThreadId,
        dwWin32Status, szErrorBuffer, pszMessage);

    ple.Restore();
}

void
FusionpTraceWin32Failure(
    DWORD dwWin32Status,
    LPCSTR pszMsg,
    ...
    )
{
    va_list ap;
    va_start(ap, pszMsg);
    ::FusionpTraceWin32FailureVa(dwWin32Status, pszMsg, ap);
    va_end(ap);
}

#if 0

void
FusionpTraceWin32Failure(
    const FRAME_INFO &rFrameInfo,
    DWORD dwWin32Status,
    LPCSTR pszMsg,
    ...
    )
{
    va_list ap;
    va_start(ap, pszMsg);
    ::FusionpTraceWin32FailureVa(rFrameInfo, dwWin32Status, pszMsg, ap);
    va_end(ap);
}

void
FusionpTraceWin32Failure(
    PCSTR pszFile,
    PCSTR pszFunction,
    INT nLine,
    DWORD dwWin32Status,
    LPCSTR pszMsg,
    ...
    )
{
    va_list ap;
    va_start(ap, pszMsg);
    ::FusionpTraceWin32FailureVa(pszFile, pszFunction, nLine, dwWin32Status, pszMsg, ap);
    va_end(ap);
}

#endif

void
FusionpTraceWin32FailureVa(
    const FRAME_INFO &rFrameInfo,
    DWORD dwWin32Status,
    LPCSTR pszMsg,
    va_list ap
    )
{
    CSxsPreserveLastError ple;

    CHAR szMessageBuffer[128];

    szMessageBuffer[0] = '\0';

    if (pszMsg != NULL)
        ::_vsnprintf(szMessageBuffer, NUMBER_OF(szMessageBuffer), pszMsg, ap);
    else
        szMessageBuffer[0] = '\0';

    szMessageBuffer[NUMBER_OF(szMessageBuffer) - 1] = '\0';

    ::FusionpTraceWin32FailureNoFormatting(rFrameInfo, dwWin32Status, szMessageBuffer);

    ple.Restore();
}

void
FusionpTraceWin32FailureVa(
    DWORD dwWin32Status,
    LPCSTR pszMsg,
    va_list ap
    )
{
    FRAME_INFO FrameInfo;
    ::FusionpGetActiveFrameInfo(FrameInfo);
    ::FusionpTraceWin32FailureVa(FrameInfo, dwWin32Status, pszMsg, ap);
}

void
FusionpTraceWin32FailureVa(
    PCSTR pszFile,
    PCSTR pszFunction,
    INT nLine,
    DWORD dwWin32Status,
    LPCSTR pszMsg,
    va_list ap
    )
{
    FRAME_INFO FrameInfo;
    ::FusionpPopulateFrameInfo(FrameInfo, pszFile, pszFunction, nLine);
    ::FusionpTraceWin32FailureVa(FrameInfo, dwWin32Status, pszMsg, ap);
}

void
FusionpTraceCallCOMSuccessfulExit(
    HRESULT hrIn,
    PCSTR pszFormat,
    ...
    )
{
    va_list ap;
    va_start(ap, pszFormat);
    ::FusionpTraceCallCOMSuccessfulExitVa(hrIn, pszFormat, ap);
    va_end(ap);
}

void
FusionpTraceCallCOMSuccessfulExitSmall(
    HRESULT hrIn
    )
{
    /*
    This is a forked version of FusionpTraceCOMSuccessfulExitVaBig that we expect to get called.
    This function uses about 256 bytes of stack.
    FusionpTraceCOMSuccessfulExitVaBug uses about 1k of stack.
    */
    FUSIONP_SUPPRESS_ERROR_REPORT_IN_OS_SETUP(hrIn);
    CHAR szErrorBuffer[256];
    const DWORD dwThreadId = ::GetCurrentThreadId();
    CFusionProcessImageFileName ProcessImageFileName;

    szErrorBuffer[0] = '\0';

    DWORD dwTemp = ::FormatMessageA(
                            FORMAT_MESSAGE_FROM_SYSTEM |
                                FORMAT_MESSAGE_MAX_WIDTH_MASK,     // dwFlags
                            NULL,                           // lpSource - not used with system messages
                            hrIn,                           // dwMessageId
                            0,                              // langid - 0 uses system default search path of languages
                            szErrorBuffer,                  // lpBuffer
                            NUMBER_OF(szErrorBuffer),       // nSize
                            NULL);                          // Arguments
    if (dwTemp == 0)
    {
        ::_snprintf(
            szErrorBuffer,
            NUMBER_OF(szErrorBuffer),
            "<Untranslatable(non-existed or too long) HRESULT: 0x%08lx>",
            hrIn);
        szErrorBuffer[NUMBER_OF(szErrorBuffer) - 1] = '\0';
    }

    PCSTR pszFormatString = "%s(%d): [function %s %Iubit process %wZ tid 0x%lx] COM Error 0x%08lx\n";

    FRAME_INFO FrameInfo;
    ::FusionpGetActiveFrameInfo(FrameInfo);

    ::FusionpDbgPrintEx(
        FUSION_DBG_LEVEL_ENTEREXIT,
        pszFormatString,
        FrameInfo.pszFile,
        FrameInfo.nLine,
        FrameInfo.pszFunction,
        sizeof(PVOID) * CHAR_BIT,
        &ProcessImageFileName,
        dwThreadId,
        hrIn,
        szErrorBuffer);
}

void
FusionpTraceCallCOMSuccessfulExitVaBig(
    HRESULT hrIn,
    LPCSTR pszMsg,
    va_list ap
    )
{
    /*
    This is a forked version of FusionpTraceCOMSuccessfulExitVaSmall that we don't expect to get called.
    FusionpTraceCOMSuccessfulExitVaSmall uses about 256 bytes of stack.
    This function uses about 1k of stack.
    */
    FUSIONP_SUPPRESS_ERROR_REPORT_IN_OS_SETUP(hrIn);
    CHAR szMsgBuffer[128];
    CHAR szErrorBuffer[128];
    CHAR szOutputBuffer[256];
    PCSTR pszFormatString = NULL;
    const DWORD dwThreadId = ::GetCurrentThreadId();
    FRAME_INFO FrameInfo;
    CFusionProcessImageFileName ProcessImageFileName;

    szMsgBuffer[0] = '\0';
    szErrorBuffer[0] = '\0';
    szOutputBuffer[0] = '\0';

    DWORD dwTemp = ::FormatMessageA(
                            FORMAT_MESSAGE_FROM_SYSTEM |
                                FORMAT_MESSAGE_MAX_WIDTH_MASK,     // dwFlags
                            NULL,                           // lpSource - not used with system messages
                            hrIn,                           // dwMessageId
                            0,                              // langid - 0 uses system default search path of languages
                            szErrorBuffer,                  // lpBuffer
                            NUMBER_OF(szErrorBuffer),       // nSize
                            NULL);                          // Arguments
    if (dwTemp == 0)
    {
        ::_snprintf(
            szErrorBuffer,
            NUMBER_OF(szErrorBuffer),
            "<Untranslatable(non-existed or too long) HRESULT: 0x%08lx>",
            hrIn);
        szErrorBuffer[NUMBER_OF(szErrorBuffer) - 1] = '\0';
    }

    szMsgBuffer[0] = '\0';

    if (pszMsg != NULL)
    {
        pszFormatString = "%s(%d): [function %s %Iubit process %wZ tid 0x%lx] COM Error 0x%08lx (%s) %s\n";
        ::_snprintf(szMsgBuffer, NUMBER_OF(szMsgBuffer), pszMsg, ap);
        szMsgBuffer[NUMBER_OF(szMsgBuffer) - 1] = '\0';
    }
    else
        pszFormatString = "%s(%d): [function %s %Iubit process %wZ tid 0x%lx] COM Error 0x%08lx (%s)\n";

    ::FusionpGetActiveFrameInfo(FrameInfo);

    ::_snprintf(szOutputBuffer, NUMBER_OF(szOutputBuffer), pszFormatString, FrameInfo.pszFile,
        FrameInfo.nLine, FrameInfo.pszFunction, sizeof(PVOID) * CHAR_BIT, &ProcessImageFileName, dwThreadId,
        hrIn, szErrorBuffer, szMsgBuffer);

    szOutputBuffer[NUMBER_OF(szOutputBuffer) - 1] = '\0';

    ::FusionpDbgPrintEx(
        FUSION_DBG_LEVEL_ENTEREXIT,
        "%s",
        szOutputBuffer);

    if ((s_hFile != NULL) && (s_hFile != INVALID_HANDLE_VALUE))
    {
        DWORD cBytesWritten = 0;
        if (!::WriteFile(s_hFile, szOutputBuffer, static_cast<DWORD>((::strlen(szOutputBuffer) + 1) * sizeof(CHAR)), &cBytesWritten, NULL))
        {
            // Avoid infinite loop if s_hFile is trashed...
            HANDLE hFileSaved = s_hFile;
            s_hFile = NULL;
            TRACE_WIN32_FAILURE_ORIGINATION(WriteFile);
            s_hFile = hFileSaved;
        }
    }
}

void
FusionpTraceCallCOMSuccessfulExitVa(
    HRESULT hrIn,
    LPCSTR pszMsg,
    va_list ap
    )
{
    /*
    This function has been split into FusionpTraceCOMSuccessfulExitVaBig and FusionpTraceCOMSuccessfulExitVaSmall, so that
    the usual case uses about 768 fewer bytes on the stack.
    */
    if ((pszMsg == NULL) &&
        ((s_hFile == NULL) ||
         (s_hFile == INVALID_HANDLE_VALUE)))
    {
        ::FusionpTraceCallCOMSuccessfulExitVaBig(hrIn, pszMsg, ap);
    }
    else
    {
        ::FusionpTraceCallCOMSuccessfulExitSmall(hrIn);
    }
}

void
FusionpTraceCOMFailure(
    HRESULT hrIn,
    LPCSTR pszMsg,
    ...
    )
{
    va_list ap;
    va_start(ap, pszMsg);
    ::FusionpTraceCOMFailureVa(hrIn, pszMsg, ap);
    va_end(ap);
}

void
FusionpTraceCOMFailureSmall(
    HRESULT hrIn
    )
{
    /*
    This is a forked version of FusionpTraceCOMFailureVaBig that we expect to get called.
    This function uses about 256 bytes of stack.
    FusionpTraceCOMFailureVaBug uses about 1k of stack.
    */
    FUSIONP_SUPPRESS_ERROR_REPORT_IN_OS_SETUP(hrIn);
    CHAR szErrorBuffer[256];
    const DWORD dwThreadId = ::GetCurrentThreadId();
    CFusionProcessImageFileName ProcessImageFileName;

    szErrorBuffer[0] = '\0';

    DWORD dwTemp = ::FormatMessageA(
                            FORMAT_MESSAGE_FROM_SYSTEM |
                                FORMAT_MESSAGE_MAX_WIDTH_MASK,     // dwFlags
                            NULL,                           // lpSource - not used with system messages
                            hrIn,                           // dwMessageId
                            0,                              // langid - 0 uses system default search path of languages
                            szErrorBuffer,                  // lpBuffer
                            NUMBER_OF(szErrorBuffer),       // nSize
                            NULL);                          // Arguments
    if (dwTemp == 0)
    {
        ::_snprintf(
            szErrorBuffer,
            NUMBER_OF(szErrorBuffer),
            "<Untranslatable(non-existed or too long) HRESULT: 0x%08lx>",
            hrIn);
        szErrorBuffer[NUMBER_OF(szErrorBuffer) - 1] = '\0';
    }

    PCSTR pszFormatString = "%s(%d): [function %s %Iubit process %wZ tid 0x%lx] COM Error 0x%08lx\n";

    FRAME_INFO FrameInfo;
    ::FusionpGetActiveFrameInfo(FrameInfo);

    ::FusionpDbgPrintEx(
        FUSION_DBG_LEVEL_ERROREXITPATH,
        pszFormatString,
        FrameInfo.pszFile,
        FrameInfo.nLine,
        FrameInfo.pszFunction,
        sizeof(PVOID) * CHAR_BIT,
        &ProcessImageFileName,
        dwThreadId,
        hrIn,
        szErrorBuffer);
}

void
FusionpTraceCOMFailureVaBig(
    HRESULT hrIn,
    LPCSTR pszMsg,
    va_list ap
    )
{
    /*
    This is a forked version of FusionpTraceCOMFailureVaSmall that we don't expect to get called.
    FusionpTraceCOMFailureVaSmall uses about 256 bytes of stack.
    This function uses about 1k of stack.
    */
    FUSIONP_SUPPRESS_ERROR_REPORT_IN_OS_SETUP(hrIn);
    CHAR szMsgBuffer[256];
    CHAR szErrorBuffer[256];
    CHAR szOutputBuffer[512];
    PCSTR pszFormatString = NULL;
    const DWORD dwThreadId = ::GetCurrentThreadId();
    FRAME_INFO FrameInfo;
    CFusionProcessImageFileName ProcessImageFileName;

    szMsgBuffer[0] = '\0';
    szErrorBuffer[0] = '\0';
    szOutputBuffer[0] = '\0';

    DWORD dwTemp = ::FormatMessageA(
                            FORMAT_MESSAGE_FROM_SYSTEM |
                                FORMAT_MESSAGE_MAX_WIDTH_MASK,     // dwFlags
                            NULL,                           // lpSource - not used with system messages
                            hrIn,                           // dwMessageId
                            0,                              // langid - 0 uses system default search path of languages
                            szErrorBuffer,                  // lpBuffer
                            NUMBER_OF(szErrorBuffer),       // nSize
                            NULL);                          // Arguments
    if (dwTemp == 0)
    {
        ::_snprintf(
            szErrorBuffer,
            NUMBER_OF(szErrorBuffer),
            "<Untranslatable(non-existed or too long) HRESULT: 0x%08lx>",
            hrIn);
        szErrorBuffer[NUMBER_OF(szErrorBuffer) - 1] = '\0';
    }

    szMsgBuffer[0] = '\0';

    if (pszMsg != NULL)
    {
        pszFormatString = "%s(%d): [function %s %Iubit process %wZ tid 0x%lx] COM Error 0x%08lx (%s) %s\n";
        ::_snprintf(szMsgBuffer, NUMBER_OF(szMsgBuffer), pszMsg, ap);
        szMsgBuffer[NUMBER_OF(szMsgBuffer) - 1] = '\0';
    }
    else
        pszFormatString = "%s(%d): [function %s %Iubit process %wZ tid 0x%lx] COM Error 0x%08lx (%s)\n";

    ::FusionpGetActiveFrameInfo(FrameInfo);

    ::_snprintf(szOutputBuffer, NUMBER_OF(szOutputBuffer), pszFormatString, FrameInfo.pszFile,
        FrameInfo.nLine, FrameInfo.pszFunction, sizeof(PVOID) * CHAR_BIT, &ProcessImageFileName,
        dwThreadId, hrIn, szErrorBuffer, szMsgBuffer);

    szOutputBuffer[NUMBER_OF(szOutputBuffer) - 1] = '\0';

    ::FusionpDbgPrintEx(
        FUSION_DBG_LEVEL_ERROREXITPATH,
        "%s",
        szOutputBuffer);

    if ((s_hFile != NULL) && (s_hFile != INVALID_HANDLE_VALUE))
    {
        DWORD cBytesWritten = 0;
        if (!::WriteFile(s_hFile, szOutputBuffer, static_cast<DWORD>((::strlen(szOutputBuffer) + 1) * sizeof(CHAR)), &cBytesWritten, NULL))
        {
            // Avoid infinite loop if s_hFile is trashed...
            HANDLE hFileSaved = s_hFile;
            s_hFile = NULL;
            TRACE_WIN32_FAILURE_ORIGINATION(WriteFile);
            s_hFile = hFileSaved;
        }
    }
}

void
FusionpTraceCOMFailureVa(
    HRESULT hrIn,
    LPCSTR pszMsg,
    va_list ap
    )
{
    /*
    This function has been split into FusionpTraceCOMFailureVaBig and FusionpTraceCOMFailureVaSmall, so that
    the usual case uses about 768 fewer bytes on the stack.
    */
    if ((pszMsg == NULL) &&
        ((s_hFile == NULL) ||
         (s_hFile == INVALID_HANDLE_VALUE)))
    {
        ::FusionpTraceCOMFailureVaBig(hrIn, pszMsg, ap);
    }
    else
    {
        ::FusionpTraceCOMFailureSmall(hrIn);
    }
}

void
FusionpTraceCOMFailureOrigination(
    HRESULT hrIn,
    LPCSTR pszMsg,
    ...
    )
{
    va_list ap;
    va_start(ap, pszMsg);
    ::FusionpTraceCOMFailureOriginationVa(hrIn, pszMsg, ap);
    va_end(ap);
}

void
FusionpTraceCOMFailureOriginationSmall(
    HRESULT hrIn
    )
{
    /*
    This is a forked version of FusionpTraceCOMFailureVaBig that we expect to get called.
    This function uses about 256 bytes of stack.
    FusionpTraceCOMFailureVaBug uses about 1k of stack.
    */
    FUSIONP_SUPPRESS_ERROR_REPORT_IN_OS_SETUP(hrIn);
    CHAR szErrorBuffer[256];
    const DWORD dwThreadId = ::GetCurrentThreadId();
    CFusionProcessImageFileName ProcessImageFileName;

    szErrorBuffer[0] = '\0';

    DWORD dwTemp = ::FormatMessageA(
                            FORMAT_MESSAGE_FROM_SYSTEM |
                                FORMAT_MESSAGE_MAX_WIDTH_MASK,     // dwFlags
                            NULL,                           // lpSource - not used with system messages
                            hrIn,                           // dwMessageId
                            0,                              // langid - 0 uses system default search path of languages
                            szErrorBuffer,                  // lpBuffer
                            NUMBER_OF(szErrorBuffer),       // nSize
                            NULL);                          // Arguments
    if (dwTemp == 0)
    {
        ::_snprintf(
            szErrorBuffer,
            NUMBER_OF(szErrorBuffer),
            "<Untranslatable(non-existed or too long) HRESULT: 0x%08lx>",
            hrIn);
        szErrorBuffer[NUMBER_OF(szErrorBuffer) - 1] = '\0';
    }

    PCSTR pszFormatString = "%s(%d): [function %s %Iubit process %wZ tid 0x%lx] COM Error 0x%08lx\n";

    FRAME_INFO FrameInfo;
    ::FusionpGetActiveFrameInfo(FrameInfo);

    ::FusionpDbgPrintEx(FUSION_DBG_LEVEL_ERROR, pszFormatString, FrameInfo.pszFile,
        FrameInfo.nLine, FrameInfo.pszFunction, sizeof(PVOID) * CHAR_BIT, &ProcessImageFileName,
        dwThreadId, hrIn, szErrorBuffer);
}

void
FusionpTraceCOMFailureOriginationVaBig(
    HRESULT hrIn,
    LPCSTR pszMsg,
    va_list ap
    )
{
    /*
    This is a forked version of FusionpTraceCOMFailureVaSmall that we don't expect to get called.
    FusionpTraceCOMFailureVaSmall uses about 256 bytes of stack.
    This function uses about 1k of stack.
    */
    FUSIONP_SUPPRESS_ERROR_REPORT_IN_OS_SETUP(hrIn);
    CHAR szMsgBuffer[256];
    CHAR szErrorBuffer[256];
    CHAR szOutputBuffer[512];
    PCSTR pszFormatString = NULL;
    const DWORD dwThreadId = ::GetCurrentThreadId();
    FRAME_INFO FrameInfo;
    CFusionProcessImageFileName ProcessImageFileName;

    szMsgBuffer[0] = '\0';
    szErrorBuffer[0] = '\0';
    szOutputBuffer[0] = '\0';

    DWORD dwTemp = ::FormatMessageA(
                            FORMAT_MESSAGE_FROM_SYSTEM |
                                FORMAT_MESSAGE_MAX_WIDTH_MASK,     // dwFlags
                            NULL,                           // lpSource - not used with system messages
                            hrIn,                           // dwMessageId
                            0,                              // langid - 0 uses system default search path of languages
                            szErrorBuffer,                  // lpBuffer
                            NUMBER_OF(szErrorBuffer),       // nSize
                            NULL);                          // Arguments
    if (dwTemp == 0)
    {
        ::_snprintf(
            szErrorBuffer,
            NUMBER_OF(szErrorBuffer),
            "<Untranslatable(non-existed or too long) HRESULT: 0x%08lx>",
            hrIn);
        szErrorBuffer[NUMBER_OF(szErrorBuffer) - 1] = '\0';
    }

    szMsgBuffer[0] = '\0';

    if (pszMsg != NULL)
    {
        pszFormatString = "%s(%d): [function %s %Iubit process %wZ tid 0x%lx] COM Error 0x%08lx (%s) %s\n";
        ::_snprintf(szMsgBuffer, NUMBER_OF(szMsgBuffer), pszMsg, ap);
        szMsgBuffer[NUMBER_OF(szMsgBuffer) - 1] = '\0';
    }
    else
        pszFormatString = "%s(%d): [function %s %Iubit process %wZ tid 0x%lx] COM Error 0x%08lx (%s)\n";

    ::FusionpGetActiveFrameInfo(FrameInfo);

    ::_snprintf(szOutputBuffer, NUMBER_OF(szOutputBuffer), pszFormatString, FrameInfo.pszFile,
        FrameInfo.nLine, FrameInfo.pszFunction, sizeof(PVOID) * CHAR_BIT, &ProcessImageFileName,
        dwThreadId, hrIn, szErrorBuffer, szMsgBuffer);

    szOutputBuffer[NUMBER_OF(szOutputBuffer) - 1] = '\0';

    ::FusionpDbgPrintEx(
        FUSION_DBG_LEVEL_ERROR,
        "%s",
        szOutputBuffer);

    if ((s_hFile != NULL) && (s_hFile != INVALID_HANDLE_VALUE))
    {
        DWORD cBytesWritten = 0;
        if (!::WriteFile(s_hFile, szOutputBuffer, static_cast<DWORD>((::strlen(szOutputBuffer) + 1) * sizeof(CHAR)), &cBytesWritten, NULL))
        {
            // Avoid infinite loop if s_hFile is trashed...
            HANDLE hFileSaved = s_hFile;
            s_hFile = NULL;
            TRACE_WIN32_FAILURE_ORIGINATION(WriteFile);
            s_hFile = hFileSaved;
        }
    }
}

void
FusionpTraceCOMFailureOriginationVa(
    HRESULT hrIn,
    LPCSTR pszMsg,
    va_list ap
    )
{
    /*
    This function has been split into FusionpTraceCOMFailureVaBig and FusionpTraceCOMFailureVaSmall, so that
    the usual case uses about 768 fewer bytes on the stack.
    */
    if ((pszMsg == NULL) &&
        ((s_hFile == NULL) ||
         (s_hFile == INVALID_HANDLE_VALUE)))
    {
        ::FusionpTraceCOMFailureOriginationVaBig(hrIn, pszMsg, ap);
    }
    else
    {
        ::FusionpTraceCOMFailureOriginationSmall(hrIn);
    }
}

struct ILogFile;

// --------------------------------------------------------------------------------
// TRACEMACROTYPE
// --------------------------------------------------------------------------------
typedef enum tagTRACEMACROTYPE {
    TRACE_INFO,
    TRACE_CALL,
    TRACE_RESULT
} TRACEMACROTYPE;

// --------------------------------------------------------------------------------
// These Traces are for c++ only
// --------------------------------------------------------------------------------
typedef DWORD SHOWTRACEMASK;
#define SHOW_TRACE_NONE     0x00000000
#define SHOW_TRACE_INFO     0x00000001
#define SHOW_TRACE_CALL     0x00000002
#define SHOW_TRACE_ALL      0xffffffff

// --------------------------------------------------------------------------------
// TRACELOGINFOINFO
// --------------------------------------------------------------------------------
typedef struct tagTRACELOGINFO {
    SHOWTRACEMASK       dwMask;
    ILogFile           *pLog;
} TRACELOGINFO, *LPTRACELOGINFO;

// function to make directdb happy
HRESULT DebugTraceEx(SHOWTRACEMASK dwMask, TRACEMACROTYPE tracetype, LPTRACELOGINFO pLog,
    HRESULT hr, LPSTR pszFile, INT nLine, LPCSTR pszMsg, LPCSTR pszFunc)
{
    return hr;
}

void
FusionpTraceCallEntry()
{
    FRAME_INFO FrameInfo;

    if (::FusionpGetActiveFrameInfo(FrameInfo))
    {
        ::FusionpDbgPrintEx(
            FUSION_DBG_LEVEL_ENTEREXIT,
            "%s(%d): Entered %s\n",
            FrameInfo.pszFile,
            FrameInfo.nLine,
            FrameInfo.pszFunction);
    }
}

void
FusionpTraceCallExit()
{
    FRAME_INFO FrameInfo;

    if (::FusionpGetActiveFrameInfo(FrameInfo))
    {
        ::FusionpDbgPrintEx(
            FUSION_DBG_LEVEL_ENTEREXIT,
            "%s(%d): Exited %s\n",
            FrameInfo.pszFile,
            FrameInfo.nLine,
            FrameInfo.pszFunction);
    }
}

void
FusionpTraceCallSuccessfulExitVa(
    PCSTR szFormat,
    va_list ap
    )
{
    FRAME_INFO FrameInfo;

    if (::FusionpGetActiveFrameInfo(FrameInfo))
    {
        CHAR Buffer[256];

        Buffer[0] = '\0';

        if (szFormat != NULL)
        {
            ::_vsnprintf(Buffer, NUMBER_OF(Buffer), szFormat, ap);
            Buffer[NUMBER_OF(Buffer) - 1] = '\0';
        }

        ::FusionpDbgPrintEx(
            FUSION_DBG_LEVEL_ENTEREXIT,
            "%s(%d): Successfully exiting %s%s%s\n",
            FrameInfo.pszFile,
            FrameInfo.nLine,
            FrameInfo.pszFunction,
            Buffer[0] == '\0' ? "" : " - ",
            Buffer);
    }
}

void
FusionpTraceCallSuccessfulExit(
    PCSTR szFormat,
    ...
    )
{
    va_list ap;
    va_start(ap, szFormat);
    ::FusionpTraceCallSuccessfulExitVa(szFormat, ap);
    va_end(ap);
}

void
FusionpTraceCallWin32UnsuccessfulExitVa(
    DWORD dwError,
    PCSTR szFormat,
    va_list ap
    )
{
    FRAME_INFO FrameInfo;

    if (::FusionpGetActiveFrameInfo(FrameInfo))
    {
        ::FusionpTraceWin32FailureVa(
            FrameInfo,
            dwError,
            szFormat,
            ap);
    }
}

void
FusionpTraceCallWin32UnsuccessfulExit(
    DWORD dwError,
    PCSTR szFormat,
    ...
    )
{
    va_list ap;
    va_start(ap, szFormat);
    ::FusionpTraceCallWin32UnsuccessfulExitVa(dwError, szFormat, ap);
    va_end(ap);
}

void
FusionpTraceCallCOMUnsuccessfulExitVa(
    HRESULT hrError,
    PCSTR szFormat,
    va_list ap
    )
{
    ::FusionpTraceCOMFailureVa(
        hrError,
        szFormat,
        ap);
}

void
FusionpTraceCallCOMUnsuccessfulExit(
    HRESULT hrError,
    PCSTR szFormat,
    ...
    )
{
    va_list ap;
    va_start(ap, szFormat);
    ::FusionpTraceCallCOMUnsuccessfulExitVa(hrError, szFormat, ap);
    va_end(ap);
}

#if FUSION_FREEZE_STACK_ENABLED

BOOL
FusionpFreezeStack(
    DWORD dwFlags,
    IN PFROZEN_STACK pFrozenStack
    )
{
    BOOL bSuccess = FALSE;
    PTRACECONTEXT pStackCursor = NULL;

    //
    // If they supplied a frozen stack structure, then start it out as having
    // zero depth.
    //
    if ( pFrozenStack ) pFrozenStack->ulDepth = 0;

    //
    // No frozen stack, or flags != 0 is an error.
    //
    if ( !pFrozenStack || ( dwFlags != 0 ) ) {
        ::SetLastError( ERROR_INVALID_PARAMETER );
        goto Exit;
    }

    //
    // If there's no stack so far, then we're done here.
    //
    if ( !ptcs || !ptcs->m_StackHead )
    {
        bSuccess = TRUE;
        goto Exit;
    }

    if ( pFrozenStack->ulMaxDepth < ptcs->m_StackDepth )
    {
        pFrozenStack->ulMaxDepth = ptcs->m_StackDepth;
        ::SetLastError( ERROR_INSUFFICIENT_BUFFER );
        goto Exit;
    }

    pStackCursor = ptcs->m_StackHead;
    while ( pStackCursor && ( pFrozenStack->ulDepth < pFrozenStack->ulMaxDepth ) )
    {
        pFrozenStack->Contexts[pFrozenStack->ulDepth] = *pStackCursor;

        //
        // This little bit verifies that the pointers we have in the stack frame
        // are really inside our process.  We need to do this so that when we go
        // and store them (actually above) for later, we are sure to clear ones
        // that aren't under our control.
        //
        // Since no processes have their base address at NULL, we can use this
        // test to ensure that the basic information has been set up.  Cases
        // in which this is not set up include running in sxstest or in
        // another module that doesn't call VirtualQueryEx to find out this
        // information.
        //
/*        if ( g_SxsDllMemoryBasicInformation.BaseAddress != NULL )
        {
            PBYTE pvBase = (PBYTE)g_SxsDllMemoryBasicInformation.AllocationBase;
            PBYTE pvLimit = pvBase + g_SxsDllMemoryBasicInformation.RegionSize;

#define PTR_IN_RANGE_CHECK( member, parent, target, base, limit ) \
    { PVOID pv = (PVOID)((parent).##member); if ( ( pv < base ) || ( pv > limit ) ) (target).##member = NULL; }
            PTR_IN_RANGE_CHECK( m_szComponentName, *pStackCursor, pFrozenStack->Contexts[pFrozenStack->ulDepth], pvBase, pvLimit )
            PTR_IN_RANGE_CHECK( m_szFile, *pStackCursor, pFrozenStack->Contexts[pFrozenStack->ulDepth], pvBase, pvLimit );
            PTR_IN_RANGE_CHECK( m_szFunctionName, *pStackCursor, pFrozenStack->Contexts[pFrozenStack->ulDepth], pvBase, pvLimit );
#undef PTR_IN_RANGE_CHECK
        }
*/
        pStackCursor = pStackCursor->m_pCtxOld;
        pFrozenStack->ulDepth++;
    }

    bSuccess = TRUE;
Exit:
    return bSuccess;
}

#endif // FUSION_FREEZE_STACK_ENABLED

VOID
FusionpDumpStack(
    IN DWORD    dwFlags,
    IN ULONG    ulLevel,
    IN PCWSTR   pcwszLinePrefix,
    IN ULONG    ulStackDepth
    )
{
    ULONG ulCurrentFrameDepth = 0;
    ULONG ulFormat = FUSIONP_DUMP_STACK_FORMAT_MEDIUM;
    PCTEB_ACTIVE_FRAME ptaf = NULL;

    if (dwFlags == 0)
    {
        dwFlags = FUSIONP_DUMP_STACK_FORMAT_MEDIUM;
    }
    else if ((dwFlags & ~(FUSIONP_DUMP_STACK_FORMAT_MASK)) != 0)
    {
#if DBG
        OutputDebugStringA("SXS.DLL: Someone called FusionpDumpStack with invalid parameters!\n");
        DebugBreak();
#endif
        return;
    }

    ulFormat = (dwFlags & FUSIONP_DUMP_STACK_FORMAT_MASK);

    if ((ulFormat != FUSIONP_DUMP_STACK_FORMAT_SHORT) &&
        (ulFormat != FUSIONP_DUMP_STACK_FORMAT_MEDIUM) &&
        (ulFormat != FUSIONP_DUMP_STACK_FORMAT_LONG))
    {
#if DBG
        OutputDebugStringA("Invalid format passed in call to FusionpDumpStack()\n");
        DebugBreak();
#endif
        return;
    }

    if (pcwszLinePrefix == NULL)
        pcwszLinePrefix = L"SXS";

#if FUSION_WIN
    ptaf = ::RtlGetFrame();
#else
    ptaf = NULL;
#endif

    while ((ptaf != NULL) && ulStackDepth--)
    {
        FRAME_INFO FrameInfo;
        ::FusionpPopulateFrameInfo(FrameInfo, ptaf);

        switch (dwFlags & FUSIONP_DUMP_STACK_FORMAT_MASK)
        {
        case FUSIONP_DUMP_STACK_FORMAT_SHORT:
            ::FusionpDbgPrintEx(
                ulLevel,
                "%ls: %s\n",
                pcwszLinePrefix,
                FrameInfo.pszFunction);
            break;

        case FUSIONP_DUMP_STACK_FORMAT_MEDIUM:
            ::FusionpDbgPrintEx(
                ulLevel,
                "%ls%s: %s (Line %d)\n",
                pcwszLinePrefix,
                FrameInfo.pszFile,
                FrameInfo.pszFunction,
                FrameInfo.nLine);
            break;

        case FUSIONP_DUMP_STACK_FORMAT_LONG:
            ::FusionpDbgPrintEx(
                ulLevel,
                "%lsFunction %s called from file %s line %d (depth %lu)\n",
                pcwszLinePrefix,
                FrameInfo.pszFunction,
                FrameInfo.pszFile,
                FrameInfo.nLine,
                ulCurrentFrameDepth++);
            break;
        }

        ptaf = ptaf->Previous;
    }
}

#if FUSION_ENABLE_FROZEN_STACK

BOOL
FusionpOutputFrozenStack(
    DWORD dwFlags,
    PCSTR Prefix,
    PFROZEN_STACK pFrozenStack
    )
{
    BOOL bSuccess = FALSE;

    if ( !pFrozenStack ) {
        ::SetLastError( ERROR_INVALID_PARAMETER );
        goto Exit;
    }

    if ( !Prefix ) Prefix = "SXS";

    for ( ULONG ul = 0; ul < pFrozenStack->ulDepth; ul++ )
    {
        FusionpDbgPrintEx(
            dwFlags,
            "%s: %s [%s(%d)]\n",
            Prefix,
            pFrozenStack->Contexts[ul].m_StaticTraceContext->m_Function,
            pFrozenStack->Contexts[ul].m_StaticTraceContext->m_File,
            pFrozenStack->Contexts[ul].m_nLine);
    }

    bSuccess = TRUE;
Exit:
    return bSuccess;
}

#endif // FUSION_ENABLE_FROZEN_STACK

#ifdef _SXS_FUTURE_STACK_FREEZING_WORK

typedef struct _SUPER_FROZEN_STACK
{
    SIZE_T      cStackFrames;
    SIZE_T      cMaxStackFrames;
    STACKFRAME  StackFrames[1];
}
SUPER_FROZEN_STACK, *PSUPER_FROZEN_STACK;

BOOL
pResolveLineFromAddress(
    PIMAGEHLP_LINE Line,
    DWORD_PTR Address,
    DWORD *pOffset
    )
{
    HPROCESS Proc = GetCurrentProcess();
    DWORD Offset = 0;

    while ( !SymGetLineFromAddr( Proc, Address - Offset, pOffset, Line ) )
    {
        if ( Offset++ == MAX_BACKTRACK )
        {
            return FALSE;
        }
    }
}

BOOL
FusionpPrintSuperFrozenStack(
    PSUPER_FROZEN_STACK pStack
    )
{
    IMAGEHLP_LINE   Line;

    for ( SIZE_T cb = 0; cb < pStack->cStackFrames; cb++ )
    {
        IMAGEHLP_LINE   Line;
        IMAGEHLP_SYMBOL Symbol;

        Line.SizeOfStuct = sizeof( Line );
        Symbol.SizeOfStruct = sizeof( Symbol );

        if ( pResolveLineFromAddress(
                &Line,
                pStack->StackFrames[cb].AddrPC.Offset
                &dwOffset
        ) )
    }
}

BOOL
FusionpSuperFreezeStack(
    DWORD dwFlags,
    PSUPER_FROZEN_STACK pStack
    )
{
    STACKFRAME  CurrentStack;
    CONTEXT     Context;
    BOOL        bOk = FALSE;
    const HANDLE hThisThread = ::GetCurrentThread();
    ULONG       ulStackSize = 0;
    BOOL        bSuccess = FALSE;

    if ( !pStack || dwFlags )
    {
        ::SetLastError( ERROR_INVALID_PARAMETER );
        goto Exit;
    }

    ZeroMemory( &CurrentStack, sizeof(CurrentStack) );
    ZeroMemory( &Context, sizeof(Context) );

    Context.ContextFlags = CONTEXT_FULL;
    GetThreadContext( hThisThread, &Context );

    CurrentStack.AddrPC.Offset      = Context.Eip;
    CurrentStack.AddrPC.Mode        = AddrModeFlat;
    CurrentStack.AddrStack.Offset   = Context.Esp;
    CurrentStack.AddrStack.Mode     = AddrModeFlat;
    CurrentStack.AddrFrame.Offset   = Context.Ebp;
    CurrentStack.AddrFrame.Mode     = AddrModeFlat;

    pStack->cStackFrames = 0;

    for ( ulStackSize = 0; ; ulStackSize++ )
    {
        bOk = ::StackWalk(
            IMAGE_FILE_MACHINE_I386,
            GetCurrentProcess(),
            hThisThread,
            &CurrentStack,
            &Context,
            NULL,
            SymFunctionTableAccess,
            SymGetModuleBase,
            NULL);

        if ( CurrentStack.AddrPC.Offset == 0 )
        {
            break;
        }
        //
        // Skip the first
        //
        else if ( ulStackSize == 0 )
        {
            continue;
        }
        //
        // If we have space in the stack...
        //
        else if ( pStack->cStackFrames < pStack->cMaxStackFrames )
        {
            pStack->StackFrames[pStack->cStackFrames++] = CurrentStack;
        }
    }

    if ( pStack->cMaxStackFrames < ulStackSize )
    {
        pStack->cMaxStackFrames = ulStackSize;
        pStack->cStackFrames = 0;

        ::SetLastError( ERROR_INSUFFICIENT_BUFFER );
        goto Exit;
    }

    bSuccess = TRUE;
Exit:
    return bSuccess;

}
#endif _SXS_FUTURE_STACK_FREEZING_WORK