/***********************************************************************

    SRVDBG.CPP


    Server Debug Support

    Copyright (C) 1995 Microsoft Corporation
    All rights reserved.

    Created on: 05/25/95

************************************************************************/
//#include "stdafx.h"
#include "pch.h"

static HANDLE g_hEventLog = NULL;
BOOL          g_fWantInt3 = TRUE;

char          g_szDbgModPath[MAX_PATH] = "<Path unknown>";



/***********************************************************************

    FUNCTION: SRVDBG_Init

    Initialize Server Debugging Support

    If NULL is passed for the path, this routine will attempt to
    determine the path through GetModuleFileName, which may not be
    accurate for DLLs, depending on the calling context of the call
    to this routine (won't work for DLLs unless called from ProcessAttach
    notification).

    Created on: 05/26/95

************************************************************************/

BOOL SRVDBG_Init(LPSTR lpszSource, LPSTR lpszPath, BOOL fWantInt3)
{
    g_fWantInt3 = fWantInt3;

    if ( !lpszSource )
        return (FALSE);

    if (!lpszPath)
        GetModuleFileName(NULL, g_szDbgModPath, sizeof(g_szDbgModPath));
    else
        lstrcpy(g_szDbgModPath, lpszPath);
    
    Assert(!g_hEventLog);
    g_hEventLog = RegisterEventSource(NULL, lpszSource );

    return (g_hEventLog != NULL);
}


/***********************************************************************

    FUNCTION: SRVDBG_Terminate

    Terminate Server Debugging Support

    Created on: 05/26/95

************************************************************************/

BOOL SRVDBG_Terminate( void )
{
    if ( !g_hEventLog )
    return ( FALSE );

    BOOL    fDeregisterEventSource =  DeregisterEventSource( g_hEventLog );
    Assert(fDeregisterEventSource);
    g_hEventLog = NULL;
    return fDeregisterEventSource;
}


/***********************************************************************

    FUNCTION: SRVDBG_ClearLog

    Clear debug event log.  SRVDBG_Init must be called first.

    Created on: 05/26/95

************************************************************************/

BOOL SRVDBG_ClearLog( void )
{
    if ( !g_hEventLog )
    return ( FALSE );

    return ( ClearEventLog( g_hEventLog, NULL ) );
}

/***********************************************************************

    FUNCTION: SRVDBG_Error

    Definition: Log error event  SRVDBG_Init must be called first.

    Created on: 12/09/97

    Comment: This was a macro before. 

************************************************************************/

void SRVDBG_Error( LPCSTR lpsz )
{
    SRVDBG_ReportEvent( SRVDBG_ERROR_TYPE, SRVDBG_ERROR, lpsz, NULL, NULL );
}

/***********************************************************************

    FUNCTION: SRVDBG_Error2

    Definition: Log error event  SRVDBG_Init must be called first.

    Created on: 12/09/97

    Comment: This was a macro before. 

************************************************************************/

void SRVDBG_Error2( LPCSTR lpsz, LPCSTR lpsz2)
{
    SRVDBG_ReportEvent( SRVDBG_ERROR_TYPE, SRVDBG_ERROR, lpsz, lpsz2, NULL );
}

/***********************************************************************

    FUNCTION: SRVDBG_Error3

    Definition: Log error event  SRVDBG_Init must be called first.

    Created on: 12/09/97

    Comment: This was a macro before.

************************************************************************/

void SRVDBG_Error3( LPCSTR lpsz, LPCSTR lpsz2, LPCSTR lpsz3)
{
    SRVDBG_ReportEvent( SRVDBG_ERROR_TYPE, SRVDBG_ERROR, lpsz, lpsz2, lpsz3 );
}

/***********************************************************************

    FUNCTION: SRVDBG_Info

    Definition: Log error event  SRVDBG_Init must be called first.

    Created on: 12/09/97

    Comment: This was a macro before.

************************************************************************/

void SRVDBG_Info( LPCSTR lpsz )
{
    SRVDBG_ReportEvent( SRVDBG_INFO_TYPE, SRVDBG_INFO, lpsz, NULL, NULL );
}

