//+--------------------------------------------------------------------------
//
// Microsoft Windows
// Copyright (C) Microsoft Corporation, 1996 - 1999
//
// File:        alloc.cpp
//
// Contents:    Cert Server debug implementation
//
//---------------------------------------------------------------------------

#include "pch.cpp"

#pragma hdrstop

#include <assert.h>

#define __dwFILE__	__dwFILE_CERTCLIB_ALLOC_CPP__


#if DBG_CERTSRV

#undef FormatMessageW
#undef LocalAlloc
#undef LocalReAlloc
#undef LocalFree

#undef CoTaskMemAlloc
#undef CoTaskMemRealloc
#undef CoTaskMemFree

#undef StringFromCLSID
#undef StringFromIID

#undef SysAllocString
#undef SysReAllocString
#undef SysAllocStringLen
#undef SysReAllocStringLen
#undef SysFreeString
#undef SysAllocStringByteLen
#undef PropVariantClear
#undef VariantClear
#undef VariantChangeType
#undef VariantChangeTypeEx


DWORD g_MemTrack = 0;

#define MTF_UNREGISTERED	0x00000002
#define MTF_ALLOCTRACE		0x00000004
#define MTF_FREETRACE		0x00000008
#define MTF_STACKTRACE		0x00000010

typedef struct _RMALLOC
{
    LONG cAlloc;
    LONG cAllocTotal;
} RMALLOC;

RMALLOC g_armAlloc[CSM_MAX];

#define C_BP_FRAME		16
#define C_BACK_TRACE_CHUNK	100
#define C_MEM_HEADER_CHUNK	100

typedef struct _BACKTRACE
{
    LONG   cAlloc;			// count of outstanding allocations
    LONG   cAllocTotal;			// count of total allocations
    LONG   cbAlloc;			// size of outstanding allocations
    LONG   cbAllocTotal;		// size of total allocations
    ULONG  apCaller[C_BP_FRAME];	// stack trace
} BACKTRACE;

typedef struct _MEMHEADER
{
    DWORD       iBackTrace;	// backtrace index
    VOID const *pvMemory;	// Pointer to memory block allocated
    LONG        cbMemory;	// Size of memory block allocated
    DWORD       Flags;		// Allocator flags
} MEMHEADER;


// critical section around myRegister APIs since they
// operate on global data structures
CRITICAL_SECTION g_critsecRegisterMemory;
BOOL g_fRegisterMemoryCritSecInit = FALSE;



VOID
RegisterMemoryEnterCriticalSection(VOID)
{
    HRESULT hr;
    
    __try
    {
	if (!g_fRegisterMemoryCritSecInit)
	{
	    InitializeCriticalSection(&g_critsecRegisterMemory);
	    g_fRegisterMemoryCritSecInit = TRUE;
	}
	EnterCriticalSection(&g_critsecRegisterMemory);
    }
    __except(hr = myHEXCEPTIONCODE(), EXCEPTION_EXECUTE_HANDLER)
    {
    }
}


VOID
RegisterMemoryLeaveCriticalSection(VOID)
{
    if (g_fRegisterMemoryCritSecInit)
    {
	LeaveCriticalSection(&g_critsecRegisterMemory);
    }
}


BACKTRACE *g_rgbt = NULL;
DWORD g_cbtMax = 0;
DWORD g_cbt = 0;

MEMHEADER *g_rgmh = NULL;
DWORD g_cmhMax = 0;
DWORD g_cmh = 0;


MEMHEADER *
AllocMemHeader()
{
    if (g_cmh >= g_cmhMax)
    {
	DWORD cb = (C_MEM_HEADER_CHUNK + g_cmhMax) * sizeof(g_rgmh[0]);
	MEMHEADER *rgmhT;

	if (NULL == g_rgmh)
	{
	    rgmhT = (MEMHEADER *) LocalAlloc(LMEM_FIXED, cb);
	}
	else
	{
	    rgmhT = (MEMHEADER *) LocalReAlloc(g_rgmh, cb, LMEM_MOVEABLE);
	}
	if (NULL == rgmhT)
	{
	    DBGPRINT((
		DBG_SS_CERTLIB,
		"Error allocating memtrack header\n"));
	    return(NULL);
	}
	g_rgmh = rgmhT;
	g_cmhMax += C_MEM_HEADER_CHUNK;
    }
    return(&g_rgmh[g_cmh++]);
}


