215 lines
5.2 KiB
C
215 lines
5.2 KiB
C
#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
|