|
|
#include "shellprv.h"
#pragma hdrstop
extern const IMallocVtbl c_CShellMallocVtbl;
const IMalloc c_mem = { &c_CShellMallocVtbl };
STDMETHODIMP CShellMalloc_QueryInterface(IMalloc *pmem, REFIID riid, LPVOID * ppvObj) { ASSERT(pmem == &c_mem); if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IMalloc)) { *ppvObj = pmem; return NOERROR; }
*ppvObj = NULL; return E_NOINTERFACE; }
STDMETHODIMP_(ULONG) CShellMalloc_AddRefRelease(IMalloc *pmem) { ASSERT(pmem == &c_mem); return 1; // static object
}
STDMETHODIMP_(void *) CShellMalloc_Alloc(IMalloc *pmem, SIZE_T cb) { ASSERT(pmem == &c_mem); return (void*)LocalAlloc(LPTR, cb); }
//
// IMalloc::Realloc is slightly different from LocalRealloc.
//
// IMalloc::Realloc(NULL, 0) = return NULL
// IMalloc::Realloc(pv, 0) = IMalloc::Free(pv)
// IMalloc::Realloc(NULL, cb) = IMalloc::Alloc(cb)
// IMalloc::Realloc(pv, cb) = LocalRealloc()
//
STDMETHODIMP_(void *) CShellMalloc_Realloc(IMalloc *pmem, void *pv, SIZE_T cb) { ASSERT(pmem == &c_mem);
if (cb == 0) { if (pv) LocalFree(pv); return NULL; } else if (pv == NULL) { return LocalAlloc(LPTR, cb); } else return LocalReAlloc(pv, cb, LMEM_MOVEABLE|LMEM_ZEROINIT); }
//
// IMalloc::Free is slightly different from LocalFree.
//
// IMalloc::Free(NULL) - nop
// IMalloc::Free(pv) - LocalFree()
//
STDMETHODIMP_(void) CShellMalloc_Free(IMalloc *pmem, void *pv) { ASSERT(pmem == &c_mem); if (pv) LocalFree(pv); }
STDMETHODIMP_(SIZE_T) CShellMalloc_GetSize(IMalloc *pmem, void *pv) { ASSERT(pmem == &c_mem); return LocalSize(pv); }
STDMETHODIMP_(int) CShellMalloc_DidAlloc(IMalloc *pmem, void *pv) { ASSERT(pmem == &c_mem); return -1; // don't know
}
STDMETHODIMP_(void) CShellMalloc_HeapMinimize(IMalloc *pmem) { ASSERT(pmem == &c_mem); }
const IMallocVtbl c_CShellMallocVtbl = { CShellMalloc_QueryInterface, CShellMalloc_AddRefRelease, CShellMalloc_AddRefRelease, CShellMalloc_Alloc, CShellMalloc_Realloc, CShellMalloc_Free, CShellMalloc_GetSize, CShellMalloc_DidAlloc, CShellMalloc_HeapMinimize, };
typedef HRESULT (STDAPICALLTYPE * LPFNCOGETMALLOC)(DWORD dwMemContext, IMalloc **ppmem);
IMalloc *g_pmemTask = NULL; // No default task allocator.
#ifdef DEBUG
extern void WINAPI DbgRegisterMallocSpy(); #endif
// on DEBUG builds, mostly for NT, we force using OLE's task allocator at all times.
// for retail we only use OLE if ole32 is already loaded in this process.
//
// this is because OLE's DEBUG allocator will complain if we pass it LocalAlloc()ed
// memory. this can happen if we start up without OLE loaded, then delay load it.
// retail OLE uses LocalAlloc() so we can use our own allocator and switch
// on the fly with no complains from OLE in retail. a common case here would be
// using the file dialogs with notepad
void _GetTaskAllocator(IMalloc **ppmem) { if (g_pmemTask == NULL) { #ifndef DEBUG
if (GetModuleHandle(TEXT("OLE32.DLL"))) // retail
#endif
{ CoGetMalloc(MEMCTX_TASK, &g_pmemTask); }
if (g_pmemTask == NULL) { // use the shell task allocator (which is LocalAlloc).
g_pmemTask = (IMalloc *)&c_mem; // const -> non const
} } else { // handing out cached version, add ref it first
g_pmemTask->lpVtbl->AddRef(g_pmemTask); }
*ppmem = g_pmemTask; }
//
// To be exported
//
STDAPI SHGetMalloc(IMalloc **ppmem) { _GetTaskAllocator(ppmem); return NOERROR; }
// BOGUS, NT redefines these to HeapAlloc variants...
#ifdef Alloc
#undef Alloc
#undef Free
#undef GetSize
#endif
__inline void FAST_GetTaskAllocator() { IMalloc *pmem; if (g_pmemTask == NULL) { // perf: avoid calling unless really need to
_GetTaskAllocator(&pmem); ASSERT(g_pmemTask != NULL); ASSERT(g_pmemTask == pmem); } // else n.b. no AddRef! but we have a refcnt of >=1, and we never Release
// so who cares...
return; }
STDAPI_(void *) SHAlloc(SIZE_T cb) { FAST_GetTaskAllocator(); return g_pmemTask->lpVtbl->Alloc(g_pmemTask, cb); }
STDAPI_(void *) SHRealloc(LPVOID pv, SIZE_T cbNew) { IMalloc *pmem; _GetTaskAllocator(&pmem); return pmem->lpVtbl->Realloc(pmem, pv, cbNew); }
STDAPI_(void) SHFree(LPVOID pv) { FAST_GetTaskAllocator(); g_pmemTask->lpVtbl->Free(g_pmemTask, pv); }
STDAPI_(SIZE_T) SHGetSize(LPVOID pv) { IMalloc *pmem; _GetTaskAllocator(&pmem); return (SIZE_T) pmem->lpVtbl->GetSize(pmem, pv); }
#ifdef DEBUG
void TaskMem_MakeInvalid(void) { static IMalloc c_memDummy = { &c_CShellMallocVtbl }; //
// so we can catch calls to the allocator after PROCESS_DETATCH
// which should be illegal because OLE32.DLL can be unloaded before our
// DLL (unload order is not deterministic) we switch the allocator
// to this dummy one that will cause our asserts to trip.
//
// note, runnin the dummy alllocator is actually fine as it will free
// memory with LocalAlloc(), which is what the OLE alocator uses in all
// cases except debug. and besides, our process is about to go away and
// all process memory will be freed anyway!
//
g_pmemTask = &c_memDummy; } #endif
|