MEMHEADER *
LookupMemHeader(
    IN VOID const *pv)
{
    MEMHEADER *pmh;
    MEMHEADER *pmhEnd;

    pmh = g_rgmh;
    pmhEnd = &g_rgmh[g_cmh];

    while (pmh < pmhEnd)
    {
	if (pv == pmh->pvMemory)
	{
	    return(pmh);
	}
	pmh++;
    }
    return(NULL);
}


VOID
FreeMemHeader(
    IN MEMHEADER *pmh)
{
    MEMHEADER *pmhLast;

    assert(1 <= g_cmh);
    pmhLast = &g_rgmh[g_cmh - 1];

    *pmh = *pmhLast;
    g_cmh--;
}


BACKTRACE *
AllocBackTrace(
    OUT DWORD *pibt)
{
    if (g_cbt >= g_cbtMax)
    {
	DWORD cb = (C_BACK_TRACE_CHUNK + g_cbtMax) * sizeof(g_rgbt[0]);
	BACKTRACE *rgbtT;

	if (NULL == g_rgbt)
	{
	    rgbtT = (BACKTRACE *) LocalAlloc(LMEM_FIXED, cb);
	}
	else
	{
	    rgbtT = (BACKTRACE *) LocalReAlloc(g_rgbt, cb, LMEM_MOVEABLE);
	    DBGPRINT((
		DBG_SS_CERTLIB,
		"Realloc'd memtrack backtrace from %x to %x\n",
		g_rgbt,
		rgbtT));
	}
	if (NULL == rgbtT)
	{
	    DBGPRINT((
		DBG_SS_CERTLIB,
		"Error allocating memtrack backtrace\n"));
	    return(NULL);
	}
	g_rgbt = rgbtT;
	g_cbtMax += C_BACK_TRACE_CHUNK;
    }
    *pibt = g_cbt + 1;
    return(&g_rgbt[g_cbt++]);
}


BACKTRACE *
LookupBackTrace(
    IN BACKTRACE *pbtIn,
    OUT DWORD *pibt)
{
    BACKTRACE *pbt;
    BACKTRACE *pbtEnd;

    pbt = g_rgbt;
    pbtEnd = &g_rgbt[g_cbt];

    while (pbt < pbtEnd)
    {
	if (0 == memcmp(pbt->apCaller, pbtIn->apCaller, sizeof(pbt->apCaller)))
	{
	    *pibt = SAFE_SUBTRACT_POINTERS(pbt, g_rgbt) + 1;
	    return(pbt);
	}
	pbt++;
    }
    return(NULL);
}


BACKTRACE *
BackTraceFromIndex(
    IN DWORD ibt)
{
    BACKTRACE *pbt = NULL;

    if (0 == ibt)
    {
	DBGPRINT((DBG_SS_CERTLIB, "BackTraceFromIndex(0)\n"));
    }
    else if (g_cbt < ibt)
    {
	DBGPRINT((
	    DBG_SS_CERTLIB,
	    "BackTraceFromIndex(%u) -- out of range\n",
	    ibt));
    }
    else
    {
	pbt = &g_rgbt[ibt - 1];
    }
    return(pbt);
}


VOID
ReadEnvironmentFlags(VOID)
{
    HRESULT hr;
    DWORD MemTrack;
    DWORD cb;
    DWORD dwDisposition;
    DWORD dwType;
    HKEY hkey = NULL;
    char *pszEnvVar;

    pszEnvVar = getenv(szCERTSRV_MEMTRACK);
    if (NULL != pszEnvVar)
    {
	g_MemTrack = (DWORD) strtol(pszEnvVar, NULL, 16);
    }
    else
    {
	hr = RegOpenKeyEx(
			HKEY_LOCAL_MACHINE,
			wszREGKEYCONFIGPATH,
			0,
			KEY_READ,
			&hkey);
	if (S_OK == hr)
	{
	    cb = sizeof(MemTrack);
	    hr = RegQueryValueEx(
			    hkey,
			    wszREGCERTSRVMEMTRACK,
			    0,
			    &dwType,
			    (BYTE *) &MemTrack,
			    &cb);
	    if (S_OK == hr && REG_DWORD == dwType && sizeof(MemTrack) == cb)
	    {
		g_MemTrack = MemTrack;
	    }
	}
    }

//error:
    if (NULL != hkey)
    {
	RegCloseKey(hkey);
    }
}


