MAPIDBG.C
/* 
 *  MAPIDBG.C 
 * 
 *  MAPI Debugging Utilities 
 * 
 *  Copyright (C) 1986-1996 Microsoft Corporation. All rights reserved. 
 */ 
 
#ifdef DEBUG 
 
#pragma warning(disable:4100)   /* unreferenced formal parameter */ 
#pragma warning(disable:4127)   /* conditional expression is constant */ 
#pragma warning(disable:4201)   /* nameless struct/union */ 
#pragma warning(disable:4206)   /* translation unit is empty */ 
#pragma warning(disable:4209)   /* benign typedef redefinition */ 
#pragma warning(disable:4214)   /* bit field types other than int */ 
#pragma warning(disable:4001)   /* single line comments */ 
#pragma warning(disable:4050)   /* different code attributes */ 
 
#ifdef _MAC 
#define INC_OLE2 
#include <windows.h> 
#include <macname1.h> 
#include <macos\menus.h> 
#include <stdio.h> 
#include <mapiprof.h> 
 
#define GetPrivateProfileIntA       MAPIGetPrivateProfileInt 
 
#elif defined(WIN16) || defined(_WIN32) 
#pragma warning(disable:4115)   /* named type definition in parentheses */ 
#include <windows.h> 
#include <mapiwin.h> 
 
#ifdef _WIN32 
#pragma warning(disable:4001)   /* single line comments */ 
#pragma warning(disable:4115)   /* named type definition in parentheses */ 
#pragma warning (disable:4514)  /* unreferenced inline function */ 
#include <objerror.h> 
#endif 
 
#else 
 
#include <stdio.h> 
void __far __pascal OutputDebugString(char __far *); 
#define wvsprintf           vsprintf 
#define wsprintf            sprintf 
 
#endif      /* _MAC */ 
 
#ifdef DOS 
#define lstrcpyA            strcpy 
#define lstrlenA            strlen 
#define lstrcatA            strcat 
#define wvsprintfA          wvsprintf 
#define wsprintfA           wsprintf 
#define OutputDebugStringA  OutputDebugString 
#endif 
 
#include <mapidbg.h> 
#include <mapidefs.h> 
#include <mapitags.h> 
#include <mapicode.h> 
#include <stdarg.h> 
#include <string.h> 
#include <time.h> 
#ifdef _MAC 
#include <macname2.h> 
#endif 
 
#if defined(DBCS) && defined(DOS) 
#include <gapidos.h> 
#endif 
 
#if defined(DEBUG) && defined(_WINNT) 
#include <lmcons.h> 
#include <lmalert.h> 
#endif 
 
/*  Patch/Hack for 16bit, optimized builds. 
 * 
 *  memcpy with a size of 0 bytes causes a 
 *  crash. 
 */ 
 
#ifndef __MEMCPY_H_ 
#define __MEMCPY_H_ 
 
#if defined(WIN16) && !defined(DEBUG) 
#define MemCopy(_dst,_src,_cb)      do                                  \ 
                                    {                                   \ 
                                        size_t __cb = (size_t)(_cb);    \ 
                                        if (__cb)                       \ 
                                            memcpy(_dst,_src,__cb);     \ 
                                    } while (FALSE) 
#else 
#define MemCopy(_dst,_src,_cb)  memcpy(_dst,_src,(size_t)(_cb)) 
#endif 
 
#endif 
 
#if (defined(WIN16) || defined(DOS)) && !defined(NO_BASED_DEBUG) 
#define BASED_DEBUG __based(__segname("DEBUG_DATA")) 
#else 
#define BASED_DEBUG 
#endif 
 
#if defined(WIN16) 
#define BASED_CODE          __based(__segname("_CODE")) 
#else 
#define BASED_CODE 
#endif 
 
 
 
#if defined(WIN16) || defined(_WIN32) 
static BOOL fTraceEnabled               = -1; 
static BOOL fUseEventLog                = -1; 
static BOOL fAssertLeaks                = -1; 
#if defined(_WIN32) && !defined(_MAC) 
BOOL fInhibitTrapThread                 = 2; 
#endif 
 
static char szKeyTraceEnabled[]         = "DebugTrace"; 
static char szKeyInhibitTrapThread[]    = "TrapOnSameThread"; 
static char szKeyEventLog[]             = "EventLog"; 
static char szKeyUseVirtual[]           = "VirtualMemory"; 
static char szKeyAssertLeaks[]          = "AssertLeaks"; 
static char szKeyCheckOften[]           = "CheckHeapOften"; 
static char szKeyFillRandom[]           = "MemoryFillRandom"; 
static char szSectionDebug[]            = "General"; 
static char szDebugIni[]                = "MAPIDBG.INI"; 
#endif 
 
#ifndef VTABLE_FILL 
#ifdef _MAC 
#define VTABLE_FILL     NULL, 
#else 
#define VTABLE_FILL 
#endif 
#endif 
 
#if defined(DEBUG) && defined(_WINNT) 
typedef BOOL  (WINAPI   *ReportEventFN)(HANDLE, WORD, WORD, DWORD, PSID, WORD, DWORD, LPCTSTR *, LPVOID); 
typedef HANDLE (WINAPI  *RegisterEventSourceAFN)(LPCTSTR, LPCTSTR); 
 
ReportEventFN pfnReportEvent = NULL; 
RegisterEventSourceAFN pfnRegisterEventSourceA = NULL; 
#endif 
 
 
#ifdef  WIN16 
#pragma code_seg("Debug") 
#endif   
 
#if defined( _WINNT) 
 
/*++ 
 
Routine Description: 
 
    This routine returns if the service specified is running interactively 
    (not invoked \by the service controller). 
 
Arguments: 
 
    None 
 
Return Value: 
 
    BOOL - TRUE if the service is an EXE. 
 
 
Note: 
 
--*/ 
 
BOOL WINAPI IsDBGServiceAnExe( VOID ) 
{ 
    HANDLE hProcessToken = NULL; 
    DWORD groupLength = 50; 
 
    PTOKEN_GROUPS groupInfo = (PTOKEN_GROUPS)LocalAlloc(0, groupLength); 
 
    SID_IDENTIFIER_AUTHORITY siaNt = SECURITY_NT_AUTHORITY; 
    PSID InteractiveSid = NULL; 
    PSID ServiceSid = NULL; 
    DWORD i; 
 
    // Start with assumption that process is an EXE, not a Service. 
    BOOL fExe = TRUE; 
 
 
    if (!OpenProcessToken(GetCurrentProcess(), TOKEN_QUERY, &hProcessToken)) 
        goto ret; 
 
    if (groupInfo == NULL) 
        goto ret; 
 
    if (!GetTokenInformation(hProcessToken, TokenGroups, groupInfo, 
        groupLength, &groupLength)) 
    { 
        if (GetLastError() != ERROR_INSUFFICIENT_BUFFER) 
            goto ret; 
 
        LocalFree(groupInfo); 
        groupInfo = NULL; 
     
        groupInfo = (PTOKEN_GROUPS)LocalAlloc(0, groupLength); 
     
        if (groupInfo == NULL) 
            goto ret; 
     
        if (!GetTokenInformation(hProcessToken, TokenGroups, groupInfo, 
            groupLength, &groupLength)) 
        { 
            goto ret; 
        } 
    } 
 
    // 
    //  We now know the groups associated with this token.  We want to look to see if 
    //  the interactive group is active in the token, and if so, we know that 
    //  this is an interactive process. 
    // 
    //  We also look for the "service" SID, and if it's present, we know we're a service. 
    // 
    //  The service SID will be present iff the service is running in a 
    //  user account (and was invoked by the service controller). 
    // 
 
 
    if (!AllocateAndInitializeSid(&siaNt, 1, SECURITY_INTERACTIVE_RID, 0, 0, 
        0, 0, 0, 0, 0, &InteractiveSid)) 
    { 
        goto ret; 
    } 
 
    if (!AllocateAndInitializeSid(&siaNt, 1, SECURITY_SERVICE_RID, 0, 0, 0, 
        0, 0, 0, 0, &ServiceSid)) 
    { 
        goto ret; 
    } 
 
    for (i = 0; i < groupInfo->GroupCount ; i += 1) 
    { 
        SID_AND_ATTRIBUTES sanda = groupInfo->Groups[i]; 
        PSID Sid = sanda.Sid; 
     
        // 
        //  Check to see if the group we're looking at is one of 
        //  the 2 groups we're interested in. 
        // 
     
        if (EqualSid(Sid, InteractiveSid)) 
        { 
            // 
            //  This process has the Interactive SID in its 
            //  token.  This means that the process is running as 
            //  an EXE. 
            // 
            goto ret; 
        } 
        else if (EqualSid(Sid, ServiceSid)) 
        { 
            // 
            //  This process has the Service SID in its 
            //  token.  This means that the process is running as 
            //  a service running in a user account. 
            // 
            fExe = FALSE; 
            goto ret; 
        } 
    } 
 
    // 
    //  Neither Interactive or Service was present in the current users token, 
    //  This implies that the process is running as a service, most likely 
    //  running as LocalSystem. 
    // 
    fExe = FALSE; 
 
ret: 
 
    if (InteractiveSid) 
        FreeSid(InteractiveSid); 
 
    if (ServiceSid) 
        FreeSid(ServiceSid); 
 
    if (groupInfo) 
        LocalFree(groupInfo); 
 
    if (hProcessToken) 
        CloseHandle(hProcessToken); 
 
    return(fExe); 
} 
 
#endif 
 
/* LogIt */ 
 