/***********************************************************************

    FUNCTION: SRVDBG_Info2

    Definition: Log error event  SRVDBG_Init must be called first.

    Created on: 12/09/97

    Comment: This was a macro before.

************************************************************************/

void SRVDBG_Info2( LPCSTR lpsz, LPCSTR lpsz2 )
{
    SRVDBG_ReportEvent( SRVDBG_INFO_TYPE, SRVDBG_INFO, lpsz, lpsz2, NULL );
}

/***********************************************************************

    FUNCTION: SRVDBG_Info3

    Definition: Log error event  SRVDBG_Init must be called first.

    Created on: 12/09/97

    Comment: This was a macro before.

************************************************************************/

void SRVDBG_Info3( LPCSTR lpsz, LPCSTR lpsz2, LPCSTR lpsz3 )
{
    SRVDBG_ReportEvent( SRVDBG_INFO_TYPE, SRVDBG_INFO, lpsz, lpsz2, lpsz3 );
}

/***********************************************************************

    FUNCTION: SRVDBG_Warning

    Definition: Log error event  SRVDBG_Init must be called first.

    Created on: 12/09/97

    Comment: This was a macro before.

************************************************************************/

void SRVDBG_Warning( LPCSTR lpsz )
{
    SRVDBG_ReportEvent( SRVDBG_WARNING_TYPE, SRVDBG_WARNING, lpsz, NULL, NULL );
}

/***********************************************************************

    FUNCTION: SRVDBG_Warning2

    Definition: Log error event  SRVDBG_Init must be called first.

    Created on: 12/09/97

    Comment: This was a macro before.

************************************************************************/

void SRVDBG_Warning2( LPCSTR lpsz, LPCSTR lpsz2 )
{
    SRVDBG_ReportEvent( SRVDBG_WARNING_TYPE, SRVDBG_WARNING, lpsz, lpsz2, NULL );
}

/***********************************************************************

    FUNCTION: SRVDBG_Warning3

    Definition: Log error event  SRVDBG_Init must be called first.

    Created on: 12/09/97

    Comment: This was a macro before.

************************************************************************/

void SRVDBG_Warning3( LPCSTR lpsz, LPCSTR lpsz2, LPCSTR lpsz3 )
{
    SRVDBG_ReportEvent( SRVDBG_WARNING_TYPE, SRVDBG_WARNING, lpsz, lpsz2, lpsz3 );
}

void SRVDBG_ErrorEvt( DWORD IDEvent, LPCSTR lpsz )
{
    SRVDBG_ReportEvent( SRVDBG_ERROR_TYPE, IDEvent, lpsz, NULL, NULL );
}

void SRVDBG_WarningEvt( DWORD IDEvent, LPCSTR lpsz )
{
    SRVDBG_ReportEvent( SRVDBG_WARNING_TYPE, IDEvent, lpsz, NULL, NULL );
}

void SRVDBG_InfoEvt( DWORD IDEvent, LPCSTR lpsz )
{
    SRVDBG_ReportEvent( SRVDBG_INFO_TYPE, IDEvent, lpsz, NULL, NULL );
}

void SRVDBG_Assert( LPCSTR lpsz )
{
    SRVDBG_ErrorEvt( SRVDBG_ASSERT, lpsz );
}

/***********************************************************************

    FUNCTION: SBRDBG_ReportEvent

    Report error, warning, or information. SRVDBG_Init must be called first.

    History
        Created on: 05/26/95
        06/05/96 cmason Added special support for getting Assert's logged 
                        into the QScript Thread Local Storage trave buffer
                        so that on Aborts and Exceptions, the Assert will
                        show up in the dump of the trace buffer.

************************************************************************/