VOID
CaptureStackBackTrace(
    EXCEPTION_POINTERS *pep,
    ULONG cSkip,
    ULONG cFrames,
    ULONG *aeip)
{
    ZeroMemory(aeip, cFrames * sizeof(aeip[0]));

#if i386 == 1
    ULONG ieip, *pebp;
    ULONG *pebpMax = (ULONG *) MAXLONG; // 2 * 1024 * 1024 * 1024; // 2 gig - 1
    ULONG *pebpMin = (ULONG *) (64 * 1024);	// 64k

    if (pep == NULL)
    {
        ieip = 0;
        cSkip++;                    // always skip current frame
        pebp = ((ULONG *) &pep) - 2;
    }
    else
    {
        ieip = 1;
        assert(cSkip == 0);
        aeip[0] = pep->ContextRecord->Eip;
        pebp = (ULONG *) pep->ContextRecord->Ebp;
    }
    if (pebp >= pebpMin && pebp < pebpMax)
    {
        __try
        {
            for ( ; ieip < cSkip + cFrames; ieip++)
            {
                if (ieip >= cSkip)
                {
                    aeip[ieip - cSkip] = *(pebp + 1);  // save an eip
                }

                ULONG *pebpNext = (ULONG *) *pebp;
                if (pebpNext < pebp + 2 ||
		    pebpNext >= pebpMax - 1 ||
		    pebpNext >= pebp + (256 * 1024) / sizeof(pebp[0]))
                {
                    break;
                }
                pebp = pebpNext;
            }
        }
        __except(EXCEPTION_EXECUTE_HANDLER)
        {
            ;
        }
    }
#endif // i386 == 1
}


WCHAR const *
wszAllocator(
    IN DWORD Flags)
{
    WCHAR const *pwsz;

    switch (Flags)
    {
	case CSM_LOCALALLOC:	pwsz = L"LocalAlloc";	  break;
	case CSM_COTASKALLOC:	pwsz = L"CoTaskMemAlloc"; break;
	case CSM_SYSALLOC:	pwsz = L"SysAllocString"; break;
	case CSM_MALLOC:	pwsz = L"malloc";	  break;
	case CSM_NEW:		pwsz = L"new";		  break;
	case CSM_NEW | CSM_GLOBALDESTRUCTOR:
				pwsz = L"new-global";	  break;
	default:		pwsz = L"???";		  break;
    }
    return(pwsz);
}


WCHAR const *
wszFreeer(
    IN DWORD Flags)
{
    WCHAR const *pwsz;

    switch (Flags)
    {
	case CSM_LOCALALLOC:	pwsz = L"LocalFree";	 break;
	case CSM_COTASKALLOC:	pwsz = L"CoTaskMemFree"; break;
	case CSM_SYSALLOC:	pwsz = L"SysFreeString"; break;
	case CSM_MALLOC:	pwsz = L"free";		 break;
	case CSM_NEW:		pwsz = L"delete";	 break;
	case CSM_NEW | CSM_GLOBALDESTRUCTOR:
				pwsz = L"delete-global"; break;
	default:		pwsz = L"???";		 break;
    }
    return(pwsz);
}


