| /* | |
| * basic interfaces | |
| * | |
| * Copyright 1997 Marcus Meissner | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| */ | |
| #include "config.h" | |
| #include <ctype.h> | |
| #include <stdlib.h> | |
| #include <string.h> | |
| #include <assert.h> | |
| #include "ole2.h" | |
| #include "windef.h" | |
| #include "winbase.h" | |
| #include "winerror.h" | |
| #include "wine/obj_base.h" | |
| #include "wine/debug.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(ole); | |
| /****************************************************************************** | |
| * IMalloc32 implementation | |
| * | |
| * NOTES | |
| * For supporting CoRegisterMallocSpy the IMalloc implementation must know if | |
| * a given memory block was allocated with a spy active. | |
| * | |
| *****************************************************************************/ | |
| /* set the vtable later */ | |
| extern ICOM_VTABLE(IMalloc) VT_IMalloc32; | |
| typedef struct { | |
| ICOM_VFIELD(IMalloc); | |
| DWORD dummy; /* nothing, we are static */ | |
| IMallocSpy * pSpy; /* the spy when active */ | |
| DWORD SpyedAllocationsLeft; /* number of spyed allocations left */ | |
| BOOL SpyReleasePending; /* CoRevokeMallocSpy called with spyed allocations left*/ | |
| LPVOID * SpyedBlocks; /* root of the table */ | |
| int SpyedBlockTableLength; /* size of the table*/ | |
| } _Malloc32; | |
| /* this is the static object instance */ | |
| _Malloc32 Malloc32 = {&VT_IMalloc32, 0, NULL, 0, 0, NULL, 0}; | |
| /* with a spy active all calls from pre to post methods are threadsave */ | |
| static CRITICAL_SECTION IMalloc32_SpyCS = CRITICAL_SECTION_INIT("IMalloc32_SpyCS"); | |
| /* resize the old table */ | |
| static int SetSpyedBlockTableLength ( int NewLength ) | |
| { | |
| Malloc32.SpyedBlocks = (LPVOID*)LocalReAlloc((HLOCAL)Malloc32.SpyedBlocks, NewLength, GMEM_ZEROINIT); | |
| Malloc32.SpyedBlockTableLength = NewLength; | |
| return Malloc32.SpyedBlocks ? 1 : 0; | |
| } | |
| /* add a location to the table */ | |
| static int AddMemoryLocation(LPVOID * pMem) | |
| { | |
| LPVOID * Current; | |
| /* allocate the table if not already allocated */ | |
| if (!Malloc32.SpyedBlockTableLength) { | |
| if (!SetSpyedBlockTableLength(0x1000)) return 0; | |
| } | |
| /* find a free location */ | |
| Current = Malloc32.SpyedBlocks; | |
| while (*Current) { | |
| Current++; | |
| if (Current >= Malloc32.SpyedBlocks + Malloc32.SpyedBlockTableLength) { | |
| /* no more space in table, grow it */ | |
| if (!SetSpyedBlockTableLength( Malloc32.SpyedBlockTableLength + 0x1000 )) return 0; | |
| } | |
| }; | |
| /* put the location in our table */ | |
| *Current = pMem; | |
| Malloc32.SpyedAllocationsLeft++; | |
| /*TRACE("%lu\n",Malloc32.SpyedAllocationsLeft);*/ | |
| return 1; | |
| } | |
| static int RemoveMemoryLocation(LPVOID * pMem) | |
| { | |
| LPVOID * Current = Malloc32.SpyedBlocks; | |
| /* find the location */ | |
| while (*Current != pMem) { | |
| Current++; | |
| if (Current >= Malloc32.SpyedBlocks + Malloc32.SpyedBlockTableLength) return 0; /* not found */ | |
| } | |
| /* location found */ | |
| Malloc32.SpyedAllocationsLeft--; | |
| /*TRACE("%lu\n",Malloc32.SpyedAllocationsLeft);*/ | |
| *Current = NULL; | |
| return 1; | |
| } | |
| /****************************************************************************** | |
| * IMalloc32_QueryInterface [VTABLE] | |
| */ | |
| static HRESULT WINAPI IMalloc_fnQueryInterface(LPMALLOC iface,REFIID refiid,LPVOID *obj) { | |
| TRACE("(%s,%p)\n",debugstr_guid(refiid),obj); | |
| if (IsEqualIID(&IID_IUnknown,refiid) || IsEqualIID(&IID_IMalloc,refiid)) { | |
| *obj = (LPMALLOC)&Malloc32; | |
| return S_OK; | |
| } | |
| return E_NOINTERFACE; | |
| } | |
| /****************************************************************************** | |
| * IMalloc32_AddRefRelease [VTABLE] | |
| */ | |
| static ULONG WINAPI IMalloc_fnAddRefRelease (LPMALLOC iface) { | |
| return 1; | |
| } | |
| /****************************************************************************** | |
| * IMalloc32_Alloc [VTABLE] | |
| */ | |
| static LPVOID WINAPI IMalloc_fnAlloc(LPMALLOC iface, DWORD cb) { | |
| LPVOID addr; | |
| TRACE("(%ld)\n",cb); | |
| if(Malloc32.pSpy) { | |
| EnterCriticalSection(&IMalloc32_SpyCS); | |
| cb = IMallocSpy_PreAlloc(Malloc32.pSpy, cb); | |
| if (0==cb) { | |
| /* PreAlloc can force Alloc to fail */ | |
| LeaveCriticalSection(&IMalloc32_SpyCS); | |
| return NULL; | |
| } | |
| } | |
| addr = HeapAlloc(GetProcessHeap(),0,cb); | |
| if(Malloc32.pSpy) { | |
| addr = IMallocSpy_PostAlloc(Malloc32.pSpy, addr); | |
| if (addr) AddMemoryLocation(addr); | |
| LeaveCriticalSection(&IMalloc32_SpyCS); | |
| } | |
| TRACE("--(%p)\n",addr); | |
| return addr; | |
| } | |
| /****************************************************************************** | |
| * IMalloc32_Realloc [VTABLE] | |
| */ | |
| static LPVOID WINAPI IMalloc_fnRealloc(LPMALLOC iface,LPVOID pv,DWORD cb) { | |
| LPVOID pNewMemory; | |
| TRACE("(%p,%ld)\n",pv,cb); | |
| if(Malloc32.pSpy) { | |
| LPVOID pRealMemory; | |
| BOOL fSpyed; | |
| EnterCriticalSection(&IMalloc32_SpyCS); | |
| fSpyed = RemoveMemoryLocation(pv); | |
| cb = IMallocSpy_PreRealloc(Malloc32.pSpy, pv, cb, &pRealMemory, fSpyed); | |
| /* check if can release the spy */ | |
| if(Malloc32.SpyReleasePending && !Malloc32.SpyedAllocationsLeft) { | |
| IMallocSpy_Release(Malloc32.pSpy); | |
| Malloc32.SpyReleasePending = FALSE; | |
| Malloc32.pSpy = NULL; | |
| } | |
| if (0==cb) { | |
| /* PreRealloc can force Realloc to fail */ | |
| LeaveCriticalSection(&IMalloc32_SpyCS); | |
| return NULL; | |
| } | |
| pv = pRealMemory; | |
| } | |
| pNewMemory = HeapReAlloc(GetProcessHeap(),0,pv,cb); | |
| if(Malloc32.pSpy) { | |
| pNewMemory = IMallocSpy_PostRealloc(Malloc32.pSpy, pNewMemory, TRUE); | |
| if (pNewMemory) AddMemoryLocation(pNewMemory); | |
| LeaveCriticalSection(&IMalloc32_SpyCS); | |
| } | |
| TRACE("--(%p)\n",pNewMemory); | |
| return pNewMemory; | |
| } | |
| /****************************************************************************** | |
| * IMalloc32_Free [VTABLE] | |
| */ | |
| static VOID WINAPI IMalloc_fnFree(LPMALLOC iface,LPVOID pv) { | |
| BOOL fSpyed = 0; | |
| TRACE("(%p)\n",pv); | |
| if(Malloc32.pSpy) { | |
| EnterCriticalSection(&IMalloc32_SpyCS); | |
| fSpyed = RemoveMemoryLocation(pv); | |
| pv = IMallocSpy_PreFree(Malloc32.pSpy, pv, fSpyed); | |
| } | |
| HeapFree(GetProcessHeap(),0,pv); | |
| if(Malloc32.pSpy) { | |
| IMallocSpy_PostFree(Malloc32.pSpy, fSpyed); | |
| /* check if can release the spy */ | |
| if(Malloc32.SpyReleasePending && !Malloc32.SpyedAllocationsLeft) { | |
| IMallocSpy_Release(Malloc32.pSpy); | |
| Malloc32.SpyReleasePending = FALSE; | |
| Malloc32.pSpy = NULL; | |
| } | |
| LeaveCriticalSection(&IMalloc32_SpyCS); | |
| } | |
| } | |
| /****************************************************************************** | |
| * IMalloc32_GetSize [VTABLE] | |
| * | |
| * NOTES | |
| * FIXME returns: | |
| * win95: size allocated (4 byte boundarys) | |
| * win2k: size originally requested !!! (allocated on 8 byte boundarys) | |
| */ | |
| static DWORD WINAPI IMalloc_fnGetSize(LPMALLOC iface,LPVOID pv) { | |
| DWORD cb; | |
| BOOL fSpyed = 0; | |
| TRACE("(%p)\n",pv); | |
| if(Malloc32.pSpy) { | |
| EnterCriticalSection(&IMalloc32_SpyCS); | |
| pv = IMallocSpy_PreGetSize(Malloc32.pSpy, pv, fSpyed); | |
| } | |
| cb = HeapSize(GetProcessHeap(),0,pv); | |
| if(Malloc32.pSpy) { | |
| cb = IMallocSpy_PostGetSize(Malloc32.pSpy, cb, fSpyed); | |
| LeaveCriticalSection(&IMalloc32_SpyCS); | |
| } | |
| return cb; | |
| } | |
| /****************************************************************************** | |
| * IMalloc32_DidAlloc [VTABLE] | |
| */ | |
| static INT WINAPI IMalloc_fnDidAlloc(LPMALLOC iface,LPVOID pv) { | |
| BOOL fSpyed = 0; | |
| int didAlloc; | |
| TRACE("(%p)\n",pv); | |
| if(Malloc32.pSpy) { | |
| EnterCriticalSection(&IMalloc32_SpyCS); | |
| pv = IMallocSpy_PreDidAlloc(Malloc32.pSpy, pv, fSpyed); | |
| } | |
| didAlloc = -1; | |
| if(Malloc32.pSpy) { | |
| didAlloc = IMallocSpy_PostDidAlloc(Malloc32.pSpy, pv, fSpyed, didAlloc); | |
| LeaveCriticalSection(&IMalloc32_SpyCS); | |
| } | |
| return didAlloc; | |
| } | |
| /****************************************************************************** | |
| * IMalloc32_HeapMinimize [VTABLE] | |
| */ | |
| static VOID WINAPI IMalloc_fnHeapMinimize(LPMALLOC iface) { | |
| TRACE("()\n"); | |
| if(Malloc32.pSpy) { | |
| EnterCriticalSection(&IMalloc32_SpyCS); | |
| IMallocSpy_PreHeapMinimize(Malloc32.pSpy); | |
| } | |
| if(Malloc32.pSpy) { | |
| IMallocSpy_PostHeapMinimize(Malloc32.pSpy); | |
| LeaveCriticalSection(&IMalloc32_SpyCS); | |
| } | |
| } | |
| static ICOM_VTABLE(IMalloc) VT_IMalloc32 = | |
| { | |
| ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE | |
| IMalloc_fnQueryInterface, | |
| IMalloc_fnAddRefRelease, | |
| IMalloc_fnAddRefRelease, | |
| IMalloc_fnAlloc, | |
| IMalloc_fnRealloc, | |
| IMalloc_fnFree, | |
| IMalloc_fnGetSize, | |
| IMalloc_fnDidAlloc, | |
| IMalloc_fnHeapMinimize | |
| }; | |
| /****************************************************************************** | |
| * IMallocSpy implementation | |
| *****************************************************************************/ | |
| /* set the vtable later */ | |
| extern ICOM_VTABLE(IMallocSpy) VT_IMallocSpy; | |
| typedef struct { | |
| ICOM_VFIELD(IMallocSpy); | |
| DWORD ref; | |
| } _MallocSpy; | |
| /* this is the static object instance */ | |
| _MallocSpy MallocSpy = {&VT_IMallocSpy, 0}; | |
| /****************************************************************************** | |
| * IMalloc32_QueryInterface [VTABLE] | |
| */ | |
| static HRESULT WINAPI IMallocSpy_fnQueryInterface(LPMALLOCSPY iface,REFIID refiid,LPVOID *obj) | |
| { | |
| TRACE("(%s,%p)\n",debugstr_guid(refiid),obj); | |
| if (IsEqualIID(&IID_IUnknown,refiid) || IsEqualIID(&IID_IMallocSpy,refiid)) { | |
| *obj = (LPMALLOC)&MallocSpy; | |
| return S_OK; | |
| } | |
| return E_NOINTERFACE; | |
| } | |
| /****************************************************************************** | |
| * IMalloc32_AddRef [VTABLE] | |
| */ | |
| static ULONG WINAPI IMallocSpy_fnAddRef (LPMALLOCSPY iface) | |
| { | |
| ICOM_THIS (_MallocSpy, iface); | |
| TRACE ("(%p)->(count=%lu)\n", This, This->ref); | |
| return ++(This->ref); | |
| } | |
| /****************************************************************************** | |
| * IMalloc32_AddRelease [VTABLE] | |
| * | |
| * NOTES | |
| * Our MallocSpy is static. If the count reaches 0 we dump the leaks | |
| */ | |
| static ULONG WINAPI IMallocSpy_fnRelease (LPMALLOCSPY iface) | |
| { | |
| ICOM_THIS (_MallocSpy, iface); | |
| TRACE ("(%p)->(count=%lu)\n", This, This->ref); | |
| if (!--(This->ref)) { | |
| /* our allocation list MUST be empty here */ | |
| } | |
| return This->ref; | |
| } | |
| static ULONG WINAPI IMallocSpy_fnPreAlloc(LPMALLOCSPY iface, ULONG cbRequest) | |
| { | |
| ICOM_THIS (_MallocSpy, iface); | |
| TRACE ("(%p)->(%lu)\n", This, cbRequest); | |
| return cbRequest; | |
| } | |
| static PVOID WINAPI IMallocSpy_fnPostAlloc(LPMALLOCSPY iface, void* pActual) | |
| { | |
| ICOM_THIS (_MallocSpy, iface); | |
| TRACE ("(%p)->(%p)\n", This, pActual); | |
| return pActual; | |
| } | |
| static PVOID WINAPI IMallocSpy_fnPreFree(LPMALLOCSPY iface, void* pRequest, BOOL fSpyed) | |
| { | |
| ICOM_THIS (_MallocSpy, iface); | |
| TRACE ("(%p)->(%p %u)\n", This, pRequest, fSpyed); | |
| return pRequest; | |
| } | |
| static void WINAPI IMallocSpy_fnPostFree(LPMALLOCSPY iface, BOOL fSpyed) | |
| { | |
| ICOM_THIS (_MallocSpy, iface); | |
| TRACE ("(%p)->(%u)\n", This, fSpyed); | |
| } | |
| static ULONG WINAPI IMallocSpy_fnPreRealloc(LPMALLOCSPY iface, void* pRequest, ULONG cbRequest, void** ppNewRequest, BOOL fSpyed) | |
| { | |
| ICOM_THIS (_MallocSpy, iface); | |
| TRACE ("(%p)->(%p %lu %u)\n", This, pRequest, cbRequest, fSpyed); | |
| *ppNewRequest = pRequest; | |
| return cbRequest; | |
| } | |
| static PVOID WINAPI IMallocSpy_fnPostRealloc(LPMALLOCSPY iface, void* pActual, BOOL fSpyed) | |
| { | |
| ICOM_THIS (_MallocSpy, iface); | |
| TRACE ("(%p)->(%p %u)\n", This, pActual, fSpyed); | |
| return pActual; | |
| } | |
| static PVOID WINAPI IMallocSpy_fnPreGetSize(LPMALLOCSPY iface, void* pRequest, BOOL fSpyed) | |
| { | |
| ICOM_THIS (_MallocSpy, iface); | |
| TRACE ("(%p)->(%p %u)\n", This, pRequest, fSpyed); | |
| return pRequest; | |
| } | |
| static ULONG WINAPI IMallocSpy_fnPostGetSize(LPMALLOCSPY iface, ULONG cbActual, BOOL fSpyed) | |
| { | |
| ICOM_THIS (_MallocSpy, iface); | |
| TRACE ("(%p)->(%lu %u)\n", This, cbActual, fSpyed); | |
| return cbActual; | |
| } | |
| static PVOID WINAPI IMallocSpy_fnPreDidAlloc(LPMALLOCSPY iface, void* pRequest, BOOL fSpyed) | |
| { | |
| ICOM_THIS (_MallocSpy, iface); | |
| TRACE ("(%p)->(%p %u)\n", This, pRequest, fSpyed); | |
| return pRequest; | |
| } | |
| static int WINAPI IMallocSpy_fnPostDidAlloc(LPMALLOCSPY iface, void* pRequest, BOOL fSpyed, int fActual) | |
| { | |
| ICOM_THIS (_MallocSpy, iface); | |
| TRACE ("(%p)->(%p %u %u)\n", This, pRequest, fSpyed, fActual); | |
| return fActual; | |
| } | |
| static int WINAPI IMallocSpy_fnPreHeapMinimize(LPMALLOCSPY iface) | |
| { | |
| ICOM_THIS (_MallocSpy, iface); | |
| TRACE ("(%p)->()\n", This); | |
| return 0; | |
| } | |
| static int WINAPI IMallocSpy_fnPostHeapMinimize(LPMALLOCSPY iface) | |
| { | |
| ICOM_THIS (_MallocSpy, iface); | |
| TRACE ("(%p)->()\n", This); | |
| return 0; | |
| } | |
| static void MallocSpyDumpLeaks() { | |
| TRACE("leaks: %lu\n", Malloc32.SpyedAllocationsLeft); | |
| } | |
| static ICOM_VTABLE(IMallocSpy) VT_IMallocSpy = | |
| { | |
| ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE | |
| IMallocSpy_fnQueryInterface, | |
| IMallocSpy_fnAddRef, | |
| IMallocSpy_fnRelease, | |
| IMallocSpy_fnPreAlloc, | |
| IMallocSpy_fnPostAlloc, | |
| IMallocSpy_fnPreFree, | |
| IMallocSpy_fnPostFree, | |
| IMallocSpy_fnPreRealloc, | |
| IMallocSpy_fnPostRealloc, | |
| IMallocSpy_fnPreGetSize, | |
| IMallocSpy_fnPostGetSize, | |
| IMallocSpy_fnPreDidAlloc, | |
| IMallocSpy_fnPostDidAlloc, | |
| IMallocSpy_fnPreHeapMinimize, | |
| IMallocSpy_fnPostHeapMinimize | |
| }; | |
| /****************************************************************************** | |
| * CoGetMalloc [OLE32.20] | |
| * | |
| * RETURNS | |
| * The win32 IMalloc | |
| */ | |
| HRESULT WINAPI CoGetMalloc(DWORD dwMemContext, LPMALLOC *lpMalloc) | |
| { | |
| *lpMalloc = (LPMALLOC)&Malloc32; | |
| return S_OK; | |
| } | |
| /*********************************************************************** | |
| * CoTaskMemAlloc [OLE32.43] | |
| * RETURNS | |
| * pointer to newly allocated block | |
| */ | |
| LPVOID WINAPI CoTaskMemAlloc(ULONG size) | |
| { | |
| return IMalloc_Alloc((LPMALLOC)&Malloc32,size); | |
| } | |
| /*********************************************************************** | |
| * CoTaskMemFree [OLE32.44] | |
| */ | |
| VOID WINAPI CoTaskMemFree(LPVOID ptr) | |
| { | |
| IMalloc_Free((LPMALLOC)&Malloc32, ptr); | |
| } | |
| /*********************************************************************** | |
| * CoTaskMemRealloc [OLE32.45] | |
| * RETURNS | |
| * pointer to newly allocated block | |
| */ | |
| LPVOID WINAPI CoTaskMemRealloc(LPVOID pvOld, ULONG size) | |
| { | |
| return IMalloc_Realloc((LPMALLOC)&Malloc32, pvOld, size); | |
| } | |
| /*********************************************************************** | |
| * CoRegisterMallocSpy [OLE32.37] | |
| * | |
| * NOTES | |
| * if a mallocspy is already registered, we cant do it again since | |
| * only the spy knows, how to free a memory block | |
| */ | |
| HRESULT WINAPI CoRegisterMallocSpy(LPMALLOCSPY pMallocSpy) | |
| { | |
| IMallocSpy* pSpy; | |
| HRESULT hres = E_INVALIDARG; | |
| TRACE("\n"); | |
| /* HACK TO ACTIVATE OUT SPY */ | |
| if (pMallocSpy == (LPVOID)-1) pMallocSpy =(IMallocSpy*)&MallocSpy; | |
| if(Malloc32.pSpy) return CO_E_OBJISREG; | |
| EnterCriticalSection(&IMalloc32_SpyCS); | |
| if (SUCCEEDED(IUnknown_QueryInterface(pMallocSpy, &IID_IMallocSpy, (LPVOID*)&pSpy))) { | |
| Malloc32.pSpy = pSpy; | |
| hres = S_OK; | |
| } | |
| LeaveCriticalSection(&IMalloc32_SpyCS); | |
| return hres; | |
| } | |
| /*********************************************************************** | |
| * CoRevokeMallocSpy [OLE32.41] | |
| * | |
| * NOTES | |
| * we can't rewoke a malloc spy as long as memory blocks allocated with | |
| * the spy are active since only the spy knows how to free them | |
| */ | |
| HRESULT WINAPI CoRevokeMallocSpy(void) | |
| { | |
| HRESULT hres = S_OK; | |
| TRACE("\n"); | |
| EnterCriticalSection(&IMalloc32_SpyCS); | |
| /* if it's our spy it's time to dump the leaks */ | |
| if (Malloc32.pSpy == (IMallocSpy*)&MallocSpy) { | |
| MallocSpyDumpLeaks(); | |
| } | |
| if (Malloc32.SpyedAllocationsLeft) { | |
| TRACE("SpyReleasePending with %lu allocations left\n", Malloc32.SpyedAllocationsLeft); | |
| Malloc32.SpyReleasePending = TRUE; | |
| hres = E_ACCESSDENIED; | |
| } else { | |
| IMallocSpy_Release(Malloc32.pSpy); | |
| Malloc32.pSpy = NULL; | |
| } | |
| LeaveCriticalSection(&IMalloc32_SpyCS); | |
| return S_OK; | |
| } | |
| /****************************************************************************** | |
| * IsValidInterface [OLE32.78] | |
| * | |
| * RETURNS | |
| * True, if the passed pointer is a valid interface | |
| */ | |
| BOOL WINAPI IsValidInterface( | |
| LPUNKNOWN punk /* [in] interface to be tested */ | |
| ) { | |
| return !( | |
| IsBadReadPtr(punk,4) || | |
| IsBadReadPtr(ICOM_VTBL(punk),4) || | |
| IsBadReadPtr(ICOM_VTBL(punk)->QueryInterface,9) || | |
| IsBadCodePtr((FARPROC)ICOM_VTBL(punk)->QueryInterface) | |
| ); | |
| } |