BOOL SRVDBG_ReportEvent(
    WORD                        fwEventType,
    DWORD                       IDEvent,
    LPCSTR                      lpsz,
    LPCSTR                      lpsz2,
    LPCSTR                      lpsz3)
{
    LPCSTR                      plpszT[5];
    WORD                        cStrings;
    BOOL                        fSuccess;       

    cStrings = 5;
    plpszT[0] = g_szDbgModPath;
    plpszT[1] = ""; // was product id
    plpszT[2] = lpsz ? lpsz : "";
    plpszT[3] = lpsz2 ? lpsz2 : "";
    plpszT[4] = lpsz3 ? lpsz3 : "";


    // go ahead and send the event to anyone watching debug output
    for (int i=0; i<cStrings; i++)
    {
        if (*plpszT[i])
        {
            if( SRVDBG_ASSERT == IDEvent )
            {
                TraceN1( "%s ", plpszT[i] );
            }
            else
            {
                ODS(plpszT[i]);
                ODS(" ");
            }
        }
    }
    if( SRVDBG_ASSERT == IDEvent )
    {
        Trace0( "\r\n" );
    }
    else
    {
        ODS("\r\n");
    }

    switch ( fwEventType )
    {
    case SRVDBG_INFO_TYPE:
    case SRVDBG_WARNING_TYPE:
        break;

    case SRVDBG_ERROR_TYPE:
        if (g_fWantInt3)
        {
            //
            // 06/13/96 cmason Will only break into a debugger if one is 
            //  running.  If not running under a debugger, does nothing.
            //
            __try
            {
               DebugBreak();
            }
            __except (EXCEPTION_EXECUTE_HANDLER)
            {
            }
        }
        //ExitProcess(1);
        break;
    }

    //
    // 05/29/96 cmason Moved check to bottom of function so that Asserts still work even it
    //  SRVDBG could not init the event log.
    // 09/08/96 garrettm If no event source, we try to create one on the fly...
    //
    if ( !g_hEventLog )
    {
        HANDLE hLog = RegisterEventSource(NULL, "UNKNOWN");

        if (hLog)
        {
            ReportEvent( hLog, fwEventType, 0, IDEvent, NULL, cStrings, 0, plpszT, NULL );
            DeregisterEventSource( hLog );
        }

        return ( FALSE );
    }

    fSuccess = ReportEvent( g_hEventLog, fwEventType, 0, IDEvent, NULL,
                            cStrings, 0, plpszT, NULL );

    return ( fSuccess );
}

void SRVDBG_ErrorBinary(LPCSTR lpsz, LPCSTR lpsz2, LPCSTR lpsz3, DWORD dwDataSize, LPVOID lpRawData )
{
    SRVDBG_ReportEventBinary(SRVDBG_ERROR_TYPE, SRVDBG_ERROR, lpsz, lpsz2, lpsz3, dwDataSize, (LPVOID)lpRawData);
}

void SRVDBG_WarningBinary( LPCSTR lpsz, LPCSTR lpsz2, LPCSTR lpsz3, DWORD dwDataSize, LPVOID lpRawData )
{
    SRVDBG_ReportEventBinary( SRVDBG_WARNING_TYPE, SRVDBG_WARNING, lpsz, lpsz2, lpsz3, dwDataSize, (LPVOID)lpRawData );
}

void SRVDBG_InfoBinary( LPCSTR lpsz, LPCSTR lpsz2, LPCSTR lpsz3, DWORD dwDataSize, LPVOID lpRawData )
{
    SRVDBG_ReportEventBinary( SRVDBG_INFO_TYPE, SRVDBG_INFO, lpsz, lpsz2, lpsz3, dwDataSize, (LPVOID)lpRawData );
}

void SRVDBG_RebootMachine()
{
    SRVDBG_ReportEvent( SRVDBG_ERROR_TYPE, SRVDBG_ERROR, "Reboot Machine", NULL, NULL );
}

void SRVDBG_RestartService( LPCSTR lpsz )
{
    SRVDBG_ReportEvent( SRVDBG_ERROR_TYPE, SRVDBG_ERROR, "Restart Service", lpsz, NULL );
}