VOID
DumpMemBlock(
    IN WCHAR const *pwsz,
    IN VOID const *pvMemory,
    IN DWORD cbMemory,
    IN DWORD Flags,
    IN DWORD ibt,
    OPTIONAL IN BACKTRACE const *pbt)
{
    DBGPRINT((
	DBG_SS_CERTLIB,
	"%ws%wspv=%-6x cb=%-4x f=%x(%ws) pbt[%d]=%x:\n",
	pwsz,
	L'\0' != *pwsz? L": " : L"",
	pvMemory,
	cbMemory,
	Flags,
	wszAllocator(Flags),
	ibt,
	pbt));

    if (NULL != pbt && DbgIsSSActive(DBG_SS_CERTLIB))
    {
	DBGPRINT((MAXDWORD, "%d: ", ibt));

        for (int i = 0; i < ARRAYSIZE(pbt->apCaller); i++)
        {
            if (NULL == pbt->apCaller[i])
	    {
                break;
	    }
            DBGPRINT((MAXDWORD, "ln %x;", pbt->apCaller[i]));
        }
        DBGPRINT((MAXDWORD, "\n"));
    }
}


VOID
myRegisterMemDump()
{
    MEMHEADER *pmh;
    MEMHEADER *pmhEnd;
    LONG cTotal;
    LONG cbTotal;

    cTotal = 0;
    cbTotal = 0;

    RegisterMemoryEnterCriticalSection();

    __try
    {
        pmh = g_rgmh;
        pmhEnd = &g_rgmh[g_cmh];

        while (pmh < pmhEnd)
        {
	    if (0 == (CSM_GLOBALDESTRUCTOR & pmh->Flags) ||
		(MTF_ALLOCTRACE & g_MemTrack))
	    {
		if (0 == cTotal)
		{
		    if (DbgIsSSActive(DBG_SS_CERTLIB))
		    {
			DBGPRINT((MAXDWORD, "\n"));
		    }
		    DBGPRINT((DBG_SS_CERTLIB, "Allocated Memory Blocks:\n"));
		}
		cTotal++;
		cbTotal += pmh->cbMemory;

		DumpMemBlock(
			L"",
			pmh->pvMemory,
			pmh->cbMemory,
			pmh->Flags,
			pmh->iBackTrace,
			BackTraceFromIndex(pmh->iBackTrace));
	    }
	    pmh++;
        }
        if (0 != cTotal)
        {
	    DBGPRINT((DBG_SS_CERTLIB, "Total: c=%x cb=%x\n\n", cTotal, cbTotal));
        }
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
    }

    RegisterMemoryLeaveCriticalSection();
}


VOID *
_VariantMemory(
    IN PROPVARIANT const *pvar,
    OUT DWORD *pFlags,
    OPTIONAL OUT DWORD *pcb)
{
    VOID *pv = NULL;
    DWORD cb = 0;
    BOOL fString = FALSE;

    *pFlags = CSM_COTASKALLOC;
    if (NULL != pcb)
    {
	*pcb = 0;
    }
    switch (pvar->vt)
    {
	case VT_BSTR:
	    pv = pvar->bstrVal;
	    fString = TRUE;
	    *pFlags = CSM_SYSALLOC;
	    break;

	case VT_BYREF | VT_BSTR:
	    pv = *pvar->pbstrVal;
	    fString = TRUE;
	    *pFlags = CSM_SYSALLOC;
	    break;

	case VT_LPWSTR:
	    pv = pvar->pwszVal;
	    fString = TRUE;
	    break;

	case VT_BLOB:
	    pv = pvar->blob.pBlobData;
	    cb = pvar->blob.cbSize;
	    break;
    }
    if (NULL != pcb)
    {
	if (fString)
	{
	    cb = (wcslen((WCHAR const *) pv) + 1) * sizeof(WCHAR);
	}
	*pcb = cb;
    }
    return(pv);
}