#ifndef _MAC 
void    LogIt(LPSTR plpcText, BOOL  fUseAlert) 
{ 
#if defined(DEBUG) && defined(_WINNT) 
    LPSTR           llpcStr[2]; 
    static HANDLE   hEventSource = NULL; 
 
    if (pfnRegisterEventSourceA == NULL) 
    { 
        /* This handle is not important as the lib will be freed on exit (and it's debug only) */ 
        HINSTANCE       lhLib = LoadLibraryA("advapi32.dll"); 
         
        if (!lhLib) 
            return; 
         
        pfnRegisterEventSourceA = (RegisterEventSourceAFN) GetProcAddress(lhLib, "RegisterEventSourceA"); 
        pfnReportEvent = (ReportEventFN) GetProcAddress(lhLib, "ReportEventA"); 
         
        if (!pfnRegisterEventSourceA || !pfnReportEvent) 
            return; 
    } 
         
    if (!hEventSource)                                       
        hEventSource = pfnRegisterEventSourceA(NULL, "MAPIDebug"); 
 
    llpcStr[0] = "MAPI Debug Log"; 
    llpcStr[1] = plpcText; 
 
    pfnReportEvent(hEventSource,    /* handle of event source */ 
        EVENTLOG_ERROR_TYPE,        /* event type             */ 
        0,                          /* event category         */ 
        0,                          /* event ID               */ 
        NULL,                       /* current user's SID     */ 
        2,                          /* strings in lpszStrings */ 
        0,                          /* no bytes of raw data   */ 
        llpcStr,                    /* array of error strings */ 
        NULL);                      /* no raw data            */ 
         
    /* Now we generate an Alert! */ 
    /* This code is adapted from PierreC's stuff, and NEEDS TO BE UNICODE!!!! */ 
    if (fUseAlert) 
    { 
#define MAX_LINE        256 
 
typedef NET_API_STATUS  (WINAPI *NAREFN)(TCHAR *, ADMIN_OTHER_INFO *, ULONG, TCHAR *); 
 
        BYTE                rgb[sizeof(ADMIN_OTHER_INFO) + (sizeof(WCHAR) * MAX_LINE)]; 
        ADMIN_OTHER_INFO *  poi     = (ADMIN_OTHER_INFO *) rgb; 
        WCHAR *             pch     = (WCHAR *) (rgb + sizeof(ADMIN_OTHER_INFO)); 
        NET_API_STATUS      nas; 
        static   NAREFN     fnNetAlertRaiseEx = NULL; 
         
        /* Resolve function here, never free library as it's debug only */ 
        if (!fnNetAlertRaiseEx) 
        { 
            HINSTANCE       lhLib = LoadLibrary("NETAPI32.DLL"); 
            if (lhLib) 
                fnNetAlertRaiseEx = (NAREFN) GetProcAddress(lhLib, "NetAlertRaiseEx"); 
        } 
         
        if (fnNetAlertRaiseEx) 
        { 
            poi->alrtad_errcode = (DWORD) -1; 
            poi->alrtad_numstrings = 1; 
             
            if (MultiByteToWideChar(CP_ACP, MB_PRECOMPOSED, plpcText, -1, pch, MAX_LINE)) 
            { 
                nas = fnNetAlertRaiseEx( 
                            (TCHAR *) L"ADMIN", 
                            poi,  
                            sizeof(ADMIN_OTHER_INFO) + ((lstrlenW(pch) + 1) * sizeof(WCHAR)), 
                            (TCHAR *) L"MAPI Assert"); 
                     
                         
            } 
        } 
    } 
     
#endif /* DEBUG && NT */ 
} 
#endif /* !_MAC */ 
 
/* DebugOutputFn ------------------------------------------------------------ */ 
 
char BASED_CODE szCR[] = "\r"; 
 
void DebugOutputFn(char *psz) 
{ 
#if defined(_MAC) 
 
    OutputDebugString(psz); 
 
#else 
 
#if defined(WIN16) || defined(_WIN32) 
    if (fTraceEnabled == -1) 
    { 
        fTraceEnabled = GetPrivateProfileIntA(szSectionDebug, szKeyTraceEnabled, 
            0, szDebugIni); 
 
        fUseEventLog = GetPrivateProfileIntA(szSectionDebug, szKeyEventLog, 
            0, szDebugIni);      
    } 
 
    if (!fTraceEnabled) 
        return; 
 
    if (fUseEventLog) 
#else 
    if (FALSE) 
#endif 
        LogIt(psz, FALSE); 
 
#ifdef WIN16 
    OutputDebugString(psz); 
    OutputDebugString(szCR); 
#else 
    OutputDebugStringA(psz); 
    OutputDebugStringA(szCR); 
#endif 
     
#endif  /* _MAC */ 
} 
 
 
/* DebugTrapFn -------------------------------------------------------------- */ 
 
#if defined(_WIN32) && !defined(_MAC) 
 
typedef struct { 
    char *      sz1; 
    char *      sz2; 
    UINT        rgf; 
    int         iResult; 
} MBContext; 
 
DWORD WINAPI MessageBoxFnThreadMain(MBContext *pmbc) 
{ 
    if (fUseEventLog) 
    { 
        LogIt(pmbc->sz1, TRUE); 
        pmbc->iResult = IDIGNORE; 
    } 
    else 
        pmbc->iResult = MessageBoxA(NULL, pmbc->sz1, pmbc->sz2, 
            pmbc->rgf | MB_SETFOREGROUND); 
 
    return(0); 
} 
 
int MessageBoxFn(char *sz1, char *sz2, UINT rgf) 
{ 
    HANDLE      hThread; 
    DWORD       dwThreadId; 
    MBContext   mbc; 
 
    mbc.sz1     = sz1; 
    mbc.sz2     = sz2; 
    mbc.rgf     = rgf; 
    mbc.iResult = IDRETRY; 
 
    #if defined(_WIN32) && !defined(_MAC) 
    if (fInhibitTrapThread == 2) 
        fInhibitTrapThread = GetPrivateProfileIntA(szSectionDebug, 
            szKeyInhibitTrapThread, 0, szDebugIni); 
    #endif 
 
    if (fInhibitTrapThread) 
    { 
        MessageBoxFnThreadMain(&mbc); 
    } 
    else 
    { 
        hThread = CreateThread(NULL, 0, 
            (PTHREAD_START_ROUTINE)MessageBoxFnThreadMain, &mbc, 0, &dwThreadId); 
 
        if (hThread != NULL) { 
            WaitForSingleObject(hThread, INFINITE); 
            CloseHandle(hThread); 
        } 
    } 
 
    return(mbc.iResult); 
} 
#else 
#define MessageBoxFn(sz1, sz2, rgf)     MessageBoxA(NULL, sz1, sz2, rgf) 
#endif 
 
int EXPORTDBG __cdecl DebugTrapFn(int fFatal, char *pszFile, int iLine, char *pszFormat, ...) 
{ 
    char    sz[512]; 
    va_list vl; 
 
    #if defined(WIN16) || defined(_WIN32) 
    int     id; 
    #endif 
    #if defined(_WIN32) && !defined(_MAC) 
    static int iServiceFlag = -1; 
    #endif 
 
    lstrcpyA(sz, "++++ MAPI Debug Trap ("); 
    _strdate(sz + lstrlenA(sz)); 
    lstrcatA(sz, " "); 
    _strtime(sz + lstrlenA(sz)); 
    lstrcatA(sz, ")\n"); 
    DebugOutputFn(sz); 
 
    va_start(vl, pszFormat); 
    wvsprintfA(sz, pszFormat, vl); 
    va_end(vl); 
 
    wsprintfA(sz + lstrlenA(sz), "\n[File %s, Line %d]\n\n", pszFile, iLine); 
 
    DebugOutputFn(sz); 
 
    #if defined(DOS) 
    _asm { int 3 } 
    #endif 
 
#if defined(WIN16) || defined(_WIN32) 
    /* Hold down control key to prevent MessageBox */ 
    if ( GetAsyncKeyState(VK_CONTROL) >= 0 ) 
    { 
        UINT uiFlags = MB_ABORTRETRYIGNORE; 
 
        if (fFatal) 
            uiFlags |= MB_DEFBUTTON1; 
        else 
            uiFlags |= MB_DEFBUTTON3; 
 
        #ifdef WIN16 
        uiFlags |= MB_ICONEXCLAMATION | MB_SYSTEMMODAL; 
        #else 
        uiFlags |= MB_ICONSTOP | MB_TASKMODAL; 
        #endif 
 
        #if defined(_WIN32) && !defined(_MAC) 
        if (iServiceFlag == -1) 
        { 
            DWORD dwVersion = GetVersion(); 
 
            if (dwVersion & 0x80000000) 
            { 
                if (LOBYTE(LOWORD(dwVersion)) < 4) 
                { 
                    //  NT 3.51 
                    iServiceFlag = 0x00040000; 
                } 
                else 
                { 
                    //  NT 4.0+ 
                    iServiceFlag = 0x00200000; 
                } 
            } 
            else 
                //  not NT, skip this 
                iServiceFlag = 0; 
        } 
 
        if (!IsDBGServiceAnExe()) 
            uiFlags |= (UINT) iServiceFlag; 
        #endif 
 
        id = MessageBoxFn(sz, "MAPI Debug Trap", uiFlags); 
 
        if (id == IDABORT) 
            *((LPBYTE)NULL) = 0; 
        else if (id == IDRETRY) 
            DebugBreak(); 
    } 
#endif 
 
    return(0); 
} 
 
/* DebugTraceFn ------------------------------------------------------------- */ 
 
int EXPORTDBG __cdecl DebugTraceFn(char *pszFormat, ...) 
{ 
    char    sz[768]; 
    int     fAutoLF = 0; 
    va_list vl; 
 
    if (*pszFormat == '~') { 
        pszFormat += 1; 
        fAutoLF = 1; 
    } 
 
    va_start(vl, pszFormat); 
    wvsprintfA(sz, pszFormat, vl); 
    va_end(vl); 
 
#ifndef _MAC 
    if (fAutoLF) 
        lstrcatA(sz, "\n"); 
#endif 
 
    DebugOutputFn(sz); 
 
    return(0); 
} 
 
/* DebugTraceProblemsFn */ 
 
void EXPORTDBG __cdecl DebugTraceProblemsFn(LPSTR sz, LPVOID pv) 
{ 
    LPSPropProblemArray pprobs = (LPSPropProblemArray)pv; 
    SPropProblem *      pprob = pprobs->aProblem; 
    int                 cprob = (int)pprobs->cProblem; 
 
    DebugTraceFn("%s: SetProps problem\n", sz); 
    while (cprob--) 
    { 
        DebugTraceFn("Property %s (index %ld): failed with %s\n", 
            SzDecodeUlPropTagFn(pprob->ulPropTag), 
            pprob->ulIndex, 
            SzDecodeScodeFn(pprob->scode)); 
    } 
} 
 
/* SCODE & PropTag decoding ------------------------------------------------- */ 
 
typedef struct 
{ 
    char *          psz; 
    unsigned long   ulPropTag; 
} PT; 
 
typedef struct 
{ 
    char *  psz; 
    SCODE   sc; 
} SC; 
 
#define Pt(_ptag)   {#_ptag, _ptag} 
#define Sc(_sc)     {#_sc, _sc} 
 
#if !defined(DOS) 
static PT BASED_DEBUG rgpt[] = { 
     
#include "_tags.h" 
     
/* 
 * Property types 
 */ 
    Pt(PR_NULL), 
    Pt(PT_UNSPECIFIED), 
    Pt(PT_NULL), 
    Pt(PT_I2), 
    Pt(PT_LONG), 
    Pt(PT_R4), 
    Pt(PT_DOUBLE), 
    Pt(PT_CURRENCY), 
    Pt(PT_APPTIME), 
    Pt(PT_ERROR), 
    Pt(PT_BOOLEAN), 
    Pt(PT_OBJECT), 
    Pt(PT_I8), 
    Pt(PT_STRING8), 
    Pt(PT_UNICODE), 
    Pt(PT_SYSTIME), 
    Pt(PT_CLSID), 
    Pt(PT_BINARY), 
    Pt(PT_TSTRING), 
    Pt(PT_MV_I2), 
    Pt(PT_MV_LONG), 
    Pt(PT_MV_R4), 
    Pt(PT_MV_DOUBLE), 
    Pt(PT_MV_CURRENCY), 
    Pt(PT_MV_APPTIME), 
    Pt(PT_MV_SYSTIME), 
    Pt(PT_MV_STRING8), 
    Pt(PT_MV_BINARY), 
    Pt(PT_MV_UNICODE), 
    Pt(PT_MV_CLSID), 
    Pt(PT_MV_I8) 
}; 
 