/***********************************************************************

    FUNCTION: SBRDBG_ReportEventBinary

    Report error, warning, or information in binary form. 
    
    SRVDBG_Init must be called first.

    History
        04/14/97 cmason Created to allow us to log larger pieces of information
                        than allowed in the Event Log with strings.

************************************************************************/
BOOL SRVDBG_ReportEventBinary
(
    WORD                        fwEventType,
    DWORD                       IDEvent,
    LPCSTR                      lpsz,
    LPCSTR                      lpsz2,
    LPCSTR                      lpsz3,
    DWORD                       dwDataSize,
    LPVOID                      lpRawData
)
{
    const WORD                  cStrings = 5;
    LPCSTR                      plpszT[cStrings];
    BOOL                        fSuccess;       

    plpszT[0] = g_szDbgModPath;
    plpszT[1] = "";  // was productid
    plpszT[2] = lpsz ? lpsz : "";
    plpszT[3] = lpsz2 ? lpsz2 : "";
    plpszT[4] = lpsz3 ? lpsz3 : "";


    // go ahead and send the event to anyone watching debug output
    for (int i=0; i < cStrings; i++)
    {
        if (*plpszT[i])
        {
            if( SRVDBG_ASSERT == IDEvent )
            {
                TraceN1( "%s ", plpszT[i] );
            }
            else
            {
                ODS(plpszT[i]);
                ODS(" ");
            }
        }
    }
    if( SRVDBG_ASSERT == IDEvent )
    {
        Trace0( "\r\n" );
    }
    else
    {
        ODS("\r\n");
    }

    switch ( fwEventType )
    {
    case SRVDBG_INFO_TYPE:
    case SRVDBG_WARNING_TYPE:
        break;

    case SRVDBG_ERROR_TYPE:
        if (g_fWantInt3)
        {
            //
            // 06/13/96 cmason Will only break into a debugger if one is 
            //  running.  If not running under a debugger, does nothing.
            //
            __try
            {
                DebugBreak();
            }
            __except (EXCEPTION_EXECUTE_HANDLER)
            {
            }
        }
        break;
    }

    //
    // 05/29/96 cmason Moved check to bottom of function so that Asserts still work even it
    //  SRVDBG could not init the event log.
    // 09/08/96 garrettm If no event source, we try to create one on the fly...
    //
    if ( !g_hEventLog )
    {
        HANDLE hLog = RegisterEventSource(NULL, "UNKNOWN");

        if (hLog)
        {
            ReportEvent( hLog, fwEventType, 0, IDEvent, NULL, cStrings, dwDataSize, plpszT, lpRawData );
            DeregisterEventSource( hLog );
        }

        return ( FALSE );
    }

    fSuccess = ReportEvent( g_hEventLog, fwEventType, 0, IDEvent, NULL,
                            cStrings, dwDataSize, plpszT, lpRawData );

    return ( fSuccess );
}


/***********************************************************************

***********************************************************************/
void SRVDBG_DoAssert(
    LPCSTR                      szFile,
    int                         iLine,
    LPCSTR                      szAssert )
{
    char                        szBuf[ 2048 ];

    wsprintf( szBuf, "File: %s at line %d\r\n%s", szFile, iLine, szAssert );
    SRVDBG_Assert( szBuf );
}
    
/***********************************************************************

    FUNCTION: SRVDBG_LogSystemID

    Place an event of some sort (by message ID) into the NT event log.

    Created 29-Mar-96 LeoN

************************************************************************/
BOOL SRVDBG_LogSystemID (
WORD type,                                              // error, warning, info
DWORD messageID,                                // id of message in module
LPCSTR g_module,                                // module containing the message
...
) {
    va_list args;
    HMODULE hMod;
    char *  psz;
   
    hMod = GetModuleHandle (g_module);
    if (hMod)
    {
        DWORD id;

        switch (type)
            {
            case 0:
                return TRUE;
            case SRVDBG_INFO_TYPE:
                id = SRVDBG_INFO;
                break;
            case SRVDBG_WARNING_TYPE:
                id = SRVDBG_WARNING;
                break;
            default:
               case SRVDBG_ERROR_TYPE:
                id = SRVDBG_ERROR;
                break;
            }

        va_start(args, g_module);
        if (0 == FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
                                  | FORMAT_MESSAGE_FROM_HMODULE
                                  | FORMAT_MESSAGE_FROM_SYSTEM,
                                  hMod, messageID, 0,
                                  (char *)(&psz), 16, &args))
        {
            char sz[200];
            wsprintf(sz, "Couldn't find error for id %d", messageID);

            SRVDBG_ReportEvent (SRVDBG_ERROR_TYPE, id, sz, g_module, NULL);
        }
        else
        {
               SRVDBG_ReportEvent (type, id, psz, NULL, NULL);
            LocalFree(psz);
        }
        va_end(args);
      }
    else
    {
          SRVDBG_ReportEvent (SRVDBG_ERROR_TYPE, SRVDBG_ERROR, "Failure Logging Message", g_module, NULL);
    }

    return TRUE;
    }