VOID
myRegisterMemAlloc(
    IN VOID const *pv,
    IN LONG cb,
    IN DWORD Flags)
{
    BACKTRACE bt;
    MEMHEADER *pmh;
    BACKTRACE *pbt;

    if (CSM_VARIANT == Flags)
    {
	pv = _VariantMemory((PROPVARIANT const *) pv, &Flags, (DWORD *) &cb);
	if (NULL == pv)
	{
	    return;	// nothing to register
	}
    }
    RegisterMemoryEnterCriticalSection();

    __try
    {
	static BOOL s_fFirst = TRUE;

        if (s_fFirst)
        {
	    ReadEnvironmentFlags();
	    s_fFirst = FALSE;
        }
        if (0 != g_MemTrack)
        {
            // Do not register NULL as an allocation
            CSASSERT(NULL != pv);

            // see if we already have a reference to this memory

            pmh = LookupMemHeader(pv);
            if (NULL != pmh)
            {
	        DBGPRINT((
		    DBG_SS_CERTLIB,
		    "Memory Leak: Tracked memory address reused. Previously allocated:\n"));
	        DumpMemBlock(
		        L"Memory leak",
		        pv,
		        pmh->cbMemory,
		        pmh->Flags,
			pmh->iBackTrace,
			BackTraceFromIndex(pmh->iBackTrace));

                CSASSERT(!"Tracked memory address reused");
                FreeMemHeader(pmh);
            }


	    pmh = AllocMemHeader();
	    if (NULL != pmh)
	    {
		DWORD ibt;

	        CaptureStackBackTrace(NULL, 0, C_BP_FRAME, bt.apCaller);

	        pbt = LookupBackTrace(&bt, &ibt);
	        if (NULL != pbt)
	        {
		    pbt->cAlloc++;
		    pbt->cAllocTotal++;
		    pbt->cbAlloc += cb;
		    pbt->cbAllocTotal += cb;
	        }
	        else
	        {
		    pbt = AllocBackTrace(&ibt);
		    if (NULL != pbt)
		    {
		        pbt->cAlloc = 1;
		        pbt->cAllocTotal = 1;
		        pbt->cbAlloc = cb;
		        pbt->cbAllocTotal = cb;
		        CopyMemory(pbt->apCaller, bt.apCaller, sizeof(pbt->apCaller));
		    }
	        }
	        if (NULL != pbt)
	        {
		    pmh->iBackTrace = ibt;
		    pmh->pvMemory = pv;
		    pmh->cbMemory = cb;
		    pmh->Flags = Flags;

		    CSASSERT(ARRAYSIZE(g_armAlloc) > Flags);
		    g_armAlloc[Flags].cAlloc++;
		    g_armAlloc[Flags].cAllocTotal++;
		    if (MTF_ALLOCTRACE & g_MemTrack)
		    {
		        DBGPRINT((
			    DBG_SS_CERTLIB,
			    "Alloc: pmh=%x: pv=%x cb=%x f=%x(%ws) -- pbt[%d]=%x: c=%x, cb=%x\n",
			    pmh,
			    pmh->pvMemory,
			    pmh->cbMemory,
			    pmh->Flags,
			    wszAllocator(pmh->Flags),
			    SAFE_SUBTRACT_POINTERS(pbt, g_rgbt),
			    pbt,
			    pbt->cAlloc,
			    pbt->cbAlloc));
			if (MTF_STACKTRACE & g_MemTrack)
			{
			    DumpMemBlock(
				    L"Alloc Trace memory block",
				    pv,
				    pmh->cbMemory,	// cbMemory
				    pmh->Flags,		// Flags
				    pmh->iBackTrace,	// ibt
				    pbt);
			}
		    }
	        }
	        else
	        {
		    FreeMemHeader(pmh);
	        }
	    } // if no problem allocating pmh
        } // if g_MemTrack
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
    }

    RegisterMemoryLeaveCriticalSection();
}