#define cpt (sizeof(rgpt) / sizeof(PT)) 
 
static SC BASED_DEBUG rgsc[] = { 
 
/* FACILITY_NULL error codes from OLE */ 
 
    Sc(S_OK), 
    Sc(S_FALSE), 
 
    Sc(E_UNEXPECTED), 
    Sc(E_NOTIMPL), 
    Sc(E_OUTOFMEMORY), 
    Sc(E_INVALIDARG), 
    Sc(E_NOINTERFACE), 
    Sc(E_POINTER), 
    Sc(E_HANDLE), 
    Sc(E_ABORT), 
    Sc(E_FAIL), 
    Sc(E_ACCESSDENIED), 
 
/* MAPI error codes from MAPICODE.H */ 
#include "_scode.h" 
                     
}; 
 
#define csc (sizeof(rgsc) / sizeof(SC)) 
#endif 
 
char * EXPORTDBG __cdecl 
SzDecodeScodeFn(SCODE sc) 
{ 
    static char rgch[64]; 
 
    #if !defined(DOS) 
    int isc; 
    for (isc = 0; isc < csc; ++isc) 
        if (sc == rgsc[isc].sc) 
            return rgsc[isc].psz; 
    #endif 
 
    wsprintfA (rgch, "%08lX", sc); 
    return rgch; 
} 
 
char * EXPORTDBG __cdecl 
SzDecodeUlPropTypeFn(unsigned long ulPropType) 
{ 
    static char rgch[8]; 
 
    switch (ulPropType) 
    { 
    case PT_UNSPECIFIED:    return("PT_UNSPECIFIED");   break; 
    case PT_NULL:           return("PT_NULL");          break; 
    case PT_I2:             return("PT_I2");            break; 
    case PT_LONG:           return("PT_LONG");          break; 
    case PT_R4:             return("PT_R4");            break; 
    case PT_DOUBLE:         return("PT_DOUBLE");        break; 
    case PT_CURRENCY:       return("PT_CURRENCY");      break; 
    case PT_APPTIME:        return("PT_APPTIME");       break; 
    case PT_ERROR:          return("PT_ERROR");         break; 
    case PT_BOOLEAN:        return("PT_BOOLEAN");       break; 
    case PT_OBJECT:         return("PT_OBJECT");        break; 
    case PT_I8:             return("PT_I8");            break; 
    case PT_STRING8:        return("PT_STRING8");       break; 
    case PT_UNICODE:        return("PT_UNICODE");       break; 
    case PT_SYSTIME:        return("PT_SYSTIME");       break; 
    case PT_CLSID:          return("PT_CLSID");         break; 
    case PT_BINARY:         return("PT_BINARY");        break; 
    } 
 
    wsprintfA(rgch, "0x%04lX", ulPropType); 
    return rgch; 
} 
 
char *  EXPORTDBG __cdecl 
SzDecodeUlPropTagFn(unsigned long ulPropTag) 
{ 
    static char rgch[64]; 
 
    #if !defined(DOS) 
    int ipt; 
    for (ipt = 0; ipt < cpt; ++ipt) 
        if (ulPropTag == rgpt[ipt].ulPropTag) 
            return rgpt[ipt].psz; 
    #endif 
 
    wsprintfA(rgch, "PROP_TAG(%s, 0x%04lX)", 
        SzDecodeUlPropType(PROP_TYPE(ulPropTag)), 
        PROP_ID(ulPropTag)); 
    return rgch; 
} 
 
SCODE  EXPORTDBG __cdecl 
ScodeFromSzFn(char *psz) 
{ 
    #if !defined(DOS) 
    int isc; 
    for (isc = 0; isc < csc; ++isc) 
        { 
        if (lstrcmpA(psz, rgsc[isc].psz) == 0) 
            { 
            return rgsc[isc].sc; 
            } 
        } 
    #endif 
    return 0; 
} 
 
unsigned long EXPORTDBG __cdecl 
UlPropTagFromSzFn(char *psz) 
{ 
    #if !defined(DOS) 
    int ipt; 
    for (ipt = 0; ipt < cpt; ++ipt) 
        { 
        if (lstrcmpA(psz, rgpt[ipt].psz) == 0) 
            { 
            return rgpt[ipt].ulPropTag; 
            } 
        } 
    #endif 
    return 0; 
} 
 
/* ScCheckScFn -------------------------------------------------------------- */ 
 
#if !defined(DOS) 
 
SCODE EXPORTDBG __cdecl ScCheckScFn(    SCODE   sc, 
                    SCODE * lpscLegal, 
                    char *  lpszMethod, 
                    char *  lpszFile, 
                    int     iLine) 
{ 
    BOOL fIsQueryInterface = (lpscLegal == IUnknown_QueryInterface_Scodes); 
 
    if (sc == S_OK) 
        return(sc); 
 
    while( *lpscLegal != S_OK && sc != *lpscLegal ) 
    { 
        lpscLegal++; 
    } 
 
    if ( *lpscLegal == S_OK ) 
    { 
        SCODE *lpscNextCommon = Common_Scodes; 
 
        /* see if this is a common scode */ 
            if ( !fIsQueryInterface ) 
                while(  *lpscNextCommon != S_OK && 
                        sc != *lpscNextCommon ) 
                { 
                    lpscNextCommon++; 
                } 
 
        /* this is an illegal error or an RPC error */ 
           if ( (*lpscNextCommon == S_OK || fIsQueryInterface) && 
                ( SCODE_FACILITY(sc) != FACILITY_RPC) ) 
           { 
                DebugTrace( "Unrecognized scode %s from %s\n\t in file %s line %d\n", 
                        SzDecodeScode( sc ), lpszMethod, lpszFile, iLine); 
            } 
    } 
 
    return(sc); 
} 
#endif 
 
/* SCODE lists -------------------------------------------------------------- */ 
 
#if !defined(DOS) 
 
#define STANDARD_OPENENTRY_SCODES \ 
    E_NOINTERFACE,  \ 
    MAPI_E_NOT_FOUND 
 
SCODE BASED_DEBUG Common_Scodes[] = 
{ 
    MAPI_E_BAD_CHARWIDTH, 
    MAPI_E_CALL_FAILED, 
    MAPI_E_INVALID_ENTRYID, 
    MAPI_E_INVALID_OBJECT, 
    MAPI_E_INVALID_PARAMETER, 
    MAPI_E_NO_ACCESS, 
    MAPI_E_NO_SUPPORT, 
    MAPI_E_NOT_ENOUGH_MEMORY, 
    MAPI_E_UNKNOWN_FLAGS, 
    S_OK 
}; 
 
SCODE BASED_DEBUG MAPILogon_Scodes[] = 
{ 
    MAPI_E_NOT_INITIALIZED, 
    MAPI_E_LOGON_FAILED, 
    S_OK 
}; 
 
SCODE BASED_DEBUG MAPIAllocateBuffer_Scodes[] = 
{ 
    MAPI_E_NOT_INITIALIZED, 
    S_OK 
}; 
 
SCODE BASED_DEBUG MAPIAllocateMore_Scodes[] = 
{ 
    MAPI_E_NOT_INITIALIZED, 
    S_OK 
}; 
 
SCODE BASED_DEBUG MAPIFreeBuffer_Scodes[] = 
{ 
    S_OK 
}; 
 