void LogSystemSZ(WORD type, LPCSTR szMessage )
{
    SRVDBG_LogSystemSZ((WORD)type, szMessage);
}


/***********************************************************************

    FUNCTION: SRVDBG_LogSystemSZ

    Place an event of some sort (by string) into the NT event log.

    Created 29-Mar-96 LeoN

************************************************************************/
BOOL SRVDBG_LogSystemSZ (
WORD type,                                              // error, warning, info
LPCSTR szMessage
) {
    DWORD id;

    switch (type)
    {
    case 0:
        return TRUE;

    case SRVDBG_INFO_TYPE:
        id = SRVDBG_INFO;
        break;
    case SRVDBG_WARNING_TYPE:
        id = SRVDBG_WARNING;
        break;
    default:
    case SRVDBG_ERROR_TYPE:
        id = SRVDBG_ERROR;
        break;
    }
    return SRVDBG_ReportEvent (type, id, szMessage, NULL, NULL);
  }

extern "C" void DoAssert(
    LPCSTR                      szFile,
    int                         iLine,
    LPCSTR                      szAssert )
{
    char                        szBuf[ 2048 ];

    wsprintf( szBuf, "File: %s at line %d\r\n%s", szFile, iLine, szAssert );
    SRVDBG_Assert( szBuf );
}

#ifdef DEBUG

// Force Debug initialization early
// #pragma init_seg(compiler)


DebugInfo g_dbi = {
    true,        // Show trace
    -1,            // No TLS trace buffer
    NULL,        // No special trace function
    true,        // Show asserts
    0,            // Assert count
    false,        // Assert on alloc failure
    false,        // Stop on Assert
};


/*---------------------------------------------------------------------------
    PrivateDbgTraceV
        Trace string with va_list arguments.  Handle indenting.
    Author: kennt
 ---------------------------------------------------------------------------*/
static void PrivateDbgTraceV(LPCSTR szFormat, va_list pvargs)
{
    char szBuffer[2048+16];
    char *psz = szBuffer;
#ifdef CORP_BUILD
    psz += wsprintf( psz, "tid(%x): ", GetCurrentThreadId());
#endif

    wvsprintf(psz, szFormat, pvargs);
    OutputTrace(szBuffer);
}

/*!--------------------------------------------------------------------------
    DbgTraceV
        Trace string with va_list arguments.  Handle indenting.
    Author: GaryBu
 ---------------------------------------------------------------------------*/
DBG_API(void) DbgTraceV(LPCSTR szFormat, va_list pvargs)
{
    PrivateDbgTraceV(szFormat, pvargs);
}

/*!--------------------------------------------------------------------------
    DbgTrace
        Trace string with args.
    Author: suryanr
 ---------------------------------------------------------------------------*/
DBG_APIV(void) DbgTrace(LPSTR szFormat, ...)
{
    va_list args;

    if (g_dbi.fDoTrace)
        {
        va_start(args, szFormat);
        DbgTraceV(szFormat, args);
        va_end(args);
        }
}


/*---------------------------------------------------------------------------
    OutputTrace
        OutputDebugString + any local handling
    Author: MarkGo
 ---------------------------------------------------------------------------*/
