Avoid excessive heap memory reallocation when generating EMF
[wine] / dlls / ole32 / ifs.c
1 /*
2  *      basic interfaces
3  *
4  *      Copyright 1997  Marcus Meissner
5  *
6  * This library is free software; you can redistribute it and/or
7  * modify it under the terms of the GNU Lesser General Public
8  * License as published by the Free Software Foundation; either
9  * version 2.1 of the License, or (at your option) any later version.
10  *
11  * This library is distributed in the hope that it will be useful,
12  * but WITHOUT ANY WARRANTY; without even the implied warranty of
13  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
14  * Lesser General Public License for more details.
15  *
16  * You should have received a copy of the GNU Lesser General Public
17  * License along with this library; if not, write to the Free Software
18  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
19  */
20
21 #include "config.h"
22
23 #include <ctype.h>
24 #include <stdarg.h>
25 #include <stdlib.h>
26 #include <string.h>
27 #include <assert.h>
28
29 #include "windef.h"
30 #include "winbase.h"
31 #include "winuser.h"
32 #include "ole2.h"
33 #include "winerror.h"
34
35 #include "wine/debug.h"
36
37 WINE_DEFAULT_DEBUG_CHANNEL(ole);
38
39 /******************************************************************************
40  *      IMalloc32 implementation
41  *
42  * NOTES
43  *  For supporting CoRegisterMallocSpy the IMalloc implementation must know if
44  *  a given memory block was allocated with a spy active.
45  *
46  *****************************************************************************/
47 /* set the vtable later */
48 extern ICOM_VTABLE(IMalloc) VT_IMalloc32;
49
50 typedef struct {
51         ICOM_VFIELD(IMalloc);
52         DWORD dummy;                /* nothing, we are static */
53         IMallocSpy * pSpy;          /* the spy when active */
54         DWORD SpyedAllocationsLeft; /* number of spyed allocations left */
55         BOOL SpyReleasePending;     /* CoRevokeMallocSpy called with spyed allocations left*/
56         LPVOID * SpyedBlocks;       /* root of the table */
57         int SpyedBlockTableLength;  /* size of the table*/
58 } _Malloc32;
59
60 /* this is the static object instance */
61 _Malloc32 Malloc32 = {&VT_IMalloc32, 0, NULL, 0, 0, NULL, 0};
62
63 /* with a spy active all calls from pre to post methods are threadsave */
64 static CRITICAL_SECTION IMalloc32_SpyCS;
65 static CRITICAL_SECTION_DEBUG critsect_debug =
66 {
67     0, 0, &IMalloc32_SpyCS,
68     { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
69       0, 0, { 0, (DWORD)(__FILE__ ": IMalloc32_SpyCS") }
70 };
71 static CRITICAL_SECTION IMalloc32_SpyCS = { &critsect_debug, -1, 0, 0, 0, 0 };
72
73 /* resize the old table */
74 static int SetSpyedBlockTableLength ( int NewLength )
75 {
76         Malloc32.SpyedBlocks = (LPVOID*)LocalReAlloc((HLOCAL)Malloc32.SpyedBlocks, NewLength, GMEM_ZEROINIT);
77         Malloc32.SpyedBlockTableLength = NewLength;
78         return Malloc32.SpyedBlocks ? 1 : 0;
79 }
80
81 /* add a location to the table */
82 static int AddMemoryLocation(LPVOID * pMem)
83 {
84         LPVOID * Current;
85
86         /* allocate the table if not already allocated */
87         if (!Malloc32.SpyedBlockTableLength) {
88             if (!SetSpyedBlockTableLength(0x1000)) return 0;
89         }
90
91         /* find a free location */
92         Current = Malloc32.SpyedBlocks;
93         while (*Current) {
94             Current++;
95             if (Current >= Malloc32.SpyedBlocks + Malloc32.SpyedBlockTableLength) {
96                 /* no more space in table, grow it */
97                 if (!SetSpyedBlockTableLength( Malloc32.SpyedBlockTableLength + 0x1000 )) return 0;
98             }
99         };
100
101         /* put the location in our table */
102         *Current = pMem;
103         Malloc32.SpyedAllocationsLeft++;
104         /*TRACE("%lu\n",Malloc32.SpyedAllocationsLeft);*/
105         return 1;
106 }
107
108 static int RemoveMemoryLocation(LPVOID * pMem)
109 {
110         LPVOID * Current = Malloc32.SpyedBlocks;
111
112         /* find the location */
113         while (*Current != pMem) {
114             Current++;
115             if (Current >= Malloc32.SpyedBlocks + Malloc32.SpyedBlockTableLength)  return 0;      /* not found  */
116         }
117
118         /* location found */
119         Malloc32.SpyedAllocationsLeft--;
120         /*TRACE("%lu\n",Malloc32.SpyedAllocationsLeft);*/
121         *Current = NULL;
122         return 1;
123 }
124
125 /******************************************************************************
126  *      IMalloc32_QueryInterface        [VTABLE]
127  */
128 static HRESULT WINAPI IMalloc_fnQueryInterface(LPMALLOC iface,REFIID refiid,LPVOID *obj) {
129
130         TRACE("(%s,%p)\n",debugstr_guid(refiid),obj);
131
132         if (IsEqualIID(&IID_IUnknown,refiid) || IsEqualIID(&IID_IMalloc,refiid)) {
133                 *obj = (LPMALLOC)&Malloc32;
134                 return S_OK;
135         }
136         return E_NOINTERFACE;
137 }
138
139 /******************************************************************************
140  *      IMalloc32_AddRefRelease         [VTABLE]
141  */
142 static ULONG WINAPI IMalloc_fnAddRefRelease (LPMALLOC iface) {
143         return 1;
144 }
145
146 /******************************************************************************
147  *      IMalloc32_Alloc                 [VTABLE]
148  */
149 static LPVOID WINAPI IMalloc_fnAlloc(LPMALLOC iface, DWORD cb) {
150
151         LPVOID addr;
152
153         TRACE("(%ld)\n",cb);
154
155         if(Malloc32.pSpy) {
156             DWORD preAllocResult;
157             
158             EnterCriticalSection(&IMalloc32_SpyCS);
159             preAllocResult = IMallocSpy_PreAlloc(Malloc32.pSpy, cb);
160             if ((cb != 0) && (preAllocResult == 0)) {
161                 /* PreAlloc can force Alloc to fail, but not if cb == 0 */
162                 TRACE("returning null\n");
163                 LeaveCriticalSection(&IMalloc32_SpyCS);
164                 return NULL;
165             }
166         }
167         
168         addr = HeapAlloc(GetProcessHeap(),0,cb);
169
170         if(Malloc32.pSpy) {
171             addr = IMallocSpy_PostAlloc(Malloc32.pSpy, addr);
172             if (addr) AddMemoryLocation(addr);
173             LeaveCriticalSection(&IMalloc32_SpyCS);
174         }
175
176         TRACE("--(%p)\n",addr);
177         return addr;
178 }
179
180 /******************************************************************************
181  * IMalloc32_Realloc [VTABLE]
182  */
183 static LPVOID WINAPI IMalloc_fnRealloc(LPMALLOC iface,LPVOID pv,DWORD cb) {
184
185         LPVOID pNewMemory;
186
187         TRACE("(%p,%ld)\n",pv,cb);
188
189         if(Malloc32.pSpy) {
190             LPVOID pRealMemory;
191             BOOL fSpyed;
192
193             EnterCriticalSection(&IMalloc32_SpyCS);
194             fSpyed = RemoveMemoryLocation(pv);
195             cb = IMallocSpy_PreRealloc(Malloc32.pSpy, pv, cb, &pRealMemory, fSpyed);
196
197             /* check if can release the spy */
198             if(Malloc32.SpyReleasePending && !Malloc32.SpyedAllocationsLeft) {
199                 IMallocSpy_Release(Malloc32.pSpy);
200                 Malloc32.SpyReleasePending = FALSE;
201                 Malloc32.pSpy = NULL;
202             }
203
204             if (0==cb) {
205                 /* PreRealloc can force Realloc to fail */
206                 LeaveCriticalSection(&IMalloc32_SpyCS);
207                 return NULL;
208             }
209             pv = pRealMemory;
210         }
211
212         pNewMemory = HeapReAlloc(GetProcessHeap(),0,pv,cb);
213
214         if(Malloc32.pSpy) {
215             pNewMemory = IMallocSpy_PostRealloc(Malloc32.pSpy, pNewMemory, TRUE);
216             if (pNewMemory) AddMemoryLocation(pNewMemory);
217             LeaveCriticalSection(&IMalloc32_SpyCS);
218         }
219
220         TRACE("--(%p)\n",pNewMemory);
221         return pNewMemory;
222 }
223
224 /******************************************************************************
225  * IMalloc32_Free [VTABLE]
226  */
227 static VOID WINAPI IMalloc_fnFree(LPMALLOC iface,LPVOID pv) {
228
229         BOOL fSpyed = 0;
230
231         TRACE("(%p)\n",pv);
232
233         if(Malloc32.pSpy) {
234             EnterCriticalSection(&IMalloc32_SpyCS);
235             fSpyed = RemoveMemoryLocation(pv);
236             pv = IMallocSpy_PreFree(Malloc32.pSpy, pv, fSpyed);
237         }
238
239         HeapFree(GetProcessHeap(),0,pv);
240
241         if(Malloc32.pSpy) {
242             IMallocSpy_PostFree(Malloc32.pSpy, fSpyed);
243
244             /* check if can release the spy */
245             if(Malloc32.SpyReleasePending && !Malloc32.SpyedAllocationsLeft) {
246                 IMallocSpy_Release(Malloc32.pSpy);
247                 Malloc32.SpyReleasePending = FALSE;
248                 Malloc32.pSpy = NULL;
249             }
250
251             LeaveCriticalSection(&IMalloc32_SpyCS);
252         }
253 }
254
255 /******************************************************************************
256  * IMalloc32_GetSize [VTABLE]
257  *
258  * NOTES
259  *  FIXME returns:
260  *      win95:  size allocated (4 byte boundarys)
261  *      win2k:  size originally requested !!! (allocated on 8 byte boundarys)
262  */
263 static DWORD WINAPI IMalloc_fnGetSize(LPMALLOC iface,LPVOID pv) {
264
265         DWORD cb;
266         BOOL fSpyed = 0;
267
268         TRACE("(%p)\n",pv);
269
270         if(Malloc32.pSpy) {
271             EnterCriticalSection(&IMalloc32_SpyCS);
272             pv = IMallocSpy_PreGetSize(Malloc32.pSpy, pv, fSpyed);
273         }
274
275         cb = HeapSize(GetProcessHeap(),0,pv);
276
277         if(Malloc32.pSpy) {
278             cb = IMallocSpy_PostGetSize(Malloc32.pSpy, cb, fSpyed);
279             LeaveCriticalSection(&IMalloc32_SpyCS);
280         }
281
282         return cb;
283 }
284
285 /******************************************************************************
286  * IMalloc32_DidAlloc [VTABLE]
287  */
288 static INT WINAPI IMalloc_fnDidAlloc(LPMALLOC iface,LPVOID pv) {
289
290         BOOL fSpyed = 0;
291         int didAlloc;
292
293         TRACE("(%p)\n",pv);
294
295         if(Malloc32.pSpy) {
296             EnterCriticalSection(&IMalloc32_SpyCS);
297             pv = IMallocSpy_PreDidAlloc(Malloc32.pSpy, pv, fSpyed);
298         }
299
300         didAlloc = -1;
301
302         if(Malloc32.pSpy) {
303             didAlloc = IMallocSpy_PostDidAlloc(Malloc32.pSpy, pv, fSpyed, didAlloc);
304             LeaveCriticalSection(&IMalloc32_SpyCS);
305         }
306         return didAlloc;
307 }
308
309 /******************************************************************************
310  * IMalloc32_HeapMinimize [VTABLE]
311  */
312 static VOID WINAPI IMalloc_fnHeapMinimize(LPMALLOC iface) {
313         TRACE("()\n");
314
315         if(Malloc32.pSpy) {
316             EnterCriticalSection(&IMalloc32_SpyCS);
317             IMallocSpy_PreHeapMinimize(Malloc32.pSpy);
318         }
319
320         if(Malloc32.pSpy) {
321             IMallocSpy_PostHeapMinimize(Malloc32.pSpy);
322             LeaveCriticalSection(&IMalloc32_SpyCS);
323         }
324 }
325
326 static ICOM_VTABLE(IMalloc) VT_IMalloc32 =
327 {
328         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
329         IMalloc_fnQueryInterface,
330         IMalloc_fnAddRefRelease,
331         IMalloc_fnAddRefRelease,
332         IMalloc_fnAlloc,
333         IMalloc_fnRealloc,
334         IMalloc_fnFree,
335         IMalloc_fnGetSize,
336         IMalloc_fnDidAlloc,
337         IMalloc_fnHeapMinimize
338 };
339
340 /******************************************************************************
341  *      IMallocSpy implementation
342  *****************************************************************************/
343
344 /* set the vtable later */
345 extern ICOM_VTABLE(IMallocSpy) VT_IMallocSpy;
346
347 typedef struct {
348         ICOM_VFIELD(IMallocSpy);
349         DWORD ref;
350 } _MallocSpy;
351
352 /* this is the static object instance */
353 _MallocSpy MallocSpy = {&VT_IMallocSpy, 0};
354
355 /******************************************************************************
356  *      IMalloc32_QueryInterface        [VTABLE]
357  */
358 static HRESULT WINAPI IMallocSpy_fnQueryInterface(LPMALLOCSPY iface,REFIID refiid,LPVOID *obj)
359 {
360
361         TRACE("(%s,%p)\n",debugstr_guid(refiid),obj);
362
363         if (IsEqualIID(&IID_IUnknown,refiid) || IsEqualIID(&IID_IMallocSpy,refiid)) {
364                 *obj = (LPMALLOC)&MallocSpy;
365                 return S_OK;
366         }
367         return E_NOINTERFACE;
368 }
369
370 /******************************************************************************
371  *      IMalloc32_AddRef                [VTABLE]
372  */
373 static ULONG WINAPI IMallocSpy_fnAddRef (LPMALLOCSPY iface)
374 {
375
376     ICOM_THIS (_MallocSpy, iface);
377
378     TRACE ("(%p)->(count=%lu)\n", This, This->ref);
379
380     return ++(This->ref);
381 }
382
383 /******************************************************************************
384  *      IMalloc32_AddRelease            [VTABLE]
385  *
386  * NOTES
387  *   Our MallocSpy is static. If the count reaches 0 we dump the leaks
388  */
389 static ULONG WINAPI IMallocSpy_fnRelease (LPMALLOCSPY iface)
390 {
391
392     ICOM_THIS (_MallocSpy, iface);
393
394     TRACE ("(%p)->(count=%lu)\n", This, This->ref);
395
396     if (!--(This->ref)) {
397         /* our allocation list MUST be empty here */
398     }
399     return This->ref;
400 }
401
402 static ULONG WINAPI IMallocSpy_fnPreAlloc(LPMALLOCSPY iface, ULONG cbRequest)
403 {
404     ICOM_THIS (_MallocSpy, iface);
405     TRACE ("(%p)->(%lu)\n", This, cbRequest);
406     return cbRequest;
407 }
408 static PVOID WINAPI IMallocSpy_fnPostAlloc(LPMALLOCSPY iface, void* pActual)
409 {
410     ICOM_THIS (_MallocSpy, iface);
411     TRACE ("(%p)->(%p)\n", This, pActual);
412     return pActual;
413 }
414
415 static PVOID WINAPI IMallocSpy_fnPreFree(LPMALLOCSPY iface, void* pRequest, BOOL fSpyed)
416 {
417     ICOM_THIS (_MallocSpy, iface);
418     TRACE ("(%p)->(%p %u)\n", This, pRequest, fSpyed);
419     return pRequest;
420 }
421 static void  WINAPI IMallocSpy_fnPostFree(LPMALLOCSPY iface, BOOL fSpyed)
422 {
423     ICOM_THIS (_MallocSpy, iface);
424     TRACE ("(%p)->(%u)\n", This, fSpyed);
425 }
426
427 static ULONG WINAPI IMallocSpy_fnPreRealloc(LPMALLOCSPY iface, void* pRequest, ULONG cbRequest, void** ppNewRequest, BOOL fSpyed)
428 {
429     ICOM_THIS (_MallocSpy, iface);
430     TRACE ("(%p)->(%p %lu %u)\n", This, pRequest, cbRequest, fSpyed);
431     *ppNewRequest = pRequest;
432     return cbRequest;
433 }
434
435 static PVOID WINAPI IMallocSpy_fnPostRealloc(LPMALLOCSPY iface, void* pActual, BOOL fSpyed)
436 {
437     ICOM_THIS (_MallocSpy, iface);
438     TRACE ("(%p)->(%p %u)\n", This, pActual, fSpyed);
439     return pActual;
440 }
441
442 static PVOID WINAPI IMallocSpy_fnPreGetSize(LPMALLOCSPY iface, void* pRequest, BOOL fSpyed)
443 {
444     ICOM_THIS (_MallocSpy, iface);
445     TRACE ("(%p)->(%p %u)\n", This,  pRequest, fSpyed);
446     return pRequest;
447 }
448
449 static ULONG WINAPI IMallocSpy_fnPostGetSize(LPMALLOCSPY iface, ULONG cbActual, BOOL fSpyed)
450 {
451     ICOM_THIS (_MallocSpy, iface);
452     TRACE ("(%p)->(%lu %u)\n", This, cbActual, fSpyed);
453     return cbActual;
454 }
455
456 static PVOID WINAPI IMallocSpy_fnPreDidAlloc(LPMALLOCSPY iface, void* pRequest, BOOL fSpyed)
457 {
458     ICOM_THIS (_MallocSpy, iface);
459     TRACE ("(%p)->(%p %u)\n", This, pRequest, fSpyed);
460     return pRequest;
461 }
462
463 static int WINAPI IMallocSpy_fnPostDidAlloc(LPMALLOCSPY iface, void* pRequest, BOOL fSpyed, int fActual)
464 {
465     ICOM_THIS (_MallocSpy, iface);
466     TRACE ("(%p)->(%p %u %u)\n", This, pRequest, fSpyed, fActual);
467     return fActual;
468 }
469
470 static void WINAPI IMallocSpy_fnPreHeapMinimize(LPMALLOCSPY iface)
471 {
472     ICOM_THIS (_MallocSpy, iface);
473     TRACE ("(%p)->()\n", This);
474 }
475
476 static void WINAPI IMallocSpy_fnPostHeapMinimize(LPMALLOCSPY iface)
477 {
478     ICOM_THIS (_MallocSpy, iface);
479     TRACE ("(%p)->()\n", This);
480 }
481
482 static void MallocSpyDumpLeaks() {
483         TRACE("leaks: %lu\n", Malloc32.SpyedAllocationsLeft);
484 }
485
486 static ICOM_VTABLE(IMallocSpy) VT_IMallocSpy =
487 {
488         ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
489         IMallocSpy_fnQueryInterface,
490         IMallocSpy_fnAddRef,
491         IMallocSpy_fnRelease,
492         IMallocSpy_fnPreAlloc,
493         IMallocSpy_fnPostAlloc,
494         IMallocSpy_fnPreFree,
495         IMallocSpy_fnPostFree,
496         IMallocSpy_fnPreRealloc,
497         IMallocSpy_fnPostRealloc,
498         IMallocSpy_fnPreGetSize,
499         IMallocSpy_fnPostGetSize,
500         IMallocSpy_fnPreDidAlloc,
501         IMallocSpy_fnPostDidAlloc,
502         IMallocSpy_fnPreHeapMinimize,
503         IMallocSpy_fnPostHeapMinimize
504 };
505
506 /******************************************************************************
507  *              CoGetMalloc     [OLE32.@]
508  *
509  * RETURNS
510  *      The win32 IMalloc
511  */
512 HRESULT WINAPI CoGetMalloc(DWORD dwMemContext, LPMALLOC *lpMalloc)
513 {
514         *lpMalloc = (LPMALLOC)&Malloc32;
515         return S_OK;
516 }
517
518 /***********************************************************************
519  *           CoTaskMemAlloc     [OLE32.@]
520  * RETURNS
521  *      pointer to newly allocated block
522  */
523 LPVOID WINAPI CoTaskMemAlloc(ULONG size)
524 {
525         return IMalloc_Alloc((LPMALLOC)&Malloc32,size);
526 }
527 /***********************************************************************
528  *           CoTaskMemFree      [OLE32.@]
529  */
530 VOID WINAPI CoTaskMemFree(LPVOID ptr)
531 {
532         IMalloc_Free((LPMALLOC)&Malloc32, ptr);
533 }
534
535 /***********************************************************************
536  *           CoTaskMemRealloc   [OLE32.@]
537  * RETURNS
538  *      pointer to newly allocated block
539  */
540 LPVOID WINAPI CoTaskMemRealloc(LPVOID pvOld, ULONG size)
541 {
542         return IMalloc_Realloc((LPMALLOC)&Malloc32, pvOld, size);
543 }
544
545 /***********************************************************************
546  *           CoRegisterMallocSpy        [OLE32.@]
547  *
548  * NOTES
549  *  if a mallocspy is already registered, we cant do it again since
550  *  only the spy knows, how to free a memory block
551  */
552 HRESULT WINAPI CoRegisterMallocSpy(LPMALLOCSPY pMallocSpy)
553 {
554         IMallocSpy* pSpy;
555         HRESULT hres = E_INVALIDARG;
556
557         TRACE("\n");
558
559         /* HACK TO ACTIVATE OUT SPY */
560         if (pMallocSpy == (LPVOID)-1) pMallocSpy =(IMallocSpy*)&MallocSpy;
561
562         if(Malloc32.pSpy) return CO_E_OBJISREG;
563
564         EnterCriticalSection(&IMalloc32_SpyCS);
565
566         if (SUCCEEDED(IUnknown_QueryInterface(pMallocSpy, &IID_IMallocSpy, (LPVOID*)&pSpy))) {
567             Malloc32.pSpy = pSpy;
568             hres = S_OK;
569         }
570
571         LeaveCriticalSection(&IMalloc32_SpyCS);
572
573         return hres;
574 }
575
576 /***********************************************************************
577  *           CoRevokeMallocSpy  [OLE32.@]
578  *
579  * NOTES
580  *  we can't rewoke a malloc spy as long as memory blocks allocated with
581  *  the spy are active since only the spy knows how to free them
582  */
583 HRESULT WINAPI CoRevokeMallocSpy(void)
584 {
585         HRESULT hres = S_OK;
586         TRACE("\n");
587
588         EnterCriticalSection(&IMalloc32_SpyCS);
589
590         /* if it's our spy it's time to dump the leaks */
591         if (Malloc32.pSpy == (IMallocSpy*)&MallocSpy) {
592             MallocSpyDumpLeaks();
593         }
594
595         if (Malloc32.SpyedAllocationsLeft) {
596             TRACE("SpyReleasePending with %lu allocations left\n", Malloc32.SpyedAllocationsLeft);
597             Malloc32.SpyReleasePending = TRUE;
598             hres = E_ACCESSDENIED;
599         } else {
600             IMallocSpy_Release(Malloc32.pSpy);
601             Malloc32.pSpy = NULL;
602         }
603         LeaveCriticalSection(&IMalloc32_SpyCS);
604
605         return S_OK;
606 }
607
608 /******************************************************************************
609  *              IsValidInterface        [OLE32.@]
610  *
611  * RETURNS
612  *  True, if the passed pointer is a valid interface
613  */
614 BOOL WINAPI IsValidInterface(
615         LPUNKNOWN punk  /* [in] interface to be tested */
616 ) {
617         return !(
618                 IsBadReadPtr(punk,4)                                    ||
619                 IsBadReadPtr(punk->lpVtbl,4)                            ||
620                 IsBadReadPtr(punk->lpVtbl->QueryInterface,9)    ||
621                 IsBadCodePtr((FARPROC)punk->lpVtbl->QueryInterface)
622         );
623 }