VOID
myRegisterMemFree(
    IN VOID const *pv,
    IN DWORD Flags)
{
    MEMHEADER *pmh;

    if (CSM_VARIANT == Flags)
    {
	pv = _VariantMemory((PROPVARIANT const *) pv, &Flags, NULL);
	if (NULL == pv)
	{
	    return;	// nothing to register
	}
    }
    RegisterMemoryEnterCriticalSection();
    CSASSERT(CSM_MAX > (~CSM_GLOBALDESTRUCTOR & Flags));

    __try
    {
        pmh = LookupMemHeader(pv);
        if (NULL != pmh)
        {
	    BACKTRACE *pbt = BackTraceFromIndex(pmh->iBackTrace);

	    if (CSM_GLOBALDESTRUCTOR & Flags)
	    {
		if ((CSM_GLOBALDESTRUCTOR | pmh->Flags) != Flags)
		{
		    BACKTRACE bt;

		    CaptureStackBackTrace(NULL, 0, C_BP_FRAME, bt.apCaller);
		    DumpMemBlock(
			    L"Wrong memory allocator for global destructor",
			    pv,
			    MAXDWORD,	// cbMemory
			    MAXDWORD,	// Flags
			    MAXDWORD,	// ibt
			    &bt);
		    CSASSERT(!"Wrong memory allocator for global destructor");
		}
		else
		{
		    pmh->Flags |= CSM_GLOBALDESTRUCTOR;
		}
	    }
	    else
	    {
		g_armAlloc[Flags].cAlloc--;

		pbt->cAlloc--;
		pbt->cbAlloc -= pmh->cbMemory;

		if (CSM_GLOBALDESTRUCTOR & pmh->Flags)
		{
		    Flags |= CSM_GLOBALDESTRUCTOR;
		}
		if (pmh->Flags != Flags)
		{
		    DBGPRINT((
			DBG_SS_CERTLIB,
			"Wrong memory allocator: Freed with %ws, Allocated by %ws\n",
			wszFreeer(Flags),
			wszAllocator(pmh->Flags)));
		    DumpMemBlock(
			    L"Wrong memory allocator",
			    pv,
			    pmh->cbMemory,
			    pmh->Flags,
			    pmh->iBackTrace,
			    BackTraceFromIndex(pmh->iBackTrace));
		    CSASSERT(pmh->Flags == Flags);
		}
		else if (MTF_FREETRACE & g_MemTrack)
		{
		    DBGPRINT((
			DBG_SS_CERTLIB,
			"Free: pmh=%x: pv=%x cb=%x f=%x(%ws) -- pbt[%d]=%x: c=%x, cb=%x\n",
			pmh,
			pv,
			pmh->cbMemory,
			pmh->Flags,
			wszAllocator(pmh->Flags),
			pmh->iBackTrace,
			pbt,
			pbt->cAlloc,
			pbt->cbAlloc));
		    if (MTF_STACKTRACE & g_MemTrack)
		    {
			BACKTRACE bt;

			CaptureStackBackTrace(NULL, 0, C_BP_FRAME, bt.apCaller);
			DumpMemBlock(
				L"Free Trace memory block(alloc)",
				pv,
				pmh->cbMemory,		// cbMemory
				pmh->Flags,		// Flags
				pmh->iBackTrace,	// ibt
				pbt);
			DumpMemBlock(
				L"Free Trace memory block(free)",
				pv,
				pmh->cbMemory,		// cbMemory
				pmh->Flags,		// Flags
				MAXDWORD,		// ibt
				&bt);
		    }
		}
		FreeMemHeader(pmh);
	    }
        }
        else if (MTF_UNREGISTERED & g_MemTrack)
        {
	    BACKTRACE bt;

	    CaptureStackBackTrace(NULL, 0, C_BP_FRAME, bt.apCaller);
	    DumpMemBlock(
		    L"Unregistered memory block",
		    pv,
		    MAXDWORD,	// cbMemory
		    MAXDWORD,	// Flags
		    MAXDWORD,	// ibt
		    &bt);
	    CSASSERT(!"Unregistered memory block");
        }
    }
    __except(EXCEPTION_EXECUTE_HANDLER)
    {
    }

    RegisterMemoryLeaveCriticalSection();
}


DWORD
myFormatMessageW(
    IN DWORD dwFlags,
    IN LPCVOID lpSource,
    IN DWORD dwMessageId,
    IN DWORD dwLanguageId,
    OUT LPWSTR lpBuffer,
    IN DWORD nSize,
    IN va_list *Arguments)
{
    DWORD cwc;

    cwc = FormatMessage(
		    dwFlags,
		    lpSource,
		    dwMessageId,
		    dwLanguageId,
		    lpBuffer,
		    nSize,
		    Arguments);
    if (cwc != 0 && (FORMAT_MESSAGE_ALLOCATE_BUFFER & dwFlags))
    {
	myRegisterMemAlloc(
		    *(WCHAR **) lpBuffer,
		    (cwc + 1) * sizeof(WCHAR),
		    CSM_LOCALALLOC);
    }
    return(cwc);
}