DBG_API(void) OutputTrace(LPCTSTR lpBuffer)
{
    DbgTraceBuf    *ptb;
    
    if (g_dbi.pfnTrace)
        g_dbi.pfnTrace(lpBuffer);
    else
        ODS(lpBuffer);

    if (g_dbi.itlsTraceRGB != (DWORD) -1
        && (ptb = (DbgTraceBuf *)TlsGetValue(g_dbi.itlsTraceRGB))!=NULL )
        {
        DWORD    cbOut = lstrlen(lpBuffer)+1;
        // Buffer the trace output

        // Is output larger than buf?
        if (cbOut >= ptb->cchBuf)
            {
            // Copy *last* cchBuf chars
            memcpy(ptb->rgch, lpBuffer + (cbOut - ptb->cchBuf), ptb->cchBuf);
            ptb->ichStart = 0;
            ptb->ichEnd = ptb->cchBuf-1;
            }
        else
            {
            // Is output larger than remaining contig space?
            if (cbOut > ptb->cchBuf - ptb->ichEnd)
                {
                memcpy(&(ptb->rgch[ptb->ichEnd]), lpBuffer,
                       ptb->cchBuf - ptb->ichEnd);
                memcpy(ptb->rgch, lpBuffer+(ptb->cchBuf-ptb->ichEnd),
                       cbOut-(ptb->cchBuf-ptb->ichEnd));
                ptb->ichStart = cbOut-(ptb->cchBuf-ptb->ichEnd);
                ptb->ichEnd = ptb->ichStart-1;
                }
            else
                {
                memcpy(&ptb->rgch[ptb->ichEnd], lpBuffer, cbOut);
                if (ptb->ichStart > ptb->ichEnd)
                    {
                    ptb->ichEnd += cbOut-1;
                    ptb->ichStart = ptb->ichEnd+1;
                    }
                else
                    ptb->ichEnd += cbOut-1;
                }
            
            }
        }
}

#ifndef FDbgFalse
/*!--------------------------------------------------------------------------
    FDbgFalse
        Returns false, but insures that debug code page is in memory.
        Also makes a handy breakpoint.

    Author: MarkGo
 ---------------------------------------------------------------------------*/
DBG_API(BOOL)    FDbgFalse()
{
    return false;
}
#endif

#ifdef __AFX_H__
/*!--------------------------------------------------------------------------
    AfxAssertFailedLine
        Overrides Afx's assertion handling
    Author: GaryBu, kennt, MarkGo
 ---------------------------------------------------------------------------*/
BOOL AFXAPI AfxAssertFailedLine(LPCSTR lpszFileName, int nLine)
{
    DbgAssert(lpszFileName, nLine, "AFX Assert");
    return false;
}

#endif

/*!--------------------------------------------------------------------------
    DbgAssert
        Display assert dialog.
    Author: GaryBu, kennt, MarkGo
 ---------------------------------------------------------------------------*/
DBG_APIV(void) DbgAssert(LPCSTR szFile, int iLine, LPCSTR szFmt, ...)
{
    va_list    arg;
    char sz[MAX_PATH*2];
    char *pch = sz;

    va_start(arg, szFmt);
    pch += wvsprintf(sz, szFmt, arg);
    va_end(arg);
    // Remove trailing newlines...
    while (*(pch-1) == '\n' || *(pch-1) == '\r')
        --pch;

    *pch = 0;
    if (g_dbi.fShowAsserts)
    {
        //
        // 03/27/96 cmason Added doing trace output as well as the Event Viewer 
        //    entry for asserts.
        // 04/04/96 cmason Removed redundancy of tracing out the assert twice, once
        //  here and agian inside the SRVDBG code called by DoAssert.  The code here 
        //  was first, but the the SRVDBG code traces out more events than just asserts.
        //
        DbgTrace( "Assert failed: ");

        //
        // This loggs to the Event Viewer
        //
        DoAssert(szFile, iLine, sz);

        //
        // 03/27/96 cmason Added breaking into the debugger if requested.
        //
        if( g_dbi.fStopOnAssert )
        {
            //
            // This does a DebugBreak() and is protected by byt __try/__except so
            //    that nothing happens if a debugger isn't running.
            //
            DbgStop();
        }
    }
    else
        DbgTrace(
    "Skipped Assert (ShowAsserts is FALSE):\r\n%s (file %s, line %d)\r\n",
                 sz, szFile, iLine);
}