SCODE BASED_DEBUG IUnknown_QueryInterface_Scodes[] = 
{ 
    E_INVALIDARG, 
    E_NOINTERFACE, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IUnknown_GetLastError_Scodes[] = 
{ 
    MAPI_E_EXTENDED_ERROR, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIProp_CopyTo_Scodes[] = 
{ 
    MAPI_W_ERRORS_RETURNED, 
    MAPI_E_INVALID_TYPE, 
    MAPI_E_FOLDER_CYCLE, 
    MAPI_E_DECLINE_COPY, 
    E_NOINTERFACE, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIProp_CopyProps_Scodes[] = 
{ 
    MAPI_W_ERRORS_RETURNED, 
    MAPI_W_PARTIAL_COMPLETION, 
    MAPI_E_INVALID_TYPE, 
    MAPI_E_FOLDER_CYCLE, 
    MAPI_E_DECLINE_COPY, 
    E_NOINTERFACE, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIProp_DeleteProps_Scodes[] = 
{ 
    MAPI_W_ERRORS_RETURNED, 
    MAPI_E_INVALID_TYPE, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIProp_GetIDsFromNames_Scodes[] = 
{ 
    MAPI_W_ERRORS_RETURNED, 
    MAPI_E_TABLE_TOO_BIG, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIProp_GetLastError_Scodes[] = 
{ 
    MAPI_E_EXTENDED_ERROR, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIProp_GetNamesFromIDs_Scodes[] = 
{ 
    MAPI_W_ERRORS_RETURNED, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIProp_GetPropList_Scodes[] = 
{ 
    MAPI_W_ERRORS_RETURNED, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIProp_GetProps_Scodes[] = 
{ 
    MAPI_E_NOT_FOUND, 
    MAPI_E_OBJECT_DELETED, 
    MAPI_W_ERRORS_RETURNED, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIProp_OpenProperty_Scodes[] = 
{ 
    MAPI_E_INTERFACE_NOT_SUPPORTED, 
    MAPI_E_NOT_FOUND, 
    MAPI_E_OBJECT_DELETED, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIProp_SetProps_Scodes[] = 
{ 
    MAPI_E_COMPUTED, 
    MAPI_E_UNEXPECTED_TYPE, 
    MAPI_E_INVALID_TYPE, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIProp_SaveChanges_Scodes[] = 
{ 
    MAPI_E_NOT_ENOUGH_DISK, 
    MAPI_E_OBJECT_CHANGED, 
    MAPI_E_OBJECT_DELETED, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IStream_Read_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IStream_Write_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IStream_Seek_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IStream_SetSize_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IStream_Tell_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IStream_LockRegion_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IStream_UnlockRegion_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IStream_Clone_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IStream_CopyTo_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IStream_Revert_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IStream_Stat_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IStream_Commit_Scodes[] = {S_OK}; 
 
SCODE BASED_DEBUG IMAPITable_GetLastError_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPITable_Advise_Scodes[] = 
{ 
    S_OK 
}; 
SCODE BASED_DEBUG IMAPITable_Unadvise_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPITable_GetStatus_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPITable_SetColumns_Scodes[] = 
{ 
    MAPI_E_BUSY, 
    S_OK 
}; 
SCODE BASED_DEBUG IMAPITable_QueryColumns_Scodes[] = 
{ 
    MAPI_E_BUSY, 
    S_OK 
}; 
SCODE BASED_DEBUG IMAPITable_GetRowCount_Scodes[] = 
{ 
    MAPI_E_BUSY, 
    MAPI_W_APPROX_COUNT, 
    S_OK 
}; 
SCODE BASED_DEBUG IMAPITable_SeekRow_Scodes[] = 
{ 
    MAPI_E_INVALID_BOOKMARK, 
    MAPI_E_UNABLE_TO_COMPLETE, 
    MAPI_W_POSITION_CHANGED, 
    S_OK 
}; 
SCODE BASED_DEBUG IMAPITable_SeekRowApprox_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPITable_QueryPosition_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPITable_FindRow_Scodes[] = 
{ 
    MAPI_E_INVALID_BOOKMARK, 
    MAPI_E_NOT_FOUND, 
    MAPI_W_POSITION_CHANGED, 
    S_OK 
}; 
SCODE BASED_DEBUG IMAPITable_Restrict_Scodes[] = 
{ 
    MAPI_E_BUSY, 
    S_OK 
}; 
SCODE BASED_DEBUG IMAPITable_CreateBookmark_Scodes[] = 
{ 
    MAPI_E_UNABLE_TO_COMPLETE, 
    S_OK 
}; 
SCODE BASED_DEBUG IMAPITable_FreeBookmark_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPITable_SortTable_Scodes[] = 
{ 
    MAPI_E_TOO_COMPLEX, 
    S_OK 
}; 
SCODE BASED_DEBUG IMAPITable_QuerySortOrder_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPITable_QueryRows_Scodes[] = 
{ 
    MAPI_E_INVALID_BOOKMARK, 
    MAPI_W_POSITION_CHANGED, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPITable_Abort_Scodes[] = 
{ 
    MAPI_E_UNABLE_TO_ABORT, 
    S_OK 
}; 
SCODE BASED_DEBUG IMAPITable_ExpandRow_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPITable_CollapseRow_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPITable_WaitForCompletion_Scodes[] = 
{ 
    MAPI_E_TIMEOUT, 
    S_OK 
}; 
SCODE BASED_DEBUG IMAPITable_GetCollapseState_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPITable_SetCollapseState_Scodes[] = {S_OK}; 
 
 
SCODE BASED_DEBUG IMAPISession_LogOff_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPISession_Release_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPISession_GetLastError_Scodes[] = 
{ 
    MAPI_E_EXTENDED_ERROR, 
    S_OK 
}; 
SCODE BASED_DEBUG IMAPISession_GetMsgStoresTable_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPISession_GetStatusTable_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPISession_OpenMsgStore_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPISession_OpenAddressBook_Scodes[] = {S_OK}; 
 
SCODE BASED_DEBUG IMAPISession_OpenEntry_Scodes[] = 
{ 
    STANDARD_OPENENTRY_SCODES, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPISession_OpenProfileSection_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPISession_Advise_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPISession_Unadvise_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPISession_CompareEntryIDs_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPISession_MessageOptions_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPISession_QueryDefaultMessageOpt_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPISession_EnumAdrTypes_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPISession_QueryIdentity_Scodes[] = {S_OK}; 
 
SCODE BASED_DEBUG IMsgStore_OpenEntry_Scodes[] = 
{ 
    STANDARD_OPENENTRY_SCODES, 
    MAPI_E_SUBMITTED, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMsgStore_SetReceiveFolder_Scodes[] = 
{ 
    MAPI_E_BAD_CHARWIDTH, 
    MAPI_E_NOT_FOUND, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMsgStore_GetReceiveFolder_Scodes[] = 
{ 
    MAPI_E_BAD_CHARWIDTH, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMsgStore_GetReceiveFolderTable_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMsgStore_StoreLogoff_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMsgStore_Advise_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMsgStore_Unadvise_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMsgStore_CompareEntryIDs_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMsgStore_GetOutgoingQueue_Scodes[] = { 
    MAPI_E_NO_SUPPORT, 
    S_OK}; 
SCODE BASED_DEBUG IMsgStore_SetLockState_Scodes[] = { 
    MAPI_E_NO_SUPPORT, 
    MAPI_E_NOT_FOUND, 
    S_OK}; 
SCODE BASED_DEBUG IMsgStore_FinishedMsg_Scodes[] = { 
    MAPI_E_NO_SUPPORT, 
    S_OK}; 
SCODE BASED_DEBUG IMsgStore_AbortSubmit_Scodes[] = { 
    MAPI_E_UNABLE_TO_ABORT, 
    MAPI_E_NOT_IN_QUEUE, 
    S_OK}; 
SCODE BASED_DEBUG IMsgStore_NotifyNewMail_Scodes[] = {S_OK}; 
 
SCODE BASED_DEBUG IMAPIFolder_GetContentsTable_Scodes[] = 
{ 
    MAPI_E_OBJECT_DELETED, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIFolder_GetHierarchyTable_Scodes[] = 
{ 
    MAPI_E_OBJECT_DELETED, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIFolder_SaveContentsSort_Scodes[] = 
{ 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIFolder_OpenEntry_Scodes[] = 
{ 
    STANDARD_OPENENTRY_SCODES, 
    MAPI_E_SUBMITTED, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIFolder_CreateMessage_Scodes[] = 
{ 
    E_NOINTERFACE, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIFolder_CopyMessages_Scodes[] = 
{ 
    E_NOINTERFACE, 
    MAPI_E_SUBMITTED, 
    MAPI_E_DECLINE_COPY, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIFolder_DeleteMessages_Scodes[] = 
{ 
    MAPI_E_SUBMITTED, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIFolder_CreateFolder_Scodes[] = 
{ 
    E_NOINTERFACE, 
    MAPI_E_COLLISION, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIFolder_CopyFolder_Scodes[] = 
{ 
    E_NOINTERFACE, 
    MAPI_E_COLLISION, 
    MAPI_E_FOLDER_CYCLE, 
    MAPI_E_DECLINE_COPY, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIFolder_DeleteFolder_Scodes[] = 
{ 
    MAPI_E_HAS_FOLDERS, 
    MAPI_E_HAS_MESSAGES, 
    MAPI_E_SUBMITTED, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIFolder_SetSearchCriteria_Scodes[] = 
{ 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIFolder_GetSearchCriteria_Scodes[] = 
{ 
    MAPI_E_NOT_INITIALIZED, 
    MAPI_E_CORRUPT_STORE, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIFolder_SetReadFlags_Scodes[] = 
{ 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIFolder_GetMessageStatus_Scodes[] = 
{ 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIFolder_SetMessageStatus_Scodes[] = 
{ 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMAPIFolder_EmptyFolder_Scodes[] = 
{ 
    MAPI_E_SUBMITTED, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMessage_GetAttachmentTable_Scodes[] = 
{ 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMessage_OpenAttach_Scodes[] = 
{ 
    MAPI_E_NOT_FOUND, 
    E_NOINTERFACE, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMessage_CreateAttach_Scodes[] = 
{ 
    E_NOINTERFACE, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMessage_DeleteAttach_Scodes[] = 
{ 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMessage_GetRecipientTable_Scodes[] = 
{ 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMessage_ModifyRecipients_Scodes[] = 
{ 
    MAPI_E_NOT_FOUND, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMessage_SubmitMessage_Scodes[] = 
{ 
    MAPI_E_NO_RECIPIENTS, 
    MAPI_E_NON_STANDARD, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMessage_SetReadFlag_Scodes[] = 
{ 
    S_OK 
}; 
 
SCODE BASED_DEBUG IAttach_SaveChanges_Scodes[] = 
{ 
    S_OK 
}; 
 
SCODE BASED_DEBUG IAddrBook_OpenEntry_Scodes[] = 
{ 
    STANDARD_OPENENTRY_SCODES, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IAddrBook_CompareEntryIDs_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IAddrBook_CreateOneOff_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IAddrBook_ResolveName_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IAddrBook_Address_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IAddrBook_Details_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IAddrBook_RecipOptions_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IAddrBook_QueryDefaultRecipOpt_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IAddrBook_ButtonPress_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IABContainer_GetContentsTable_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IABContainer_GetHierarchyTable_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG INotifObj_ChangeEvMask_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPIStatus_ChangePassword_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPIStatus_FlushQueues_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPIStatus_SettingsDialog_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG IMAPIStatus_ValidateState_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG SMAPI_MAPILogon_Scodes[] = { 
    MAPI_E_LOGON_FAILED, 
    S_OK}; 
SCODE BASED_DEBUG SMAPI_MAPILogoff_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG SMAPI_MAPIFreeBuffer_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG SMAPI_MAPISendMail_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG SMAPI_MAPISendDocuments_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG SMAPI_MAPIFindNext_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG SMAPI_MAPIReadMail_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG SMAPI_MAPISaveMail_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG SMAPI_MAPIDeleteMail_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG SMAPI_MAPIAddress_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG SMAPI_MAPIResolveName_Scodes[] = {S_OK}; 
SCODE BASED_DEBUG SMAPI_MAPIDetails_Scodes[] = {S_OK}; 
 
SCODE BASED_DEBUG IMSProvider_Logon_Scodes[] = { 
    MAPI_E_UNCONFIGURED, 
    MAPI_E_FAILONEPROVIDER, 
    MAPI_E_STRING_TOO_LONG, 
    MAPI_E_LOGON_FAILED, 
    MAPI_E_CORRUPT_STORE, 
    MAPI_E_USER_CANCEL, 
    S_OK}; 
SCODE BASED_DEBUG IMSProvider_Deinit_Scodes[] = { 
    S_OK}; 
SCODE BASED_DEBUG IMSProvider_Shutdown_Scodes[] = { 
    S_OK}; 
 
SCODE BASED_DEBUG IMSProvider_Init_Scodes[] = { 
    MAPI_E_VERSION, 
    S_OK}; 
SCODE BASED_DEBUG IMSProvider_SpoolerLogon_Scodes[] = { 
    MAPI_E_LOGON_FAILED, 
    S_OK}; 
 
SCODE BASED_DEBUG IMSLogon_OpenEntry_Scodes[] = 
{ 
    STANDARD_OPENENTRY_SCODES, 
    S_OK 
}; 
 
SCODE BASED_DEBUG IMSLogon_OpenStatusEntry_Scodes[] = { 
    S_OK}; 
 
SCODE BASED_DEBUG IMSLogon_CompareEntryIDs_Scodes[] = { 
    S_OK}; 
 
SCODE BASED_DEBUG IMSLogon_Advise_Scodes[] = { 
    S_OK}; 
SCODE BASED_DEBUG IMSLogon_Unadvise_Scodes[] = { 
    S_OK}; 
SCODE BASED_DEBUG IMSLogon_Logoff_Scodes[] = { 
    S_OK}; 
#endif 
 
/* DBGMEM ------------------------------------------------------------------- */ 
 
#undef  INTERFACE 
#define INTERFACE struct _DBGMEM 
DECLARE_INTERFACE(DBGMEM_) 
{ 
    BEGIN_INTERFACE 
    STDMETHOD(QueryInterface)       (THIS_ REFIID riid, LPVOID FAR* ppvObj) PURE; \ 
    STDMETHOD_(ULONG,AddRef)        (THIS) PURE; \ 
    STDMETHOD_(ULONG,Release)       (THIS) PURE; \ 
    STDMETHOD_(void FAR*, Alloc)    (THIS_ ULONG cb) PURE; \ 
    STDMETHOD_(void FAR*, Realloc)  (THIS_ void FAR* pv, ULONG cb) PURE; \ 
    STDMETHOD_(void, Free)          (THIS_ void FAR* pv) PURE; \ 
    STDMETHOD_(ULONG, GetSize)      (THIS_ void FAR* pv) PURE; \ 
    STDMETHOD_(int, DidAlloc)       (THIS_ void FAR* pv) PURE; \ 
    STDMETHOD_(void, HeapMinimize)  (THIS) PURE; \ 
}; 
 
extern DBGMEM_Vtbl vtblDBGMEM; 
 
typedef struct _DBGMEM  DBGMEM,  FAR *PDBGMEM; 
typedef struct _BLK     BLK,     *PBLK; 
typedef struct _BLK UNALIGNED * PUABLK; 
typedef struct _BLKTAIL BLKTAIL, *PBLKTAIL; 
 
struct _DBGMEM { 
    DBGMEM_Vtbl *       lpVtbl; 
    ULONG               cRef; 
    LPMALLOC            pmalloc; 
    char                szSubsys[16]; 
    ULONG               ulAllocNum; 
    ULONG               ulAllocAt; 
    ULONG               ulFailureAt; 
    BOOL                fCheckOften; 
    BOOL                fUnleakable; 
    ULONG               cbVirtual; 
    BOOL                fFillRandom; 
    int                 cbExtra; 
    int                 cbTail; 
    PBLK                pblkHead; 
#if defined(_WIN32) && defined(_X86_) 
    CRITICAL_SECTION    cs; 
#endif 
}; 
 
#define NCALLERS    12 
 
struct _BLK { 
    PDBGMEM         pdbgmem;        /* pointer to the allocator */ 
    PBLK            pblkNext;       /* next link in chain of allocated blocks */ 
    PBLK            pblkPrev;       /* prev link in chain of allocated blocks */ 
    ULONG           ulAllocNum;     /* internal allocation number */ 
    BOOL            fUnleakable;    /* TRUE if leak code should ignore block */ 
    #if defined(_WIN32) && defined(_X86_) 
    FARPROC         pfnCallers[NCALLERS]; 
    #endif 
    PBLKTAIL        pblktail;       /* pointer to block tail */ 
}; 
 
struct _BLKTAIL { 
    PBLK            pblk;           /* pointer back to beginning of the block */ 
}; 
 
#define PblkToPv(pblk)          ((LPVOID)((PBLK)(pblk) + 1)) 
#define PvToPblk(pblk)          ((PBLK)(pv) - 1) 
#define PblkClientSize(pblk)    ((ULONG)((char *)(pblk)->pblktail - (char *)PblkToPv(pblk))) 
#define PblkAllocSize(pblk)     (PblkClientSize(pblk) + sizeof(BLK) + (pblk)->pdbgmem->cbTail) 
 
#if defined(_WIN32) && defined(_X86_) 
#define DBGMEM_EnterCriticalSection(pdbgmem)    \ 
        EnterCriticalSection(&(pdbgmem)->cs) 
#define DBGMEM_LeaveCriticalSection(pdbgmem)    \ 
        LeaveCriticalSection(&(pdbgmem)->cs) 
#else 
#define DBGMEM_EnterCriticalSection(pdbgmem) 
#define DBGMEM_LeaveCriticalSection(pdbgmem) 
#endif 
 
#define INITGUID 
#include <initguid.h> 
 
DEFINE_OLEGUID(DBGMEM_IID_IUnknown,     0x00000000L, 0, 0); 
DEFINE_OLEGUID(DBGMEM_IID_IMalloc,      0x00000002L, 0, 0); 
DEFINE_OLEGUID(DBGMEM_IID_IBaseMalloc,  0x000203FFL, 0, 0); 
 
/* Forward Declarations ----------------------------------------------------- */ 
 
BOOL DBGMEM_ValidatePblk(PDBGMEM pdbgmem, PBLK pblk, char ** pszReason); 
BOOL DBGMEM_ValidatePv(PDBGMEM pdbgmem, void * pv, char * pszFunc); 
STDMETHODIMP_(void) DBGMEM_Free(PDBGMEM pdbgmem, void * pv); 
 
/* Call Stack (_WIN32) ------------------------------------------------------- */ 
 
#if defined(_WIN32) && defined(_X86_) 
 
#ifdef _WIN95 
#define dwStackLimit    0x00400000      /*  4MB for Windows 95 */ 
#else 
#define dwStackLimit    0x00010000      /*  64KB for NT */ 
#endif 
 
void EXPORTDBG __cdecl GetCallStack(DWORD *pdwCaller, int cSkip, int cFind) 
{ 
    DWORD * pdwStack; 
    DWORD * pdwStackPrev = (DWORD *)0; 
    DWORD   dwCaller; 
 
    __asm mov pdwStack, ebp 
 
    memset(pdwCaller, 0, cFind * sizeof(DWORD)); 
 
    while (cSkip + cFind > 0) 
    { 
        pdwStack = (DWORD *)*pdwStack; 
 
        if (    pdwStack <= (DWORD *)dwStackLimit 
            ||  pdwStackPrev >= pdwStack 
            ||  IsBadReadPtr(pdwStack, 2 * sizeof(DWORD))) 
            break; 
 
        dwCaller = *(pdwStack + 1); 
 
        if (dwCaller <= dwStackLimit) 
            break; 
        else if (cSkip > 0) 
            cSkip -= 1; 
        else 
        { 
            *pdwCaller++ = dwCaller; 
            cFind -= 1; 
 
            pdwStackPrev = pdwStack; 
        } 
    } 
} 
 
#endif 
 
/* Virtual Memory Support (_WIN32) ------------------------------------------- */ 
 
#if defined(_WIN32) && (defined(_X86_) || defined(_PPC_) || defined(_MIPS_)) 
 
#define PAGE_SIZE       4096 
#define PvToVMBase(pv)  ((void *)((ULONG)pv & 0xFFFF0000)) 
 
BOOL VMValidatePvEx(void *pv, ULONG cbCluster) 
{ 
    void *  pvBase; 
    BYTE *  pb; 
 
    pvBase = PvToVMBase(pv); 
 
    pb = (BYTE *)pvBase + sizeof(ULONG); 
 
    while (pb < (BYTE *)pv) { 
        if (*pb++ != 0xAD) { 
            TrapSz1("VMValidatePvEx(pv=%08lX): Block leader has been overwritten", pv); 
            return(FALSE); 
        } 
    } 
 
    if (cbCluster != 1) 
    { 
        ULONG cb = *((ULONG *)pvBase); 
        ULONG cbPad = 0; 
 
        if (cb % cbCluster) 
            cbPad = (cbCluster - (cb % cbCluster)); 
 
        if (cbPad) 
        { 
            BYTE *pbMac; 
 
            pb = (BYTE *)pv + cb; 
            pbMac = pb + cbPad; 
 
            while (pb < pbMac) 
            { 
                if (*pb++ != 0xBC) 
                { 
                    TrapSz1("VMValidatePvEx(pv=%08lX): Block trailer has been " 
                        "overwritten", pv); 
                    return(FALSE); 
                } 
            } 
        } 
    } 
 
    return(TRUE); 
} 
 
void * EXPORTDBG __cdecl VMAlloc(ULONG cb) 
{ 
    return VMAllocEx(cb, 1); 
} 
 
void * EXPORTDBG __cdecl VMAllocEx(ULONG cb, ULONG cbCluster) 
{ 
    ULONG   cbAlloc; 
    void *  pvR; 
    void *  pvC; 
    ULONG   cbPad   = 0; 
 
    // a cluster size of 0 means don't use the virtual allocator. 
 
    AssertSz(cbCluster != 0, "Cluster size is zero."); 
 
    if (cb > 0x100000) 
        return(0); 
 
    if (cb % cbCluster) 
        cbPad = (cbCluster - (cb % cbCluster)); 
 
    cbAlloc = sizeof(ULONG) + cb + cbPad + PAGE_SIZE - 1; 
    cbAlloc -= cbAlloc % PAGE_SIZE; 
    cbAlloc += PAGE_SIZE; 
 
    pvR = VirtualAlloc(0, cbAlloc, MEM_RESERVE, PAGE_NOACCESS); 
 
    if (pvR == 0) 
        return(0); 
 
    pvC = VirtualAlloc(pvR, cbAlloc - PAGE_SIZE, MEM_COMMIT, PAGE_READWRITE); 
 
    if (pvC != pvR) 
    { 
        VirtualFree(pvR, 0, MEM_RELEASE); 
        return(0); 
    } 
 
    *(ULONG *)pvC = cb; 
 
    memset((BYTE *)pvC + sizeof(ULONG), 0xAD, 
        (UINT) cbAlloc - cb - cbPad - sizeof(ULONG) - PAGE_SIZE); 
 
    if (cbPad) 
        memset((BYTE *)pvC + cbAlloc - PAGE_SIZE - cbPad, 0xBC, 
            (UINT) cbPad); 
 
    return((BYTE *)pvC + (cbAlloc - cb - cbPad - PAGE_SIZE)); 
} 
 
void EXPORTDBG __cdecl VMFree(void *pv) 
{ 
    VMFreeEx(pv, 1); 
} 
 
void EXPORTDBG __cdecl VMFreeEx(void *pv, ULONG cbCluster) 
{ 
    VMValidatePvEx(pv, cbCluster); 
 
    if (!VirtualFree(PvToVMBase(pv), 0, MEM_RELEASE)) 
        TrapSz2("VMFreeEx(pv=%08lX): VirtualFree failed (%08lX)", 
            pv, GetLastError()); 
} 
 
void * EXPORTDBG __cdecl VMRealloc(void *pv, ULONG cb) 
{ 
    return VMReallocEx(pv, cb, 1); 
} 
 
void * EXPORTDBG __cdecl VMReallocEx(void *pv, ULONG cb, ULONG cbCluster) 
{ 
    void *  pvNew = 0; 
    ULONG   cbCopy; 
 
    VMValidatePvEx(pv, cbCluster); 
 
    cbCopy = *(ULONG *)PvToVMBase(pv); 
    if (cbCopy > cb) 
        cbCopy = cb; 
 
    pvNew = VMAllocEx(cb, cbCluster); 
 
    if (pvNew) 
    { 
        MemCopy(pvNew, pv, cbCopy); 
        VMFreeEx(pv, cbCluster); 
    } 
 
    return(pvNew); 
} 
 
ULONG EXPORTDBG __cdecl VMGetSize(void *pv) 
{ 
    return VMGetSizeEx(pv, 1); 
} 
 
ULONG EXPORTDBG __cdecl VMGetSizeEx(void *pv, ULONG cbCluster) 
{ 
    return(*(ULONG *)PvToVMBase(pv)); 
} 
 
#endif 
 
/* Virtual Memory Support (WIN16) ------------------------------------------- */ 
 
#ifdef WIN16 
 
#define PvToVMBase(pv)  ((void *)((ULONG)pv & 0xFFFF0000)) 
 
BOOL VMValidatePvEx(void *pv, ULONG cbCluster) 
{ 
    void *  pvBase; 
    BYTE *  pb; 
 
    pvBase = PvToVMBase(pv); 
 
    pb = (BYTE *)pvBase + sizeof(ULONG); 
 
    while (pb < (BYTE *)pv) { 
        if (*pb++ != 0xAD) { 
            TrapSz1("VMValidatePvEx(pv=%08lX): Block leader has been overwritten", pv); 
            return(FALSE); 
        } 
    } 
 
    if (cbCluster != 1) 
    { 
        ULONG cb = *((ULONG *)pvBase); 
        ULONG cbPad = 0; 
 
        if (cb % cbCluster) 
            cbPad = (cbCluster - (cb % cbCluster)); 
 
        if (cbPad) 
        { 
            BYTE *pbMac; 
 
            pb = (BYTE *)pv + cb; 
            pbMac = pb + cbPad; 
 
            while (pb < pbMac) 
            { 
                if (*pb++ != 0xBC) 
                { 
                    TrapSz1("VMValidatePvEx(pv=%08lX): Block trailer has been " 
                        "overwritten", pv); 
                    return(FALSE); 
                } 
            } 
        } 
    } 
 
    return(TRUE); 
} 
 
BOOL VMValidatePv(void *pv) 
{ 
    return VMValidatePvEx(pv, 1); 
} 
 
void * EXPORTDBG __cdecl VMAlloc(ULONG cb) 
{ 
    return VMAllocEx(cb, 1); 
} 
 
void * EXPORTDBG __cdecl VMAllocEx(ULONG cb, ULONG cbCluster) 
{ 
    HGLOBAL hGlobal; 
    ULONG   cbAlloc; 
    ULONG   cbAllocFromSys; 
    void *  pvAlloc; 
    ULONG   cbPad   = 0; 
 
    if (cb > 0x10000 - sizeof(ULONG)) 
        return(0); 
 
    if (cb % cbCluster) 
        cbPad = (cbCluster - (cb % cbCluster)); 
 
    cbAlloc = sizeof(ULONG) + cb + cbPad; 
 
    if (cbAlloc > 0x10000) 
        return(0); 
 
#ifdef SIMPLE_MAPI 
    hGlobal = GlobalAlloc(GPTR | GMEM_SHARE, cbAlloc); 
#else    
    hGlobal = GlobalAlloc(GPTR, cbAlloc); 
#endif   
 
    if (hGlobal == 0) 
        return(0); 
 
    cbAllocFromSys = GlobalSize(hGlobal); 
 
    Assert(cbAllocFromSys >= cbAlloc); 
 
    cbAlloc = cbAllocFromSys; 
 
    pvAlloc = GlobalLock(hGlobal); 
 
    if (pvAlloc == 0) { 
        GlobalFree(hGlobal); 
        return(0); 
    } 
 
    Assert(((ULONG)pvAlloc & 0x0000FFFF) == 0); 
 
    *(ULONG *)pvAlloc = cb; 
 
    memset((BYTE *)pvAlloc + sizeof(ULONG), 0xAD, 
        (size_t)(cbAlloc - cb - cbPad - sizeof(ULONG))); 
 
    if (cbPad) 
        memset((BYTE *)pvAlloc + cbAlloc - cbPad, 0xBC, (size_t) cbPad); 
 
    return((BYTE *)pvAlloc + (cbAlloc - cb - cbPad)); 
} 
 
void EXPORTDBG __cdecl VMFree(void *pv) 
{ 
    VMFreeEx(pv, 1); 
} 
 
void EXPORTDBG __cdecl VMFreeEx(void *pv, ULONG cbCluster) 
{ 
    if (VMValidatePvEx(pv, cbCluster)) 
    { 
        HGLOBAL hGlobal; 
        ULONG cb = *(ULONG *)PvToVMBase(pv); 
 
        memset(pv, 0xFE, (size_t)cb); 
 
        hGlobal = (HGLOBAL)((ULONG)pv >> 16); 
        GlobalFree(hGlobal); 
    } 
} 
 
void * EXPORTDBG __cdecl VMRealloc(void *pv, ULONG cb) 
{ 
    return VMReallocEx(pv, cb, 1); 
} 
 
void * EXPORTDBG __cdecl VMReallocEx(void *pv, ULONG cb, ULONG cbCluster) 
{ 
    void *  pvNew = 0; 
    ULONG   cbCopy; 
 
    if (VMValidatePvEx(pv, cbCluster)) { 
        cbCopy = *(ULONG *)PvToVMBase(pv); 
        if (cbCopy > cb) 
            cbCopy = cb; 
 
        pvNew = VMAllocEx(cb, cbCluster); 
 
        if (pvNew) { 
            MemCopy(pvNew, pv, (size_t)cbCopy); 
            VMFreeEx(pv, cbCluster); 
        } 
    } 
 
    return(pvNew); 
} 
 
ULONG EXPORTDBG __cdecl VMGetSize(void *pv) 
{ 
    return VMGetSizeEx(pv, 1); 
} 
 
ULONG EXPORTDBG __cdecl VMGetSizeEx(void *pv, ULONG ulCluster) 
{ 
    if (VMValidatePvEx(pv, ulCluster)) 
        return(*(ULONG *)PvToVMBase(pv)); 
 
    return(0); 
} 
 
#endif 
 
/* Virtual Memory Support (Others) ------------------------------------------ */ 
/* 
 *  The VM Allocators do not currently work on: 
 *      ALPHA 
 *      MAC 
 */ 
#if defined(MAC) || defined(_ALPHA_) 
#define VMAlloc(cb)             0 
#define VMAllocEx(cb, ul)       0 
#define VMRealloc(pv, cb)       0 
#define VMReallocEx(pv, cb, ul) 0 
#define VMFree(pv) 
#define VMFreeEx(pv, ul) 
#define VMGetSize(pv)           0 
#define VMGetSizeEx(pv, ul)     0 
#endif 
 
/* PblkEnqueue / PblkDequeue ------------------------------------------------ */ 
 
void PblkEnqueue(PBLK pblk) 
{ 
    pblk->pblkNext          = pblk->pdbgmem->pblkHead; 
    pblk->pblkPrev          = 0; 
    pblk->pdbgmem->pblkHead = pblk; 
 
    if (pblk->pblkNext) 
        pblk->pblkNext->pblkPrev = pblk; 
 
} 
 
void PblkDequeue(PBLK pblk) 
{ 
    if (pblk->pblkNext) 
        pblk->pblkNext->pblkPrev = pblk->pblkPrev; 
 
    if (pblk->pblkPrev) 
        pblk->pblkPrev->pblkNext = pblk->pblkNext; 
    else 
        pblk->pdbgmem->pblkHead  = pblk->pblkNext; 
} 
 
/* QueryInterface/AddRef/Release -------------------------------------------- */ 
 
STDMETHODIMP DBGMEM_QueryInterface(PDBGMEM pdbgmem, REFIID riid, LPVOID FAR* ppvObj) 
{ 
    if (memcmp(riid, &DBGMEM_IID_IBaseMalloc, sizeof(IID)) == 0) { 
        pdbgmem->pmalloc->lpVtbl->AddRef(pdbgmem->pmalloc); 
        *ppvObj = pdbgmem->pmalloc; 
        return(0); 
    } 
 
    if (memcmp(riid, &DBGMEM_IID_IMalloc, sizeof(IID)) == 0 || 
        memcmp(riid, &DBGMEM_IID_IUnknown, sizeof(IID)) == 0) { 
        ++pdbgmem->cRef; 
        *ppvObj = pdbgmem; 
        return(0); 
    } 
 
    *ppvObj = NULL; /* OLE requires zeroing [out] parameter */ 
    return(ResultFromScode(E_NOINTERFACE)); 
} 
 
STDMETHODIMP_(ULONG) DBGMEM_AddRef(PDBGMEM pdbgmem) 
{ 
    ULONG cRef; 
 
    DBGMEM_EnterCriticalSection(pdbgmem); 
    cRef = ++pdbgmem->cRef; 
    DBGMEM_LeaveCriticalSection(pdbgmem); 
 
    return(cRef); 
} 
 
STDMETHODIMP_(ULONG) DBGMEM_Release(PDBGMEM pdbgmem) 
{ 
    ULONG       cRef; 
    LPMALLOC    pmalloc; 
 
    DBGMEM_EnterCriticalSection(pdbgmem); 
    cRef = --pdbgmem->cRef; 
    DBGMEM_LeaveCriticalSection(pdbgmem); 
 
    if (cRef == 0) { 
        DBGMEM_CheckMemFn(pdbgmem, TRUE); 
        pmalloc = pdbgmem->pmalloc; 
        pdbgmem->lpVtbl = 0; 
        #if defined(_WIN32) && defined(_X86_) 
        DeleteCriticalSection(&pdbgmem->cs); 
        #endif 
        pmalloc->lpVtbl->Free(pmalloc, pdbgmem); 
        pmalloc->lpVtbl->Release(pmalloc); 
    } 
 
    return(cRef); 
} 
 
/* IMalloc::Alloc ----------------------------------------------------------- */ 
 
STDMETHODIMP_(void FAR *) DBGMEM_Alloc(PDBGMEM pdbgmem, ULONG cb) 
{ 
    PBLK    pblk; 
    ULONG   cbAlloc; 
    LPVOID  pvAlloc = 0; 
    BYTE    bFill   = 0xFA; 
 
    DBGMEM_EnterCriticalSection(pdbgmem); 
 
    if (pdbgmem->fCheckOften) 
        DBGMEM_CheckMemFn(pdbgmem, FALSE); 
 
    cbAlloc = sizeof(BLK) + cb + pdbgmem->cbTail; 
 
    if (pdbgmem->ulFailureAt != 0) 
    { 
        if (pdbgmem->ulFailureAt != pdbgmem->ulAllocAt) 
            ++pdbgmem->ulAllocAt; 
        else 
            cbAlloc = 0; 
    } 
 
    if (cbAlloc < cb) 
        pblk = 0; 
    else if (pdbgmem->cbVirtual) 
        pblk = VMAllocEx(cbAlloc, pdbgmem->cbVirtual); 
    else 
        pblk = (PBLK)pdbgmem->pmalloc->lpVtbl->Alloc(pdbgmem->pmalloc, cbAlloc); 
 
    if (pblk) { 
        pblk->pdbgmem       = pdbgmem; 
        pblk->ulAllocNum    = ++pdbgmem->ulAllocNum; 
        pblk->fUnleakable   = FALSE; 
        pblk->pblktail      = (PBLKTAIL)((char *)pblk + sizeof(BLK) + cb); 
 
        if (!pdbgmem->cbVirtual) 
            *((PUABLK UNALIGNED * ) 
            &((struct _BLKTAIL UNALIGNED *) pblk->pblktail)->pblk) = pblk; 
 
        PblkEnqueue(pblk); 
 
        #if defined(_WIN32) && defined(_X86_) 
        GetCallStack((DWORD *)pblk->pfnCallers, 0, NCALLERS); 
        #endif 
 
        if (pdbgmem->fCheckOften) 
            DBGMEM_CheckMemFn(pdbgmem, FALSE); 
 
        pvAlloc = PblkToPv(pblk); 
 
        if (pdbgmem->fFillRandom) 
            bFill = (BYTE)pblk->ulAllocNum; 
 
        memset(pvAlloc, bFill, (size_t)cb); 
 
        if (pdbgmem->cbExtra) 
            memset(pblk->pblktail + 1, 0xAE, pdbgmem->cbExtra * sizeof(ULONG)); 
    } 
 
    DBGMEM_LeaveCriticalSection(pdbgmem); 
 
    return(pvAlloc); 
} 
 
/* IMalloc::Realloc --------------------------------------------------------- */ 
 
STDMETHODIMP_(void FAR *) DBGMEM_Realloc(PDBGMEM pdbgmem, void FAR* pv, ULONG cb) 
{ 
    ULONG   cbAlloc; 
    LPVOID  pvAlloc = 0; 
    BYTE    bFill = 0xFA; 
 
    DBGMEM_EnterCriticalSection(pdbgmem); 
 
    if (pdbgmem->fCheckOften) 
        DBGMEM_CheckMemFn(pdbgmem, FALSE); 
 
    if (pv == 0) { 
        TrapSz1("DBGMEM_Realloc(pv=NULL,cb=%ld): IMalloc::Realloc is being used allocate a new memory block.  Explicit use of IMalloc::Alloc is preferred.", cb); 
        pvAlloc = DBGMEM_Alloc(pdbgmem, cb); 
    } else if (cb == 0) { 
        TrapSz1("DBGMEM_Realloc(pv=%08lX,cb=0): IMalloc::Realloc is being used to free a memory block.  Explicit use of IMalloc::Free is preferred.", pv); 
        DBGMEM_Free(pdbgmem, pv); 
        pvAlloc = 0; 
    } else if (DBGMEM_ValidatePv(pdbgmem, pv, "DBGMEM_Realloc")) { 
        PBLK    pblk    = PvToPblk(pv); 
        ULONG   cbOld   = PblkClientSize(pblk); 
        PBLK    pblkNew; 
 
        PblkDequeue(pblk); 
 
        cbAlloc = sizeof(BLK) + cb + pdbgmem->cbTail; 
 
        if (pdbgmem->ulFailureAt != 0) 
        { 
            if (pdbgmem->ulFailureAt != pdbgmem->ulAllocAt) 
                ++pdbgmem->ulAllocAt; 
            else 
                cbAlloc = 0; 
        } 
 
        if (cbAlloc < cb) 
            pblkNew = 0; 
        else if (pdbgmem->cbVirtual) 
            pblkNew = (PBLK)VMReallocEx(pblk, cbAlloc, pdbgmem->cbVirtual); 
        else 
            pblkNew = (PBLK)pdbgmem->pmalloc->lpVtbl->Realloc(pdbgmem->pmalloc, pblk, cbAlloc); 
 
        if (pblkNew == 0) { 
            PblkEnqueue(pblk); 
            pvAlloc = 0; 
        } else { 
            pblkNew->pblktail = (PBLKTAIL)((char *)pblkNew + sizeof(BLK) + cb); 
 
            if (!pdbgmem->cbVirtual) 
                *((PUABLK UNALIGNED * ) 
                &((struct _BLKTAIL UNALIGNED *) pblkNew->pblktail)->pblk) = pblkNew; 
 
            PblkEnqueue(pblkNew); 
 
            pvAlloc = PblkToPv(pblkNew); 
 
            if (pdbgmem->fFillRandom) 
                bFill = (BYTE)pblkNew->ulAllocNum; 
 
            if (cb > cbOld) 
                memset((char *)pvAlloc + cbOld, bFill, (size_t)(cb - cbOld)); 
 
            if (pdbgmem->cbExtra) 
                memset(pblkNew->pblktail + 1, 0xAE, pdbgmem->cbExtra * sizeof(ULONG)); 
        } 
    } 
 
    DBGMEM_LeaveCriticalSection(pdbgmem); 
 
    return(pvAlloc); 
} 
 
/* IMalloc::Free ------------------------------------------------------------ */ 
 
STDMETHODIMP_(void) DBGMEM_Free(PDBGMEM pdbgmem, void FAR * pv) 
{ 
    DBGMEM_EnterCriticalSection(pdbgmem); 
 
    if (pdbgmem->fCheckOften) 
        DBGMEM_CheckMemFn(pdbgmem, FALSE); 
 
    if (pv && DBGMEM_ValidatePv(pdbgmem, pv, "DBGMEM_Free")) { 
        PBLK pblk = PvToPblk(pv); 
 
        PblkDequeue(pblk); 
 
        memset(pblk, 0xDC, (size_t)PblkAllocSize(pblk)); 
 
        if (pdbgmem->cbVirtual) 
            VMFreeEx(pblk, pdbgmem->cbVirtual); 
        else 
            pdbgmem->pmalloc->lpVtbl->Free(pdbgmem->pmalloc, pblk); 
    } 
 
    DBGMEM_LeaveCriticalSection(pdbgmem); 
} 
 
/* IMalloc::GetSize --------------------------------------------------------- */ 
 
STDMETHODIMP_(ULONG) DBGMEM_GetSize(PDBGMEM pdbgmem, void FAR * pv) 
{ 
    ULONG ulResult = (ULONG)(-1); 
 
    DBGMEM_EnterCriticalSection(pdbgmem); 
 
    if (pv == 0) 
        TrapSz("Although technically not an error, I bet you didn't really want to pass a NULL pointer to IMalloc::GetSize, did you?  I hope you can deal with a size of -1, because that's the offical answer.  Good luck."); 
    else if (DBGMEM_ValidatePv(pdbgmem, pv, "DBGMEM_GetSize")) 
        ulResult = PblkClientSize(PvToPblk(pv)); 
 
    DBGMEM_LeaveCriticalSection(pdbgmem); 
 
    return(ulResult); 
} 
 
/* IMalloc::DidAlloc -------------------------------------------------------- */ 
 
STDMETHODIMP_(int) DBGMEM_DidAlloc(PDBGMEM pdbgmem, void FAR * pv) 
{ 
    PBLK    pblk; 
    char *  pszReason; 
    int     iResult = 0; 
 
    DBGMEM_EnterCriticalSection(pdbgmem); 
 
    for (pblk = pdbgmem->pblkHead; pblk; pblk = pblk->pblkNext) 
    { 
        AssertSz2(DBGMEM_ValidatePblk(pdbgmem,pblk,&pszReason)==TRUE, 
                 "Block header (pblk=%08lX) is invalid\n%s", 
                 pblk, pszReason); 
        if (PblkToPv(pblk) == pv) { 
            iResult = 1; 
            break; 
        } 
    } 
 
    DBGMEM_LeaveCriticalSection(pdbgmem); 
 
    return(iResult); 
} 
 
/* IMalloc::HeapMinimize ---------------------------------------------------- */ 
 
STDMETHODIMP_(void) DBGMEM_HeapMinimize(PDBGMEM pdbgmem) 
{ 
    pdbgmem->pmalloc->lpVtbl->HeapMinimize(pdbgmem->pmalloc); 
} 
 
/* DBGMEM_ValidatePblk ------------------------------------------------------ */ 
 
BOOL DBGMEM_ValidatePblk(PDBGMEM pdbgmem, PBLK pblk, char ** pszReason) 
{ 
    #if defined(WIN16) || (defined(_WIN32) && defined(_X86_)) 
    if (IsBadWritePtr(pblk, sizeof(BLK))) { 
        *pszReason = "Block header cannot be written to"; 
        goto err; 
    } 
    #endif 
 
    if (pblk->pdbgmem != pdbgmem) { 
        *pszReason = "Block header does not have correct pointer back to allocator"; 
        goto err; 
    } 
 
    if (pblk->pblkNext) { 
        #if defined(WIN16) || (defined(_WIN32) && defined(_X86_)) 
        if (IsBadWritePtr(pblk->pblkNext, sizeof(BLK))) { 
            *pszReason = "Block header has invalid next link pointer"; 
            goto err; 
        } 
        #endif 
 
        if (pblk->pblkNext->pblkPrev != pblk) { 
            *pszReason = "Block header points to a next block which doesn't point back to it"; 
            goto err; 
        } 
    } 
    if (pblk->pblkPrev) { 
        #if defined(WIN16) || (defined(_WIN32) && defined(_X86_)) 
        if (IsBadWritePtr(pblk->pblkPrev, sizeof(BLK))) { 
            *pszReason = "Block header has invalid prev link pointer"; 
            goto err; 
        } 
        #endif 
 
        if (pblk->pblkPrev->pblkNext != pblk) { 
            *pszReason = "Block header points to a prev block which doesn't point back to it"; 
            goto err; 
        } 
    } else if (pdbgmem->pblkHead != pblk) { 
        *pszReason = "Block header has a zero prev link but the allocator doesn't believe it is the first block"; 
        goto err; 
    } 
 
    if (pblk->ulAllocNum > pdbgmem->ulAllocNum) { 
        *pszReason = "Block header has an invalid internal allocation number"; 
        goto err; 
    } 
 
    if (!pdbgmem->cbVirtual) { 
        #if defined(WIN16) || (defined(_WIN32) && defined(_X86_)) 
        if (IsBadWritePtr(pblk->pblktail, pdbgmem->cbTail)) { 
            *pszReason = "Block header has invalid pblktail pointer"; 
            goto err; 
        } 
        #endif 
 
        if (*((PUABLK UNALIGNED * ) 
            &((struct _BLKTAIL UNALIGNED *) pblk->pblktail)->pblk) != pblk) { 
            *pszReason = "Block trailer does not point back to the block header"; 
            goto err; 
        } 
    } 
 
    if (pdbgmem->cbExtra) { 
        ULONG UNALIGNED * pul = (ULONG UNALIGNED *)(pblk->pblktail + 1); 
        int n = pdbgmem->cbExtra; 
        for (; --n >= 0; ++pul) 
            if (*pul != 0xAEAEAEAE) { 
                *pszReason = "Block trailer spiddle-zone has been overwritten"; 
                goto err; 
            } 
    } 
 
    return(TRUE); 
 
err: 
    return(FALSE); 
} 
 
/* DBGMEM_ValidatePv -------------------------------------------------------- */ 
 
BOOL DBGMEM_ValidatePv(PDBGMEM pdbgmem, void * pv, char * pszFunc) 
{ 
    char *  pszReason; 
 
    if (DBGMEM_DidAlloc(pdbgmem, pv) == 0) { 
        TrapSz3("DBGMEM_ValidatePv(subsys=%s,pv=%08lX) [via %s]\nDetected a memory block which was not allocated by this allocator", 
            pdbgmem->szSubsys, pv, pszFunc); 
        return(FALSE); 
    } 
 
    if (DBGMEM_ValidatePblk(pdbgmem,PvToPblk(pv),&pszReason)) 
        return(TRUE); 
 
    TrapSz4("DBGMEM_ValidatePv(%s,pv=%08lX) [via %s]\n%s", 
        pdbgmem->szSubsys, pv, pszFunc, pszReason); 
 
    return(FALSE); 
} 
 
/* DBGMEM_ReportLeak -------------------------------------------------------- */ 
 
#if defined(_WIN32) && defined(_X86_) 
void EXPORTDBG __cdecl DBGMEM_LeakHook(FARPROC pfn) 
{ 
    /* Dummy function so that you can set a breakpoint with command   */ 
    /* "ln ecx;g", in order to get the debugger to print out the name */ 
    /* of the function which allocated the leaked memory block        */ 
} 
#endif 
 
void DBGMEM_ReportLeak(PDBGMEM pdbgmem, PBLK pblk) 
{ 
    int i = 0; 
 
    DebugTrace("%s Memory Leak: @%08lX, allocation #%ld, size %ld\n", 
        pdbgmem->szSubsys, PblkToPv(pblk), pblk->ulAllocNum, PblkClientSize(pblk)); 
 
    #if defined(_WIN32) && defined(_X86_) 
    for (i = 0; i < NCALLERS && pblk->pfnCallers[i] != 0; i++) { 
        DebugTrace("[%d] %08lX ", i, pblk->pfnCallers[i]); 
        DBGMEM_LeakHook(pblk->pfnCallers[i]); 
    } 
    DebugTrace("\n"); 
    #endif 
} 
 
/* DBGMEM_NoLeakDetectFn ---------------------------------------------------- */ 
 
void EXPORTDBG __cdecl DBGMEM_NoLeakDetectFn(void * pmalloc, void *pv) 
{ 
    PDBGMEM pdbgmem = (PDBGMEM)pmalloc; 
 
    DBGMEM_EnterCriticalSection(pdbgmem); 
 
    if (pv == 0) 
        pdbgmem->fUnleakable = TRUE; 
    else if (DBGMEM_ValidatePv(pdbgmem, pv, "DBGMEM_NoLeakDetectFn")) 
        PvToPblk(pv)->fUnleakable = TRUE; 
 
    DBGMEM_LeaveCriticalSection(pdbgmem); 
} 
 
/* DBGMEM_SetFailureAtFn ---------------------------------------------------- */ 
 
void EXPORTDBG __cdecl DBGMEM_SetFailureAtFn(void * pmalloc, ULONG ulFailureAt) 
{ 
    PDBGMEM pdbgmem = (PDBGMEM)pmalloc; 
 
    DBGMEM_EnterCriticalSection(pdbgmem); 
 
    pdbgmem->ulFailureAt = ulFailureAt; 
 
    DBGMEM_LeaveCriticalSection(pdbgmem); 
} 
 
/* DBGMEM_CheckMemFn -------------------------------------------------------- */ 
 
void EXPORTDBG __cdecl DBGMEM_CheckMemFn(void * pmalloc, BOOL fReportOrphans) 
{ 
    PDBGMEM pdbgmem = (PDBGMEM)pmalloc; 
    PBLK    pblk; 
    int     cLeaks = 0; 
 
    DBGMEM_EnterCriticalSection(pdbgmem); 
 
    for (pblk = pdbgmem->pblkHead; pblk; pblk = pblk->pblkNext) { 
        if (!DBGMEM_ValidatePv(pdbgmem, PblkToPv(pblk), "DBGMEM_CheckMemFn")) 
            break; 
 
        if (fReportOrphans && !pdbgmem->fUnleakable && !pblk->fUnleakable) { 
            DBGMEM_ReportLeak(pdbgmem, pblk); 
            cLeaks += 1; 
        } 
    } 
 
    #if defined(WIN16) || (defined(_WIN32) && defined(_X86_)) 
    if (fAssertLeaks == -1) 
    { 
        fAssertLeaks = GetPrivateProfileIntA(szSectionDebug, szKeyAssertLeaks, 
            0, szDebugIni); 
    } 
    #endif 
 
    if (cLeaks > 0) 
    { 
        #if defined(WIN16) || (defined(_WIN32) && defined(_X86_)) 
        if (fAssertLeaks) 
        { 
            TrapSz3("DBGMEM detected %d memory leak%s in subsystem %s", 
                cLeaks, cLeaks == 1 ? "" : "s", pdbgmem->szSubsys); 
        } 
        else 
        { 
            TraceSz3("DBGMEM detected %d memory leak%s in subsystem %s", 
                cLeaks, cLeaks == 1 ? "" : "s", pdbgmem->szSubsys); 
        } 
        #else 
        TraceSz3("DBGMEM detected %d memory leak%s in subsystem %s", 
            cLeaks, cLeaks == 1 ? "" : "s", pdbgmem->szSubsys); 
        #endif 
    } 
 
    DBGMEM_LeaveCriticalSection(pdbgmem); 
} 
 
/* vtblDBGMEM --------------------------------------------------------------- */ 
 
DBGMEM_Vtbl BASED_DEBUG vtblDBGMEM = 
{ 
    VTABLE_FILL 
    DBGMEM_QueryInterface, 
    DBGMEM_AddRef, 
    DBGMEM_Release, 
    DBGMEM_Alloc, 
    DBGMEM_Realloc, 
    DBGMEM_Free, 
    DBGMEM_GetSize, 
    DBGMEM_DidAlloc, 
    DBGMEM_HeapMinimize 
}; 
 
/* DBGMEM_EncapsulateFn ----------------------------------------------------- */ 
 
void * EXPORTDBG __cdecl DBGMEM_EncapsulateFn(void * pvmalloc, char *pszSubsys, BOOL fCheckOften) 
{ 
    LPMALLOC    pmalloc = (LPMALLOC)pvmalloc; 
    PDBGMEM     pdbgmem; 
    LPMALLOC    pmallocBase; 
    ULONG       cbVirtual = 0; 
    BOOL        fFillRandom = FALSE; 
    HRESULT     hr; 
 
    hr = pmalloc->lpVtbl->QueryInterface(pmalloc, &DBGMEM_IID_IBaseMalloc, &pmallocBase); 
    if (hr) { 
        pmallocBase = pmalloc; 
        pmallocBase->lpVtbl->AddRef(pmallocBase); 
    } 
 
    pdbgmem = (PDBGMEM)pmallocBase->lpVtbl->Alloc(pmallocBase, sizeof(DBGMEM)); 
 
    if (pdbgmem == 0) { 
        TrapSz("DBGMEM: Failed trying to allocate memory for the first time!\n"); 
        return(pmallocBase); 
    } 
 
    #if defined(WIN16) || (defined(_WIN32) && defined(_X86_)) 
    cbVirtual = GetPrivateProfileIntA(szSectionDebug, szKeyUseVirtual, 0, 
        szDebugIni); 
 
    if (cbVirtual != 0 && cbVirtual != 1 && cbVirtual != 4) 
        cbVirtual = 1; 
 
    if (cbVirtual) 
        DebugTrace("DBGMEM: Subsystem '%s' using virtual memory allocator -" 
            " align %d.\n", pszSubsys, cbVirtual); 
 
    if (!fCheckOften) 
        fCheckOften = GetPrivateProfileIntA(szSectionDebug, szKeyCheckOften, 0, 
            szDebugIni); 
 
    fFillRandom = GetPrivateProfileIntA(szSectionDebug, szKeyFillRandom, 0, 
        szDebugIni); 
 
    #endif 
 
    memset(pdbgmem, 0, sizeof(DBGMEM)); 
 
    pdbgmem->lpVtbl         = &vtblDBGMEM; 
    pdbgmem->cRef           = 1; 
    pdbgmem->pmalloc        = pmallocBase; 
    pdbgmem->fCheckOften    = fCheckOften; 
    pdbgmem->fUnleakable    = FALSE; 
    pdbgmem->cbVirtual      = cbVirtual; 
    pdbgmem->fFillRandom    = fFillRandom; 
    pdbgmem->cbExtra        = 0; 
    pdbgmem->ulAllocAt      = 1L; 
    pdbgmem->ulFailureAt    = 0L; 
 
    if (pdbgmem->cbVirtual) 
        pdbgmem->cbTail     = 0; 
    else 
        pdbgmem->cbTail     = sizeof(BLKTAIL) + pdbgmem->cbExtra * sizeof(ULONG); 
 
    lstrcpyn(pdbgmem->szSubsys, pszSubsys, sizeof(pdbgmem->szSubsys)); 
 
    #if defined(_WIN32) && defined(_X86_) 
    InitializeCriticalSection(&pdbgmem->cs); 
    #endif 
 
    return(pdbgmem); 
} 
 
/* DBGMEM_ShutdownFn -------------------------------------------------------- */ 
 
void EXPORTDBG __cdecl DBGMEM_ShutdownFn(void *pvmalloc) 
{ 
    LPMALLOC    pmalloc = (LPMALLOC)pvmalloc; 
    PDBGMEM     pdbgmem = (PDBGMEM)pvmalloc; 
    LPMALLOC    pmallocBase; 
    HRESULT     hr; 
 
    hr = pmalloc->lpVtbl->QueryInterface(pmalloc, &DBGMEM_IID_IBaseMalloc, &pmallocBase); 
    if (hr == 0) { 
        pmallocBase->lpVtbl->Release(pmallocBase); 
        if (pdbgmem->cRef != 1) { 
            TrapSz2("DBGMEM_Shutdown: Expected a cRef of 1; instead have %ld for %s", 
                pdbgmem->cRef, pdbgmem->szSubsys); 
            pdbgmem->cRef = 1; 
        } 
    } 
 
    pmalloc->lpVtbl->Release(pmalloc); 
} 
 
/* -------------------------------------------------------------------------- */ 
 
#endif