HLOCAL
myLocalAlloc(
    IN UINT uFlags,
    IN UINT uBytes)
{
    HLOCAL hMem;

    // one of these should always be specified (see LocalAlloc specification)
    assert((LMEM_FIXED == (uFlags & LMEM_FIXED))  ||
           (LMEM_MOVEABLE == (uFlags & LMEM_MOVEABLE)) );

    hMem = LocalAlloc(uFlags, uBytes);
    if (NULL != hMem)
    {
	myRegisterMemAlloc(hMem, uBytes, CSM_LOCALALLOC);
    }
    return(hMem);
}


HLOCAL
myLocalReAlloc(
    IN HLOCAL hMem,
    IN UINT uBytes,
    IN UINT uFlags)
{
    HLOCAL hMemNew;

    // if realloc called without MOVEABLE flag, realloc can't relocate allocation
    assert(LMEM_MOVEABLE == (uFlags & LMEM_MOVEABLE));

    hMemNew = LocalReAlloc(hMem, uBytes, uFlags);
    if (NULL != hMemNew)
    {
	myRegisterMemFree(hMem, CSM_LOCALALLOC);
	myRegisterMemAlloc(hMemNew, uBytes, CSM_LOCALALLOC);
    }

    return(hMemNew);
}


HLOCAL
myLocalFree(
    IN HLOCAL hMem)
{
    myRegisterMemFree(hMem, CSM_LOCALALLOC);
    return(LocalFree(hMem));
}


VOID *
myCoTaskMemAlloc(
    IN ULONG cb)
{
    VOID *pv;

    pv = CoTaskMemAlloc(cb);
    if (NULL != pv)
    {
	myRegisterMemAlloc(pv, cb, CSM_COTASKALLOC);
    }
    return(pv);
}


VOID *
myCoTaskMemRealloc(
    IN VOID *pv,
    IN ULONG cb)
{
    VOID *pvNew;

    pvNew = CoTaskMemRealloc(pv, cb);
    if (NULL != pvNew)
    {
	myRegisterMemFree(pv, CSM_COTASKALLOC);
	myRegisterMemAlloc(pvNew, cb, CSM_COTASKALLOC);
    }
    return(pvNew);
}


VOID
myCoTaskMemFree(
    IN VOID *pv)
{
    myRegisterMemFree(pv, CSM_COTASKALLOC);
    CoTaskMemFree(pv);
}


HRESULT
myStringFromCLSID(
    IN REFCLSID rclsid,
    OUT LPOLESTR FAR *ppwsz)
{
    HRESULT hr;

    hr = StringFromCLSID(rclsid, ppwsz);
    _JumpIfError(hr, error, "StringFromCLSID");

    if (NULL != *ppwsz)
    {
	myRegisterMemAlloc(
		    *ppwsz, (wcslen(*ppwsz) + 1) * sizeof(WCHAR),
		    CSM_COTASKALLOC);
    }

error:
    return(hr);
}


HRESULT
myStringFromIID(
    IN REFIID rclsid,
    OUT LPOLESTR FAR *ppwsz)
{
    HRESULT hr;

    hr = StringFromIID(rclsid, ppwsz);
    _JumpIfError(hr, error, "StringFromIID");

    if (NULL != *ppwsz)
    {
	myRegisterMemAlloc(
		    *ppwsz, (wcslen(*ppwsz) + 1) * sizeof(WCHAR),
		    CSM_COTASKALLOC);
    }

error:
    return(hr);
}


BSTR
mySysAllocString(
    IN const OLECHAR *pwszIn)
{
    BSTR str;

    str = SysAllocString(pwszIn);
    if (NULL != str)
    {
	myRegisterMemAlloc(str, (wcslen(pwszIn) + 1) * sizeof(WCHAR), CSM_SYSALLOC);
    }
    return(str);
}


INT
mySysReAllocString(
    IN OUT BSTR *pstr,
    IN const OLECHAR *pwszIn)
{
    BSTR str = *pstr;
    INT i;

    i = SysReAllocString(pstr, pwszIn);
    if (i)
    {
	myRegisterMemFree(str, CSM_SYSALLOC);
	myRegisterMemAlloc(*pstr, (wcslen(pwszIn) + 1) * sizeof(WCHAR), CSM_SYSALLOC);
    }
    return(i);
}


