windows-nt/Source/XPSP1/NT/shell/shell32/malloc.c
2020-09-26 16:20:57 +08:00

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