Windows NT 4.0 source code leak
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1549 lines
41 KiB

//+-------------------------------------------------------------------------
//
// Microsoft Windows:
// Copyright (C) Microsoft Corporation, 1992 - 1995.
//
// File: MemAPI.CXX
//
// Contents: Memory allocation routines and IMallocSpy support
//
// Classes: CDebugMalloc
// CRetailMalloc
// CSpyMalloc
//
// Functions: CoGetMalloc
// CoRegisterMallocSpy
// CoRevokeMallocSpy
// CoTaskMemAlloc
// CoTaskMemFree
// MIDL_user_allocate
// MIDL_user_free
//
// History:
// 04-Nov-93 AlexT Created
// 25-Jan-94 AlexT Add CTaskMemory
// 25-Jan-94 alexgo added PubMemRealloc
// 08-Feb-94 AlexT Fix MIPS alignment
// 24-Oct-94 BruceMa Add API's CoRegisterMallocSpy and
// CoRevokeMallocSpy, and support for
// IMallocSpy
// 01-Nov-94 BruceMa Improve performance of retail IMalloc
// 27-Sep-95 ShannonC Rewrote in C to improve performance.
//
// Notes:
// OLE implements IMalloc using a single, static instance of CMalloc.
// CoGetMalloc always returns the same IMalloc pointer. When necessary,
// OLE can change the behavior of CMalloc by changing the lpVtbl.
//
// The CMalloc object has three interchangeable vtables. Normally,
// the CMalloc object uses either the CRetailMallocVtbl or the
// CDebugMallocVtbl. If an IMallocSpy is registered, OLE will switch
// to the CSpyMallocVtbl in order to add IMallocSpy support. Note that
// we will not change vtables when the IMallocSpy is revoked. Once OLE
// switches to the CSpyMallocVtbl, it can never change back.
//
//--------------------------------------------------------------------------
#include <ole2int.h>
#include <memapi.hxx>
#include "cspytbl.hxx"
#if DBG==1
#include <alocdbg.h>
#endif // DBG==1
//+-------------------------------------------------------------------------
//
// Class: CMalloc
//
// Purpose: Base class for OLE memory allocators.
//
// Interface: IMalloc
//
// See Also: CDebugMalloc, CRetailMalloc, CSpyMalloc
//
//--------------------------------------------------------------------------
HRESULT __stdcall CMalloc_QueryInterface(IMalloc * pThis,
REFIID riid,
void **ppvObject);
ULONG __stdcall CMalloc_AddRef(IMalloc * pThis);
ULONG __stdcall CMalloc_Release(IMalloc * pThis);
typedef struct IMallocVtbl
{
HRESULT ( __stdcall *QueryInterface )(IMalloc * pThis,
REFIID riid,
void ** ppvObject);
ULONG ( __stdcall *AddRef )(IMalloc * pThis);
ULONG ( __stdcall *Release )(IMalloc * pThis);
void *( __stdcall *Alloc )(IMalloc * pThis, ULONG cb);
void *( __stdcall *Realloc )(IMalloc * pThis, void *pv, ULONG cb);
void ( __stdcall *Free )(IMalloc * pThis, void *pv);
ULONG ( __stdcall *GetSize )(IMalloc * pThis, void *pv);
int ( __stdcall *DidAlloc )(IMalloc * pThis, void *pv);
void ( __stdcall *HeapMinimize )(IMalloc * pThis);
} IMallocVtbl;
typedef struct CMalloc
{
IMallocVtbl *lpVtbl;
} CMalloc;
//Global variables
//g_lpVtblMalloc points to CDebugMallocVtbl or CRetailMallocVtbl.
IMallocVtbl * g_lpVtblMalloc = 0;
//WARNING: g_CMalloc.lpVtbl may change at runtime.
//Initially, g_CMalloc.lpVtbl points to either
//CDebugMallocVtbl or CRetailMallocVtbl. When an IMallocSpy is
//registered, g_CMalloc.lpVtbl changes so it points to CSpyMallocVtbl.
CMalloc g_CMalloc;
IMalloc * g_pMalloc = (IMalloc *) &g_CMalloc;
//+-------------------------------------------------------------------------
//
// Class: CDebugMalloc
//
// Purpose: OLE debug memory allocator.
//
// Interface: IMalloc
//
//--------------------------------------------------------------------------
#if DBG==1
//function prototypes.
void * __stdcall CDebugMalloc_Alloc(IMalloc *pThis, ULONG cb);
void * __stdcall CDebugMalloc_Realloc(IMalloc *pThis, void *pv, ULONG cb);
void __stdcall CDebugMalloc_Free(IMalloc *pThis, void *pv);
ULONG __stdcall CDebugMalloc_GetSize(IMalloc *pThis, void *pv);
int __stdcall CDebugMalloc_DidAlloc(IMalloc *pThis, void *pv);
void __stdcall CDebugMalloc_HeapMinimize(IMalloc *pThis);
//CDebugMalloc vtbl.
IMallocVtbl CDebugMallocVtbl =
{
CMalloc_QueryInterface,
CMalloc_AddRef,
CMalloc_Release,
CDebugMalloc_Alloc,
CDebugMalloc_Realloc,
CDebugMalloc_Free,
CDebugMalloc_GetSize,
CDebugMalloc_DidAlloc,
CDebugMalloc_HeapMinimize
};
//Global variables
COleStaticMutexSem _mxsTaskMemory;
ULONG g_BytesAllocated = 0;
ULONG g_MemoryBlocksAllocated = 0;
typedef struct
{
DWORD dwSig; // Memory block signature
ULONG ulSize; // Allocated size
ULONG cbCommit; // Count of committed bytes
struct HeapAllocRec FAR *pArenaRecord; // Arena record
} MEMINFO, *PMEMINFO;
#define OLEMEM_SIG 0x5f4d454d // MEM_
#define OLEMEM_ALLOCBYTE 0xde
#define OLEMEM_FREEBYTE 0xed
#ifdef _X86_
# define OLEMEM_ALIGN_SIZE 4
#else
# define OLEMEM_ALIGN_SIZE 8
#endif
#endif //DBG==1
//+-------------------------------------------------------------------------
//
// Class: CRetailMalloc
//
// Purpose: OLE retail memory allocator. This memory allocator uses
// the NT heap.
//
// Interface: IMalloc
//
//--------------------------------------------------------------------------
//Function prototypes.
void * __stdcall CRetailMalloc_Alloc(IMalloc *pThis, ULONG cb);
void * __stdcall CRetailMalloc_Realloc(IMalloc *pThis, void *pv, ULONG cb);
void __stdcall CRetailMalloc_Free(IMalloc *pThis, void *pv);
ULONG __stdcall CRetailMalloc_GetSize(IMalloc *pThis, void *pv);
int __stdcall CRetailMalloc_DidAlloc(IMalloc *pThis, void *pv);
void __stdcall CRetailMalloc_HeapMinimize(IMalloc *pThis);
// Makes serialized heap access obvious
const DWORD HEAP_SERIALIZE = 0;
//CRetailMalloc vtbl.
IMallocVtbl CRetailMallocVtbl =
{
CMalloc_QueryInterface,
CMalloc_AddRef,
CMalloc_Release,
CRetailMalloc_Alloc,
CRetailMalloc_Realloc,
CRetailMalloc_Free,
CRetailMalloc_GetSize,
CRetailMalloc_DidAlloc,
CRetailMalloc_HeapMinimize
};
//+-------------------------------------------------------------------------
//
// Class: CSpyMalloc
//
// Purpose: OLE spy memory allocator.
//
// Interface: IMalloc
//
//--------------------------------------------------------------------------
//function prototypes.
void * __stdcall CSpyMalloc_Alloc(IMalloc *pThis, ULONG cb);
void * __stdcall CSpyMalloc_Realloc(IMalloc *pThis, void *pv, ULONG cb);
void __stdcall CSpyMalloc_Free(IMalloc *pThis, void *pv);
ULONG __stdcall CSpyMalloc_GetSize(IMalloc *pThis, void *pv);
int __stdcall CSpyMalloc_DidAlloc(IMalloc *pThis, void *pv);
void __stdcall CSpyMalloc_HeapMinimize(IMalloc *pThis);
//CSpyMalloc vtbl.
IMallocVtbl CSpyMallocVtbl =
{
CMalloc_QueryInterface,
CMalloc_AddRef,
CMalloc_Release,
CSpyMalloc_Alloc,
CSpyMalloc_Realloc,
CSpyMalloc_Free,
CSpyMalloc_GetSize,
CSpyMalloc_DidAlloc,
CSpyMalloc_HeapMinimize
};
// Globals for the IMallocSpy code
//
// IMallocSpy instance supplied by the user
LPMALLOCSPY g_pMallocSpy = NULL;
// The thread id (via CoGetCurrentProcess) which registered the IMallocSpy
DWORD g_dwMallocSpyRegistrationTID = 0;
// Semaphore used while spying
COleStaticMutexSem g_SpySem;
// Indicates whether a revoke was attempted with allocation count > 0
BOOL g_fRevokePending = FALSE;
// Table of IMallocSpy allocations not yet freed
LPSPYTABLE g_pAllocTbl = NULL;
//+-------------------------------------------------------------------------
//
// Function: MallocInitialize
//
// Synopsis: Initializes the memory allocator.
//
//--------------------------------------------------------------------------
HRESULT MallocInitialize(BOOL fForceLocalAlloc)
{
HRESULT hr = S_OK;
g_hHeap = GetProcessHeap();
if(0 == g_hHeap)
{
return E_OUTOFMEMORY;
}
#if DBG==1
if(fForceLocalAlloc == TRUE)
{
//Use the OLE retail memory allocator.
g_lpVtblMalloc = &CRetailMallocVtbl;
}
else
{
//Use the OLE debug memory allocator.
g_lpVtblMalloc = &CDebugMallocVtbl;
}
#else
//Use the OLE retail memory allocator.
g_lpVtblMalloc = &CRetailMallocVtbl;
#endif //DBG==1
g_CMalloc.lpVtbl = g_lpVtblMalloc;
return hr;
}
//+-------------------------------------------------------------------------
//
// Function: MallocUninitialize
//
// Synopsis: Clean up and check for local memory leaks
//
// Effects: Prints out messages on debug terminal if there are leaks
//
// Returns: If no leaks, TRUE
// Otherwise, FALSE
//
//--------------------------------------------------------------------------
BOOL MallocUninitialize(void)
{
BOOL bResult = TRUE;
VDATEHEAP();
#if DBG==1
if(&CDebugMallocVtbl == g_lpVtblMalloc)
{
//Check for memory leaks from the debug heap.
if (g_MemoryBlocksAllocated > 0)
{
CairoleDebugOut((DEB_ERROR,
"Leaked %ld bytes (%ld allocations)\n",
g_BytesAllocated,
g_MemoryBlocksAllocated));
bResult = FALSE;
}
AllocArenaDump( NULL );
}
#endif //DBG==1
return bResult;
}
//+-------------------------------------------------------------------------
//
// Function: CoGetMalloc
//
// Synopsis: returns system provided IMalloc
//
// Arguments: [dwContext] - type of allocator to return
// [ppMalloc] - where to return the allocator
//
//--------------------------------------------------------------------------
STDAPI CoGetMalloc(DWORD dwContext, IMalloc **ppMalloc)
{
HRESULT hr;
switch (dwContext)
{
case MEMCTX_TASK:
//We use a static IMalloc object so
//we don't need to AddRef it here.
*ppMalloc = g_pMalloc;
hr = S_OK;
break;
case MEMCTX_SHARED:
CairoleDebugOut((DEB_WARN, "CoGetMalloc(MEMCTX_SHARED, ...) not "
"supported for 32-bit OLE\n"));
// fall through to E_INVALIDARG
default:
*ppMalloc = NULL;
hr = E_INVALIDARG;
}
return hr;
}
//+---------------------------------------------------------------------
//
// Function: CoRegisterMallocSpy
//
// Synopsis: Registers the supplied implementation instance of
// IMallocSpy
//
// Arguments: [pMallocSpy]
//
// Returns: CO_E_OBJISREG - A spy is already registered
// E_INVALIDARG - The QueryInterface for
// IID_IMallocSpy failed
// S_OK - Spy registered ok
//
//----------------------------------------------------------------------
STDAPI CoRegisterMallocSpy(LPMALLOCSPY pMallocSpy)
{
HRESULT hr = S_OK;
OLETRACEIN((API_CoRegisterMallocSpy, PARAMFMT("pMallocSpy = %p"), pMallocSpy));
if(pMallocSpy != 0)
{
LPMALLOCSPY pMSpy;
hr = pMallocSpy->QueryInterface(IID_IMallocSpy, (void **) &pMSpy);
if(SUCCEEDED(hr))
{
g_SpySem.Request();
if (0 == g_pMallocSpy)
{
BOOL fOk;
// Initialize
g_fRevokePending = FALSE;
if (g_pAllocTbl)
{
delete g_pAllocTbl;
}
g_pAllocTbl = new CSpyTable(&fOk);
if (fOk)
{
// Register the new one
CairoleDebugOut((DEB_TRACE, "IMallocSpy registered: %x\n", pMSpy));
g_pMallocSpy = pMSpy;
g_dwMallocSpyRegistrationTID = CoGetCurrentProcess();
//Switch the IMalloc lpVtbl to CSpyMallocVtbl.
g_CMalloc.lpVtbl = &CSpyMallocVtbl;
hr = S_OK;
}
else
{
hr = E_OUTOFMEMORY;
}
}
else
{
// An IMallocSpy is already registered. Deny this registration.
CairoleDebugOut((DEB_ERROR, "Registering IMallocSpy %x over %x\n",
pMallocSpy, g_pMallocSpy));
hr = CO_E_OBJISREG;
}
if(FAILED(hr))
{
pMSpy->Release();
}
g_SpySem.Release();
}
else
{
hr = E_INVALIDARG;
}
}
else
{
hr = E_INVALIDARG;
}
OLETRACEOUT((API_CoRegisterMallocSpy, hr));
return hr;
}
//+---------------------------------------------------------------------
//
// Function: CoRevokeMallocSpy
//
// Synopsis: Revokes any registered IMallocSpy instance
//
// Returns: CO_E_OBJNOTREG - No spy is currently registered
// E_ACCESSDENIED - A spy is registered but there are
// outstanding allocations done while
// the spy was active which have not
// yet been freed
// S_OK - Spy revoked successfully
//
//----------------------------------------------------------------------
STDAPI CoRevokeMallocSpy(void)
{
HRESULT hr = S_OK;
OLETRACEIN((API_CoRevokeMallocSpy, NOPARAM));
// Make revoking thread safe
g_SpySem.Request();
// Check that an IMallocSpy instance is registered
if (g_pMallocSpy != 0)
{
if (0 == g_pAllocTbl->m_cAllocations)
{
// Attempt to release it
CairoleDebugOut((DEB_TRACE, "IMallocSpy revoked: %x\n", g_pMallocSpy));
g_pMallocSpy->Release();
g_pMallocSpy = NULL;
g_fRevokePending = FALSE;
delete g_pAllocTbl;
g_pAllocTbl = NULL;
g_dwMallocSpyRegistrationTID = 0;
hr = S_OK;
}
else
{
// If there are still outstanding Alloc/Realloc's which have not yet
// been Free'd, then deny the revoke
g_fRevokePending = TRUE;
hr = E_ACCESSDENIED;
}
}
else
{
CairoleDebugOut((DEB_WARN, "Attempt to revoke NULL IMallocSpy\n"));
hr = CO_E_OBJNOTREG;
}
g_SpySem.Release();
OLETRACEOUT((API_CoRevokeMallocSpy, hr));
return hr;
}
//+-------------------------------------------------------------------------
//
// Function: CoTaskMemAlloc
//
// Synopsis: Allocate public memory (using task IMalloc)
//
// Arguments: [ulcb] -- memory block size
//
// Returns: Pointer to allocated memory or NULL
//
//--------------------------------------------------------------------------
STDAPI_(LPVOID) CoTaskMemAlloc(ULONG ulcb)
{
return g_pMalloc->Alloc(ulcb);
}
//+-------------------------------------------------------------------------
//
// Function: CoTaskMemFree
//
// Synopsis: Free public memory
//
// Arguments: [pv] -- pointer to memory block
//
// Requires: pv must have been allocated with the task allocator
//
//--------------------------------------------------------------------------
STDAPI_(void) CoTaskMemFree(void *pv)
{
g_pMalloc->Free(pv);
}
//+-------------------------------------------------------------------------
//
// Function: CoTaskMemRealloc
//
// Synopsis: Re-Allocate public memory (using task IMalloc)
//
// Arguments: [pv] -- pointer to the memory to be resized
// [ulcb] -- memory block size
//
// Returns: Pointer to allocated memory or NULL
//
//--------------------------------------------------------------------------
STDAPI_(LPVOID) CoTaskMemRealloc(LPVOID pv, ULONG ulcb)
{
return g_pMalloc->Realloc(pv, ulcb);
}
//+-------------------------------------------------------------------------
//
// Function: MIDL_user_allocate
//
// Purpose: allocates memory on behalf of midl-generated stubs
//
//--------------------------------------------------------------------------
extern "C" void * __RPC_API MIDL_user_allocate(size_t cb)
{
return PrivMemAlloc8(cb);
}
//+-------------------------------------------------------------------------
//
// Function: MIDL_user_free
//
// Purpose: frees memory allocated by MIDL_user_allocate
//
//--------------------------------------------------------------------------
extern "C" void __RPC_API MIDL_user_free(void *pv)
{
PrivMemFree(pv);
}
//+-------------------------------------------------------------------------
//
// Function: CMalloc_QueryInterface
//
// Synopsis: QueryInterface on the memory allocator.
//
// Arguments: riid - Supplies the IID.
//
// Returns:
//
//--------------------------------------------------------------------------
HRESULT __stdcall CMalloc_QueryInterface(IMalloc * pThis,
REFIID riid,
void **ppvObject)
{
HRESULT hr;
if (IsEqualIID(riid,IID_IUnknown) || IsEqualIID(riid,IID_IMalloc))
{
//We use a static IMalloc object so
//we don't need to AddRef it here.
*ppvObject = pThis;
hr = S_OK;
}
else
{
*ppvObject = 0;
hr = E_NOINTERFACE;
}
return hr;
}
//+-------------------------------------------------------------------------
//
// Function: CMalloc_AddRef
//
// Synopsis: AddRef the memory allocator.
//
//--------------------------------------------------------------------------
ULONG __stdcall CMalloc_AddRef(IMalloc * pThis)
{
//We use a static IMalloc object so
//we don't need to AddRef it here.
return 1;
}
//+-------------------------------------------------------------------------
//
// Function: CMalloc_Release
//
// Synopsis: Release the memory allocator.
//
//--------------------------------------------------------------------------
ULONG __stdcall CMalloc_Release(IMalloc * pThis)
{
//We use a static IMalloc object so
//we don't need to Release it here.
return 1;
}
//+-------------------------------------------------------------------------
//
// Member: GetMemInfo
//
// Synopsis: Retrieves memory info block pointer
//
// Arguments: [pv] - memory address
//
// Requires: pv != NULL
//
// Returns: If valid memory address, memory info block pointer
// Otherwise, NULL
//
// Algorithm: The memory info block is always located before the address at
// the beginning of the page.
//
//--------------------------------------------------------------------------
#if DBG==1
PMEMINFO GetMemInfo(void *pv)
{
SYSTEM_INFO si;
PMEMINFO pmi;
CairoleAssert(pv != NULL && "GetMemInfo bad input");
// Retrieve page size
#ifdef _CHICAGO_
si.dwPageSize = 0x1000;
#else
GetSystemInfo(&si);
#endif
pmi = (PMEMINFO) ((((ULONG) pv) - sizeof(MEMINFO)) & ~(si.dwPageSize-1));
// Make sure we can access it
if (IsBadReadPtr(pmi, si.dwPageSize))
{
CairoleDebugOut((DEB_WARN,
"GetMemInfo - no read access\n"));
}
else if (pmi->dwSig != OLEMEM_SIG)
{
CairoleDebugOut((DEB_WARN,
"GetMemInfo - bad mem signature\n"));
}
else
{
return(pmi);
}
return(NULL);
}
#endif //DBG==1
//+-------------------------------------------------------------------------
//
// Member: CDebugMalloc_Alloc
//
// Synopsis: Local memory allocator
//
// Arguments: [cb] -- memory block size
//
// Returns: Memory block pointer
//
// Modifies: Heap
//
// Algorithm: reserve memory including guard pages
// commit memory
// initialize committed memory
// initialize control block
// return pointer such that tail is on guard page
//
//--------------------------------------------------------------------------
#if DBG==1
void * __stdcall CDebugMalloc_Alloc(IMalloc *pThis, ULONG cb)
{
SYSTEM_INFO si;
ULONG cbAlloc;
ULONG cbCommit;
ULONG cbReserve;
void *pvReserve;
void *pvCommit;
PMEMINFO pmi;
void *pvRet = 0;
// Parameter validation
if (cb > 0x7FFFFFFF)
return 0;
// Retrieve page size
#ifdef _CHICAGO_
si.dwPageSize = 0x1000;
#else
GetSystemInfo(&si);
#endif
// For x86, align the memory on a 4 byte boundary.
// For non-x86 platforms, align the memory on an 8 byte boundary.
cbAlloc = (cb + OLEMEM_ALIGN_SIZE - 1)
& ~(OLEMEM_ALIGN_SIZE - 1);
// Calculate pages necessary for both requested size and our
// control info
cbCommit = (cbAlloc + sizeof(MEMINFO) + si.dwPageSize - 1)
& ~(si.dwPageSize - 1);
// Reserve enough for allocation and guard pages
cbReserve = cbCommit + si.dwPageSize;
// Reserve cbReserve pages
pvReserve = VirtualAlloc(
NULL,
cbReserve,
MEM_RESERVE,
PAGE_NOACCESS);
if (pvReserve != 0)
{
// Commit cbCommit pages
pvCommit = VirtualAlloc(pvReserve,
cbCommit,
MEM_COMMIT,
PAGE_EXECUTE_READWRITE);
if (pvCommit != 0)
{
// Initialize pages
memset(pvCommit, OLEMEM_ALLOCBYTE, cbCommit);
// write sMemInfo data
pmi = (PMEMINFO) pvCommit;
pmi->dwSig = OLEMEM_SIG;
pmi->ulSize = cb;
pmi->cbCommit = cbCommit;
// Increment local count
{
COleStaticLock lck(_mxsTaskMemory);
static AllocArena *pAllocArena = (AllocArena *)-1;
if (pAllocArena == (AllocArena *)-1)
{
pAllocArena = AllocArenaCreate( MEMCTX_TASK, "CDebugMalloc");
}
pmi->pArenaRecord = AllocArenaRecordAlloc(pAllocArena, cb);
g_BytesAllocated += pmi->ulSize;
g_MemoryBlocksAllocated++;
}
// Calculate return pointer
pvRet = ((BYTE *) pvCommit) + cbCommit - cbAlloc;
// Public memory guaranteed to be aligned
CairoleAssert(((ULONG)pvRet & (OLEMEM_ALIGN_SIZE - 1)) == NULL &&
"public memory allocation not aligned");
}
else
{
CairoleDebugOut((DEB_WARN,
"CDebugMalloc_Alloc(%ld) couldn't commit - %lx\n",
cbCommit, GetLastError()));
// Release reserved pages.
VirtualFree(pvReserve, 0, MEM_RELEASE);
}
}
else
{
CairoleDebugOut((DEB_WARN,
"CDebugMalloc_Alloc(%ld) couldn't reserve - %lx\n",
cbReserve, GetLastError()));
}
return pvRet;
}
#endif //DBG==1
//+-------------------------------------------------------------------------
//
// Member: CDebugMalloc_Realloc
//
// Synopsis: Reallocated local memory
//
// Arguments: [pv] -- original memory block
// [cb] -- new size
//
// Returns: new memory block
//
//--------------------------------------------------------------------------
#if DBG==1
void * __stdcall CDebugMalloc_Realloc(IMalloc * pThis, void * pv, ULONG cb)
{
void *pvNew = 0;
if (pv != 0)
{
PMEMINFO pmi = GetMemInfo(pv);
CairoleAssert(pmi != 0 && "CDebugMalloc_Realloc - bad pointer");
if(pmi != 0)
{
if (cb != 0)
{
// Allocate a new memory block.
pvNew = CDebugMalloc_Alloc(pThis, cb);
if(pvNew != 0)
{
// Copy data from the old memory block.
memcpy(pvNew, pv, min(pmi->ulSize, cb));
// Free the old memory block.
CDebugMalloc_Free(pThis, pv);
}
else
{
// We could not allocate a new memory block.
// Leave the old memory block unchanged.
}
}
else
{
// Free the old memory block.
CDebugMalloc_Free(pThis, pv);
}
}
}
else
{
// Treat this as an Alloc
pvNew = CDebugMalloc_Alloc(pThis, cb);
}
return pvNew;
}
#endif //DBG==1
//+-------------------------------------------------------------------------
//
// Member: CDebugMalloc_Free
//
// Synopsis: release local memory
//
// Arguments: [pv] -- memory address
//
// Algorithm:
// get control information
// validate memory block
// verify that bytes between header and pv are untouched
// set to known bad value
// decommit
// unreserve
//
//--------------------------------------------------------------------------
#if DBG==1
void __stdcall CDebugMalloc_Free(IMalloc *pThis, void * pv)
{
if(pv != 0)
{
PMEMINFO pmi = GetMemInfo(pv);
CairoleAssert(pmi != NULL && "CDebugMalloc_Free - bad pointer");
if(pmi != 0)
{
BOOL bResult;
MEMINFO mi = *pmi;
void *pvCommit = pmi;;
BYTE *pbCheck = ((BYTE *) pvCommit) + sizeof(MEMINFO);
// Verify that bytes between header and pvNew are untouched
while (pbCheck < (BYTE *) pv && *pbCheck == OLEMEM_ALLOCBYTE)
{
pbCheck++;
}
CairoleAssert(pbCheck == (BYTE *) pv &&
"CDebugMalloc_Free - header region dirty");
// Verify that bytes between allocation and end of page are untouched
pbCheck = ((BYTE *) pv) + mi.ulSize;
while (pbCheck < (BYTE *) pvCommit + mi.cbCommit &&
*pbCheck == OLEMEM_ALLOCBYTE)
{
pbCheck++;
}
CairoleAssert(pbCheck == ((BYTE *) pvCommit) + mi.cbCommit &&
"CDebugMalloc_Free - tail region dirty");
// Set to known bad value
memset(pvCommit, OLEMEM_FREEBYTE, mi.cbCommit);
// Decommit
bResult = VirtualFree(pvCommit, mi.cbCommit, MEM_DECOMMIT);
CairoleAssert(bResult && "CDebugMalloc_Free - VirtualFree(DECOMMIT) failed");
// Unreserve
bResult = VirtualFree(pvCommit, 0, MEM_RELEASE);
CairoleAssert(bResult && "CDebugMalloc_Free - VirtualFree(RELEASE) failed");
// Decrement local count
{
COleStaticLock lck(_mxsTaskMemory);
CairoleAssert(mi.ulSize <= g_BytesAllocated &&
"Public memory tracking broken");
CairoleAssert(g_MemoryBlocksAllocated > 0 &&
"Public memory tracking broken");
g_BytesAllocated -= mi.ulSize;
g_MemoryBlocksAllocated--;
}
AllocArenaRecordFree(mi.pArenaRecord, mi.ulSize);
}
}
}
#endif //DBG==1
//+-------------------------------------------------------------------------
//
// Member: CDebugMalloc_GetSize
//
// Synopsis: Return size of memory block
//
// Arguments: [pv] -- memory address
//
// Returns: If valid memory, the size of the block
// Otherwise, 0
//
//--------------------------------------------------------------------------
#if DBG==1
ULONG __stdcall CDebugMalloc_GetSize(IMalloc *pThis, void * pv)
{
ULONG ulSize = (ULONG) -1;
if (pv != 0)
{
PMEMINFO pmi = GetMemInfo(pv);
CairoleAssert(pmi != NULL && "CDebugMalloc_GetSize - bad pointer");
if (pmi != 0)
{
// Fetch the size of the allocation
ulSize = pmi->ulSize;
}
}
return ulSize;
}
#endif //DBG==1
//+-------------------------------------------------------------------------
//
// Member: CDebugMalloc_DidAlloc
//
// Synopsis: Return whether this allocator allocated the block
//
// Arguments: [pv] -- memory address
//
// Returns: If allocated by this allocator, TRUE
// Otherwise, FALSE
//
//--------------------------------------------------------------------------
#if DBG==1
int __stdcall CDebugMalloc_DidAlloc(IMalloc *pThis, void * pv)
{
int fDidAlloc = FALSE;
if (pv != 0)
{
PMEMINFO pmi = GetMemInfo(pv);
if (pmi != NULL)
{
fDidAlloc = TRUE;
}
}
else
{
fDidAlloc = -1;
}
return fDidAlloc;
}
#endif //DBG==1
//+-------------------------------------------------------------------------
//
// Member: CDebugMalloc_HeapMinimize
//
// Synopsis: Minimize heap
//
//--------------------------------------------------------------------------
#if DBG==1
void __stdcall CDebugMalloc_HeapMinimize(IMalloc *pThis)
{
CairoleAssert(g_hHeap != 0 && "GetProcessHeap failed");
// Compact the heap
HeapCompact(g_hHeap, HEAP_SERIALIZE);
}
#endif //DBG==1
//+-------------------------------------------------------------------------
//
// Function: CRetailMalloc_Alloc
//
// Synopsis: Allocate a block of memory.
//
// Arguments: [cb] -- Specifies the size of the memory block to allocate.
//
// Returns: Pointer to allocated memory or NULL
//
//--------------------------------------------------------------------------
void * __stdcall CRetailMalloc_Alloc(IMalloc *pThis, ULONG cb)
{
CairoleAssert(g_hHeap != 0 && "GetProcessHeap failed");
return (LPVOID) HeapAlloc(g_hHeap, HEAP_SERIALIZE, cb);
}
//+-------------------------------------------------------------------------
//
// Function: CRetailMalloc_DidAlloc
//
// Synopsis: Determine if the memory block was allocated by this
// memory allocator.
//
// Arguments: [pv] -- pointer to the memory block
//
// Notes: The OLE spec requires that -1 be returned for
// NULL pointers.
//
//--------------------------------------------------------------------------
int __stdcall CRetailMalloc_DidAlloc(IMalloc *pThis, void * pv)
{
int fDidAlloc = -1;
CairoleAssert(g_hHeap != 0 && "GetProcessHeap failed");
// Chicago does not support HeapValidate
#ifndef _CHICAGO_
if (pv != 0)
{
fDidAlloc = HeapValidate(g_hHeap, HEAP_SERIALIZE, pv);
}
#endif //_CHICAGO_
return fDidAlloc;
}
//+-------------------------------------------------------------------------
//
// Function: CRetailMalloc_Free
//
// Synopsis: Free a memory block previously allocated via CRetailMalloc_Alloc.
//
// Arguments: [pv] -- pointer to the memory block to be freed.
//
//--------------------------------------------------------------------------
void __stdcall CRetailMalloc_Free(IMalloc *pThis, void * pv)
{
CairoleAssert(g_hHeap != 0 && "GetProcessHeap failed");
if (pv != 0)
HeapFree(g_hHeap, HEAP_SERIALIZE, pv);
}
//+-------------------------------------------------------------------------
//
// Function: CRetailMalloc_GetSize
//
// Synopsis: Gets the size of a memory block.
//
// Arguments: [pv] -- pointer to the memory block
//
// Notes: The OLE spec requires that -1 be returned for
// NULL pointers.
//
//--------------------------------------------------------------------------
ULONG __stdcall CRetailMalloc_GetSize(IMalloc * pThis, void * pv)
{
CairoleAssert(g_hHeap != 0 && "GetProcessHeap failed");
if (0 == pv)
{
return((ULONG) -1);
}
return HeapSize(g_hHeap, HEAP_SERIALIZE, pv);
}
//+-------------------------------------------------------------------------
//
// Function: CRetailMalloc_Minimize
//
// Synopsis: Compact the heap.
//
//--------------------------------------------------------------------------
void __stdcall CRetailMalloc_HeapMinimize(IMalloc * pThis)
{
CairoleAssert(g_hHeap != 0 && "GetProcessHeap failed");
// Compact the heap
HeapCompact(g_hHeap, HEAP_SERIALIZE);
}
//+-------------------------------------------------------------------------
//
// Function: CRetailMalloc_Realloc
//
// Synopsis: Changes the size of a memory block previously allocated
// via CRetailMalloc_Alloc.
//
// Arguments: [pv] -- Points to the memory block to be reallocated.
// [cb] -- Specifies the new size of the memory block.
//
// Returns: Pointer to allocated memory or NULL.
//
//--------------------------------------------------------------------------
void * __stdcall CRetailMalloc_Realloc(IMalloc *pThis, void * pv, ULONG cb)
{
LPVOID pvNew = 0;
CairoleAssert(g_hHeap != 0 && "GetProcessHeap failed");
if(pv != 0)
{
if(cb != 0)
{
pvNew = (LPVOID) HeapReAlloc(g_hHeap, HEAP_SERIALIZE, pv, cb);
}
else
{
//Treat this as a free.
HeapFree(g_hHeap, HEAP_SERIALIZE, pv);
pvNew = 0;
}
}
else
{
//Treat this as an alloc.
pvNew = (LPVOID) HeapAlloc(g_hHeap, HEAP_SERIALIZE, cb);
}
return pvNew;
}
//+-------------------------------------------------------------------------
//
// Member: CSpyMalloc::Alloc
//
// Synopsis: Local memory allocator
//
// Arguments: [cb] -- memory block size
//
// Returns: Pointer to new memory block.
//
//--------------------------------------------------------------------------
void * __stdcall CSpyMalloc_Alloc(IMalloc *pThis, ULONG cb)
{
void *pvRet = NULL;
g_SpySem.Request();
// If an IMallocSpy is active, call the pre method
if (g_pMallocSpy)
{
ULONG cbAlloc = g_pMallocSpy->PreAlloc(cb);
// The pre method forces failure by returning 0
if ((cbAlloc != 0) || (0 == cb))
{
// Allocate the memory
pvRet = g_lpVtblMalloc->Alloc(pThis, cbAlloc);
// Call the post method
pvRet = g_pMallocSpy->PostAlloc(pvRet);
// Update the spy table.
if (pvRet != NULL)
{
g_pAllocTbl->Add(pvRet);
}
}
}
else
{
// Allocate the memory
pvRet = g_lpVtblMalloc->Alloc(pThis, cb);
}
g_SpySem.Release();
return pvRet;
}
//+-------------------------------------------------------------------------
//
// Member: CSpyMalloc_Realloc
//
// Synopsis: Reallocated local memory
//
// Arguments: [pv] -- original memory block
// [cb] -- new size
//
// Returns: Pointer to new memory block
//
//--------------------------------------------------------------------------
void *CSpyMalloc_Realloc(IMalloc *pThis, void * pv, ULONG cb)
{
void *pvNew = 0;
if(pv != 0)
{
if(cb != 0)
{
g_SpySem.Request();
// If an IMallocSpy is active, call the pre method
if (g_pMallocSpy != 0)
{
void *pvTemp = 0;
ULONG j = 0;
BOOL fSpyed = g_pAllocTbl->Find(pv, &j);
ULONG cbAlloc = g_pMallocSpy->PreRealloc(pv, cb, &pvTemp, fSpyed);
// The pre method forces failure by returning 0
if (cbAlloc != 0)
{
//Reallocate the memory
pvTemp = g_lpVtblMalloc->Realloc(pThis, pvTemp, cbAlloc);
// Call the post method
pvNew = g_pMallocSpy->PostRealloc(pvTemp, fSpyed);
// Update the spy table.
if (pvNew != 0)
{
if (fSpyed)
{
g_pAllocTbl->Remove(pv);
}
g_pAllocTbl->Add(pvNew);
}
}
}
else
{
//Reallocate the memory.
pvNew = g_lpVtblMalloc->Realloc(pThis, pv, cb);
}
g_SpySem.Release();
}
else
{
//Treat this as a Free.
pThis->Free(pv);
}
}
else
{
//Treat this as an Alloc.
pvNew = pThis->Alloc(cb);
}
return pvNew;
}
//+-------------------------------------------------------------------------
//
// Member: CSpyMalloc_Free
//
// Synopsis: release local memory
//
// Arguments: [pv] -- memory address
//
//--------------------------------------------------------------------------
void __stdcall CSpyMalloc_Free(IMalloc *pThis, void * pv)
{
if(pv != 0)
{
g_SpySem.Request();
// If an IMallocSpy is active, call the pre method
if (g_pMallocSpy)
{
ULONG j;
BOOL fSpyed = g_pAllocTbl->Find(pv, &j);
void *pvNew = g_pMallocSpy->PreFree(pv, fSpyed);
// Free the buffer
g_lpVtblMalloc->Free(pThis, pvNew);
// If an IMallocSpy is active, call the post method
g_pMallocSpy->PostFree(fSpyed);
// Update the spy table.
if (fSpyed)
{
g_pAllocTbl->Remove(pv);
}
if (g_pAllocTbl->m_cAllocations == 0 && g_fRevokePending)
{
CoRevokeMallocSpy();
}
}
else
{
// Free the buffer
g_lpVtblMalloc->Free(pThis, pv);
}
g_SpySem.Release();
}
}
//+-------------------------------------------------------------------------
//
// Member: CSpyMalloc_GetSize
//
// Synopsis: Return size of memory block
//
// Arguments: [pv] -- memory address
//
// Returns: If valid memory, the size of the block
// Otherwise, 0
//
// Notes: The OLE spec requires that -1 be returned for
// NULL pointers.
//
//--------------------------------------------------------------------------
ULONG __stdcall CSpyMalloc_GetSize(IMalloc *pThis, void * pv)
{
ULONG ulSize = (ULONG) -1;
if (pv != 0)
{
g_SpySem.Request();
// If an IMallocSpy is active, call the pre method
if (g_pMallocSpy)
{
ULONG j;
BOOL fSpyed = g_pAllocTbl->Find(pv, &j);
void *pvNew = g_pMallocSpy->PreGetSize(pv, fSpyed);
// Fetch the size of the allocation
ulSize = g_lpVtblMalloc->GetSize(pThis, pvNew);
// Call the post method
ulSize = g_pMallocSpy->PostGetSize(ulSize, fSpyed);
}
else
{
// Fetch the size of the allocation
ulSize = g_lpVtblMalloc->GetSize(pThis, pv);
}
g_SpySem.Release();
}
return ulSize;
}
//+-------------------------------------------------------------------------
//
// Member: CSpyMalloc_DidAlloc
//
// Synopsis: Return whether this allocator allocated the block
//
// Arguments: [pv] -- memory address
//
// Returns: If allocated by this allocator, TRUE
// Otherwise, FALSE
//
// Notes: The OLE spec requires that -1 be returned for
// NULL pointers.
//
//--------------------------------------------------------------------------
int __stdcall CSpyMalloc_DidAlloc(IMalloc *pThis, void * pv)
{
int fDidAlloc = (ULONG) -1;
if (pv != 0)
{
g_SpySem.Request();
// If an IMallocSpy is active, call the pre method
if (g_pMallocSpy)
{
ULONG j;
BOOL fSpyed = g_pAllocTbl->Find(pv, &j);
void *pvNew = g_pMallocSpy->PreDidAlloc(pv, fSpyed);
// Check the allocation
fDidAlloc = g_lpVtblMalloc->DidAlloc(pThis, pvNew);
// Call the post method
fDidAlloc = g_pMallocSpy->PostDidAlloc(pv, fSpyed, fDidAlloc);
}
else
{
// Check the allocation
fDidAlloc = g_lpVtblMalloc->DidAlloc(pThis, pv);
}
g_SpySem.Release();
}
return fDidAlloc;
}
//+-------------------------------------------------------------------------
//
// Member: CSpyMalloc_HeapMinimize
//
// Synopsis: Minimize heap
//
//--------------------------------------------------------------------------
void __stdcall CSpyMalloc_HeapMinimize(IMalloc *pThis)
{
g_SpySem.Request();
// If an IMallocSpy is active, call the pre method
if (g_pMallocSpy)
{
g_pMallocSpy->PreHeapMinimize();
// Compact the heap
g_lpVtblMalloc->HeapMinimize(pThis);
// Call the post method
g_pMallocSpy->PostHeapMinimize();
}
else
{
// Compact the heap
g_lpVtblMalloc->HeapMinimize(pThis);
}
g_SpySem.Release();
}