//+------------------------------------------------------------------------- // // 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 #include #include "cspytbl.hxx" #if DBG==1 #include #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(); }