/*!--------------------------------------------------------------------------
    DbgStop -- causes a break into the debugger if the debugger is running.
    -
    Author: afx
 ---------------------------------------------------------------------------*/
#pragma optimize("qgel", off) // assembler cannot be globally optimized
DBG_API(void) DbgStop()
{
    __try
    {
        DebugBreak();
    }
    __except (EXCEPTION_EXECUTE_HANDLER)
    {
    }
}

DBG_API(DWORD)    DbgSetThreadTraceBuffer(DWORD itlsTrace)
{
    DWORD itlsPrev = g_dbi.itlsTraceRGB;

    g_dbi.itlsTraceRGB = itlsTrace;

    return itlsPrev;
}


DBG_API(const char *)    DbgGetTraceBuffer(BOOL fSecondChunk)
{
    DbgTraceBuf    *ptb;
    
    if (g_dbi.itlsTraceRGB == (DWORD)-1)
        return fSecondChunk ? "" : "<No trace information available>\r\n";
            
    if (NULL==(ptb = (DbgTraceBuf *)TlsGetValue(g_dbi.itlsTraceRGB)))
        return fSecondChunk ? "" : "<No trace buffer set>\r\n";

    if (!fSecondChunk)
        {
        // Return initial chunk, regardless of wraparound.
        return &(ptb->rgch[ptb->ichStart]);
        }
    
    // We only have a second chunk in wraparound
    if (ptb->ichStart > ptb->ichEnd)
        return ptb->rgch;

    return "";
}


DBG_API(BOOL)    DbgSetTraceBuffer(void *rgbTrace, int cbTrace)
{
    DbgTraceBuf    *ptb;
    
    if (g_dbi.itlsTraceRGB == (DWORD) -1
        || (rgbTrace && cbTrace <= sizeof(DbgTraceBuf)))
        return false;

    ptb = (DbgTraceBuf *)rgbTrace;
    if (ptb)
        {
        ptb->cchBuf = cbTrace - sizeof(DbgTraceBuf);
        ptb->ichStart = ptb->ichEnd = 0;
        ptb->rgch[ptb->ichEnd] = '\0';
        ptb->rgch[ptb->cchBuf] = '\0';
        }
    
    TlsSetValue(g_dbi.itlsTraceRGB, ptb);
    return true;
}


#pragma optimize("", on)

#endif //DEBUG

/***********************************************************************

    FUNCTION: LoadMsg

    Loads a string from the message table and returns it in an allocated
    string buffer.    

    History
      Created on: 06/01/95
      04/29/96 cmason   Trav#336: Added FORMAT_MESSAGE_MAX_WIDTH_MASK
                        so that CRLF's must be explicitly specified.

************************************************************************/

char *LoadMsg(UINT idMsg,                   
        HMODULE hmod)    // May be null, in which case app's msg table is used
{
    void *pv;
    char *pszRet;
    DWORD cch;

    cch = FormatMessage(FORMAT_MESSAGE_ALLOCATE_BUFFER
                  | FORMAT_MESSAGE_IGNORE_INSERTS
                  | FORMAT_MESSAGE_FROM_HMODULE
                  | FORMAT_MESSAGE_FROM_SYSTEM
                  | FORMAT_MESSAGE_MAX_WIDTH_MASK,
                  hmod, idMsg, 0 /*Default language*/,
                  (char *)(&pv), 16, NULL);
    if ( !cch )
        return NULL;

    //
    // 04/30/96 cmason #336: Removing the trailing space.
    //
    Assert( ' ' == ((char *)pv)[cch - 1]);
    ((char *)pv)[cch - 1] = '\0';

    pszRet = new char[cch];
    memcpy(pszRet, pv, cch);

    LocalFree(pv);
    return pszRet;
}