BSTR
mySysAllocStringLen(
    IN const OLECHAR *pwcIn,
    IN UINT cwc)
{
    BSTR str;

    str = SysAllocStringLen(pwcIn, cwc);
    if (NULL != str)
    {
	myRegisterMemAlloc(str, cwc * sizeof(WCHAR), CSM_SYSALLOC);
    }
    return(str);
}


INT
mySysReAllocStringLen(
    IN OUT BSTR *pstr,
    IN const OLECHAR *pwcIn,
    IN UINT cwc)
{
    BSTR str = *pstr;
    INT i;

    i = SysReAllocStringLen(pstr, pwcIn, cwc);
    if (i)
    {
	myRegisterMemFree(str, CSM_SYSALLOC);
	myRegisterMemAlloc(*pstr, cwc * sizeof(WCHAR), CSM_SYSALLOC);
    }
    return(i);
}


VOID
mySysFreeString(
    IN BSTR str)
{
    if (NULL != str)
    {
	myRegisterMemFree(str, CSM_SYSALLOC);
    }
    SysFreeString(str);
}


BSTR
mySysAllocStringByteLen(
    LPCSTR pszIn,
    UINT cb)
{
    BSTR str;

    str = SysAllocStringByteLen(pszIn, cb);
    if (NULL != str)
    {
	myRegisterMemAlloc(str, cb, CSM_SYSALLOC);
    }
    return(str);
}


VOID
_RegisterVariantMemAlloc(
    IN PROPVARIANT *pvar)
{
    VOID *pv;
    DWORD Flags;
    DWORD cb;
    
    pv = _VariantMemory(pvar, &Flags, &cb);
    if (NULL != pv)
    {
	myRegisterMemAlloc(pv, cb, Flags);
    }
}


VOID
_RegisterVariantMemFree(
    IN PROPVARIANT *pvar)
{
    VOID *pv;
    DWORD Flags;
    
    pv = _VariantMemory(pvar, &Flags, NULL);
    if (NULL != pv)
    {
	myRegisterMemFree(pv, Flags);
    }
}


HRESULT
myPropVariantClear(
    IN PROPVARIANT *pvar)
{
    _RegisterVariantMemFree(pvar);
    return(PropVariantClear(pvar));
}


HRESULT
myVariantClear(
    IN VARIANTARG *pvar)
{
    _RegisterVariantMemFree((PROPVARIANT *) pvar);
    return(VariantClear(pvar));
}


HRESULT
myVariantChangeType(
    OUT VARIANTARG *pvarDest,
    IN VARIANTARG *pvarSrc,
    IN unsigned short wFlags,
    IN VARTYPE vt)
{
    HRESULT hr;

    // if converting in-place, memory will be freed by the API call

    if (pvarDest == pvarSrc)
    {
	_RegisterVariantMemFree((PROPVARIANT *) pvarSrc);
    }
    hr = VariantChangeType(pvarDest, pvarSrc, wFlags, vt);
    _RegisterVariantMemAlloc((PROPVARIANT *) pvarDest);
    return(hr);
}


HRESULT
myVariantChangeTypeEx(
    OUT VARIANTARG *pvarDest,
    IN VARIANTARG *pvarSrc,
    IN LCID lcid,
    IN unsigned short wFlags,
    IN VARTYPE vt)
{
    HRESULT hr;

    // if converting in-place, memory will be freed by the API call

    if (pvarDest == pvarSrc)
    {
	_RegisterVariantMemFree((PROPVARIANT *) pvarSrc);
    }
    hr = VariantChangeTypeEx(pvarDest, pvarSrc, lcid, wFlags, vt);
    _RegisterVariantMemAlloc((PROPVARIANT *) pvarDest);
    return(hr);
}


VOID *
myNew(
    IN size_t size)
{
    VOID *pv;
    
    pv = LocalAlloc(LMEM_FIXED, size);
    if (NULL != pv)
    {
	myRegisterMemAlloc(pv, size, CSM_NEW);
    }
    return(pv);
}


VOID
myDelete(
    IN VOID *pv)
{
    myRegisterMemFree(pv, CSM_NEW);
    LocalFree(pv);
}

#endif // DBG_CERTSRV