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