[OLE32] Sync with Wine Staging 3.3. CORE-14434
[reactos.git] / dll / win32 / 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., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, 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 #define COBJMACROS
30
31 #include "windef.h"
32 #include "winbase.h"
33 #include "winuser.h"
34 #include "ole2.h"
35 #include "winerror.h"
36
37 #include "wine/debug.h"
38
39 WINE_DEFAULT_DEBUG_CHANNEL(olemalloc);
40
41 /******************************************************************************
42 * IMalloc32 implementation
43 *
44 * NOTES
45 * For supporting CoRegisterMallocSpy the IMalloc implementation must know if
46 * a given memory block was allocated with a spy active.
47 *
48 *****************************************************************************/
49 /* set the vtable later */
50 static const IMallocVtbl VT_IMalloc32;
51
52 typedef struct {
53 IMalloc IMalloc_iface;
54 DWORD dummy; /* nothing, we are static */
55 IMallocSpy * pSpy; /* the spy when active */
56 DWORD SpyedAllocationsLeft; /* number of spyed allocations left */
57 BOOL SpyReleasePending; /* CoRevokeMallocSpy called with spyed allocations left*/
58 LPVOID * SpyedBlocks; /* root of the table */
59 DWORD SpyedBlockTableLength;/* size of the table*/
60 } _Malloc32;
61
62 /* this is the static object instance */
63 static _Malloc32 Malloc32 = {{&VT_IMalloc32}, 0, NULL, 0, 0, NULL, 0};
64
65 /* with a spy active all calls from pre to post methods are threadsave */
66 static CRITICAL_SECTION IMalloc32_SpyCS;
67 static CRITICAL_SECTION_DEBUG critsect_debug =
68 {
69 0, 0, &IMalloc32_SpyCS,
70 { &critsect_debug.ProcessLocksList, &critsect_debug.ProcessLocksList },
71 0, 0, { (DWORD_PTR)(__FILE__ ": IMalloc32_SpyCS") }
72 };
73 static CRITICAL_SECTION IMalloc32_SpyCS = { &critsect_debug, -1, 0, 0, 0, 0 };
74
75 /* resize the old table */
76 static BOOL SetSpyedBlockTableLength ( DWORD NewLength )
77 {
78 LPVOID *NewSpyedBlocks;
79
80 if (!Malloc32.SpyedBlocks) NewSpyedBlocks = LocalAlloc(LMEM_ZEROINIT, NewLength * sizeof(PVOID));
81 else NewSpyedBlocks = LocalReAlloc(Malloc32.SpyedBlocks, NewLength * sizeof(PVOID), LMEM_ZEROINIT | LMEM_MOVEABLE);
82 if (NewSpyedBlocks) {
83 Malloc32.SpyedBlocks = NewSpyedBlocks;
84 Malloc32.SpyedBlockTableLength = NewLength;
85 }
86
87 return NewSpyedBlocks != NULL;
88 }
89
90 /* add a location to the table */
91 static BOOL AddMemoryLocation(LPVOID * pMem)
92 {
93 LPVOID * Current;
94
95 /* allocate the table if not already allocated */
96 if (!Malloc32.SpyedBlockTableLength && !SetSpyedBlockTableLength(0x1000))
97 return FALSE;
98
99 /* find a free location */
100 Current = Malloc32.SpyedBlocks;
101 while (*Current) {
102 Current++;
103 if (Current >= Malloc32.SpyedBlocks + Malloc32.SpyedBlockTableLength) {
104 /* no more space in table, grow it */
105 DWORD old_length = Malloc32.SpyedBlockTableLength;
106 if (!SetSpyedBlockTableLength( Malloc32.SpyedBlockTableLength + 0x1000))
107 return FALSE;
108 Current = Malloc32.SpyedBlocks + old_length;
109 }
110 };
111
112 /* put the location in our table */
113 *Current = pMem;
114 Malloc32.SpyedAllocationsLeft++;
115 /*TRACE("%lu\n",Malloc32.SpyedAllocationsLeft);*/
116 return TRUE;
117 }
118
119 static BOOL RemoveMemoryLocation(LPCVOID pMem)
120 {
121 LPVOID * Current;
122
123 /* allocate the table if not already allocated */
124 if (!Malloc32.SpyedBlockTableLength && !SetSpyedBlockTableLength(0x1000))
125 return FALSE;
126
127 Current = Malloc32.SpyedBlocks;
128
129 /* find the location */
130 while (*Current != pMem) {
131 Current++;
132 if (Current >= Malloc32.SpyedBlocks + Malloc32.SpyedBlockTableLength)
133 return FALSE; /* not found */
134 }
135
136 /* location found */
137 Malloc32.SpyedAllocationsLeft--;
138 /*TRACE("%lu\n",Malloc32.SpyedAllocationsLeft);*/
139 *Current = NULL;
140 return TRUE;
141 }
142
143 /******************************************************************************
144 * IMalloc32_QueryInterface [VTABLE]
145 */
146 static HRESULT WINAPI IMalloc_fnQueryInterface(IMalloc *iface, REFIID refiid, void **obj)
147 {
148 TRACE("(%s,%p)\n",debugstr_guid(refiid),obj);
149
150 if (IsEqualIID(&IID_IUnknown,refiid) || IsEqualIID(&IID_IMalloc,refiid)) {
151 *obj = &Malloc32;
152 return S_OK;
153 }
154 return E_NOINTERFACE;
155 }
156
157 /******************************************************************************
158 * IMalloc32_AddRefRelease [VTABLE]
159 */
160 static ULONG WINAPI IMalloc_fnAddRefRelease(IMalloc *iface)
161 {
162 return 1;
163 }
164
165 /******************************************************************************
166 * IMalloc32_Alloc [VTABLE]
167 */
168 static void * WINAPI IMalloc_fnAlloc(IMalloc *iface, SIZE_T cb)
169 {
170 void *addr;
171
172 TRACE("(%ld)\n",cb);
173
174 if(Malloc32.pSpy) {
175 SIZE_T preAllocResult;
176
177 EnterCriticalSection(&IMalloc32_SpyCS);
178 preAllocResult = IMallocSpy_PreAlloc(Malloc32.pSpy, cb);
179 if ((cb != 0) && (preAllocResult == 0)) {
180 /* PreAlloc can force Alloc to fail, but not if cb == 0 */
181 TRACE("returning null\n");
182 LeaveCriticalSection(&IMalloc32_SpyCS);
183 return NULL;
184 }
185 }
186
187 addr = HeapAlloc(GetProcessHeap(),0,cb);
188
189 if(Malloc32.pSpy) {
190 addr = IMallocSpy_PostAlloc(Malloc32.pSpy, addr);
191 if (addr) AddMemoryLocation(addr);
192 LeaveCriticalSection(&IMalloc32_SpyCS);
193 }
194
195 TRACE("--(%p)\n",addr);
196 return addr;
197 }
198
199 /******************************************************************************
200 * IMalloc32_Realloc [VTABLE]
201 */
202 static void * WINAPI IMalloc_fnRealloc(IMalloc *iface, void *pv, SIZE_T cb)
203 {
204 void *pNewMemory;
205
206 TRACE("(%p,%ld)\n",pv,cb);
207
208 if(Malloc32.pSpy) {
209 void *pRealMemory;
210 BOOL fSpyed;
211
212 EnterCriticalSection(&IMalloc32_SpyCS);
213 fSpyed = RemoveMemoryLocation(pv);
214 cb = IMallocSpy_PreRealloc(Malloc32.pSpy, pv, cb, &pRealMemory, fSpyed);
215
216 /* check if can release the spy */
217 if(Malloc32.SpyReleasePending && !Malloc32.SpyedAllocationsLeft) {
218 IMallocSpy_Release(Malloc32.pSpy);
219 Malloc32.SpyReleasePending = FALSE;
220 Malloc32.pSpy = NULL;
221 LeaveCriticalSection(&IMalloc32_SpyCS);
222 }
223
224 if (0==cb) {
225 /* PreRealloc can force Realloc to fail */
226 if (Malloc32.pSpy)
227 LeaveCriticalSection(&IMalloc32_SpyCS);
228 return NULL;
229 }
230
231 pv = pRealMemory;
232 }
233
234 if (!pv) pNewMemory = HeapAlloc(GetProcessHeap(),0,cb);
235 else if (cb) pNewMemory = HeapReAlloc(GetProcessHeap(),0,pv,cb);
236 else {
237 HeapFree(GetProcessHeap(),0,pv);
238 pNewMemory = NULL;
239 }
240
241 if(Malloc32.pSpy) {
242 pNewMemory = IMallocSpy_PostRealloc(Malloc32.pSpy, pNewMemory, TRUE);
243 if (pNewMemory) AddMemoryLocation(pNewMemory);
244 LeaveCriticalSection(&IMalloc32_SpyCS);
245 }
246
247 TRACE("--(%p)\n",pNewMemory);
248 return pNewMemory;
249 }
250
251 /******************************************************************************
252 * IMalloc32_Free [VTABLE]
253 */
254 static void WINAPI IMalloc_fnFree(IMalloc *iface, void *pv)
255 {
256 BOOL fSpyed = FALSE;
257
258 TRACE("(%p)\n",pv);
259
260 if(!pv)
261 return;
262
263 if(Malloc32.pSpy) {
264 EnterCriticalSection(&IMalloc32_SpyCS);
265 fSpyed = RemoveMemoryLocation(pv);
266 pv = IMallocSpy_PreFree(Malloc32.pSpy, pv, fSpyed);
267 }
268
269 HeapFree(GetProcessHeap(),0,pv);
270
271 if(Malloc32.pSpy) {
272 IMallocSpy_PostFree(Malloc32.pSpy, fSpyed);
273
274 /* check if can release the spy */
275 if(Malloc32.SpyReleasePending && !Malloc32.SpyedAllocationsLeft) {
276 IMallocSpy_Release(Malloc32.pSpy);
277 Malloc32.SpyReleasePending = FALSE;
278 Malloc32.pSpy = NULL;
279 }
280
281 LeaveCriticalSection(&IMalloc32_SpyCS);
282 }
283 }
284
285 /******************************************************************************
286 * IMalloc32_GetSize [VTABLE]
287 *
288 * NOTES
289 * FIXME returns:
290 * win95: size allocated (4 byte boundarys)
291 * win2k: size originally requested !!! (allocated on 8 byte boundarys)
292 */
293 static SIZE_T WINAPI IMalloc_fnGetSize(IMalloc *iface, void *pv)
294 {
295 SIZE_T cb;
296 BOOL fSpyed = FALSE;
297
298 TRACE("(%p)\n",pv);
299
300 if(Malloc32.pSpy) {
301 EnterCriticalSection(&IMalloc32_SpyCS);
302 pv = IMallocSpy_PreGetSize(Malloc32.pSpy, pv, fSpyed);
303 }
304
305 cb = HeapSize(GetProcessHeap(),0,pv);
306
307 if(Malloc32.pSpy) {
308 cb = IMallocSpy_PostGetSize(Malloc32.pSpy, cb, fSpyed);
309 LeaveCriticalSection(&IMalloc32_SpyCS);
310 }
311
312 return cb;
313 }
314
315 /******************************************************************************
316 * IMalloc32_DidAlloc [VTABLE]
317 */
318 static INT WINAPI IMalloc_fnDidAlloc(IMalloc *iface, void *pv)
319 {
320 BOOL fSpyed = FALSE;
321 int didAlloc;
322
323 TRACE("(%p)\n",pv);
324
325 if(Malloc32.pSpy) {
326 EnterCriticalSection(&IMalloc32_SpyCS);
327 pv = IMallocSpy_PreDidAlloc(Malloc32.pSpy, pv, fSpyed);
328 }
329
330 didAlloc = -1;
331
332 if(Malloc32.pSpy) {
333 didAlloc = IMallocSpy_PostDidAlloc(Malloc32.pSpy, pv, fSpyed, didAlloc);
334 LeaveCriticalSection(&IMalloc32_SpyCS);
335 }
336 return didAlloc;
337 }
338
339 /******************************************************************************
340 * IMalloc32_HeapMinimize [VTABLE]
341 */
342 static void WINAPI IMalloc_fnHeapMinimize(IMalloc *iface)
343 {
344 TRACE("()\n");
345
346 if(Malloc32.pSpy) {
347 EnterCriticalSection(&IMalloc32_SpyCS);
348 IMallocSpy_PreHeapMinimize(Malloc32.pSpy);
349 }
350
351 if(Malloc32.pSpy) {
352 IMallocSpy_PostHeapMinimize(Malloc32.pSpy);
353 LeaveCriticalSection(&IMalloc32_SpyCS);
354 }
355 }
356
357 static const IMallocVtbl VT_IMalloc32 =
358 {
359 IMalloc_fnQueryInterface,
360 IMalloc_fnAddRefRelease,
361 IMalloc_fnAddRefRelease,
362 IMalloc_fnAlloc,
363 IMalloc_fnRealloc,
364 IMalloc_fnFree,
365 IMalloc_fnGetSize,
366 IMalloc_fnDidAlloc,
367 IMalloc_fnHeapMinimize
368 };
369
370 /******************************************************************************
371 * CoGetMalloc [OLE32.@]
372 *
373 * Retrieves the current IMalloc interface for the process.
374 *
375 * PARAMS
376 * context [I] Should always be MEMCTX_TASK.
377 * imalloc [O] Address where memory allocator object will be stored.
378 *
379 * RETURNS
380 * Success: S_OK.
381 * Failure: HRESULT code.
382 */
383 HRESULT WINAPI CoGetMalloc(DWORD context, IMalloc **imalloc)
384 {
385 if (context != MEMCTX_TASK) {
386 *imalloc = NULL;
387 return E_INVALIDARG;
388 }
389
390 *imalloc = &Malloc32.IMalloc_iface;
391 return S_OK;
392 }
393
394 /***********************************************************************
395 * CoTaskMemAlloc [OLE32.@]
396 *
397 * Allocates memory using the current process memory allocator.
398 *
399 * PARAMS
400 * size [I] Size of the memory block to allocate.
401 *
402 * RETURNS
403 * Success: Pointer to newly allocated memory block.
404 * Failure: NULL.
405 */
406 LPVOID WINAPI CoTaskMemAlloc(SIZE_T size)
407 {
408 return IMalloc_Alloc(&Malloc32.IMalloc_iface,size);
409 }
410
411 /***********************************************************************
412 * CoTaskMemFree [OLE32.@]
413 *
414 * Frees memory allocated from the current process memory allocator.
415 *
416 * PARAMS
417 * ptr [I] Memory block to free.
418 *
419 * RETURNS
420 * Nothing.
421 */
422 VOID WINAPI CoTaskMemFree(LPVOID ptr)
423 {
424 IMalloc_Free(&Malloc32.IMalloc_iface, ptr);
425 }
426
427 /***********************************************************************
428 * CoTaskMemRealloc [OLE32.@]
429 *
430 * Allocates memory using the current process memory allocator.
431 *
432 * PARAMS
433 * pvOld [I] Pointer to old memory block.
434 * size [I] Size of the new memory block.
435 *
436 * RETURNS
437 * Success: Pointer to newly allocated memory block.
438 * Failure: NULL.
439 */
440 LPVOID WINAPI CoTaskMemRealloc(LPVOID pvOld, SIZE_T size)
441 {
442 return IMalloc_Realloc(&Malloc32.IMalloc_iface, pvOld, size);
443 }
444
445 /***********************************************************************
446 * CoRegisterMallocSpy [OLE32.@]
447 *
448 * Registers an object that receives notifications on memory allocations and
449 * frees.
450 *
451 * PARAMS
452 * pMallocSpy [I] New spy object.
453 *
454 * RETURNS
455 * Success: S_OK.
456 * Failure: HRESULT code.
457 *
458 * NOTES
459 * if a mallocspy is already registered, we can't do it again since
460 * only the spy knows, how to free a memory block
461 */
462 HRESULT WINAPI CoRegisterMallocSpy(LPMALLOCSPY pMallocSpy)
463 {
464 IMallocSpy* pSpy;
465 HRESULT hres = E_INVALIDARG;
466
467 TRACE("%p\n", pMallocSpy);
468
469 if(!pMallocSpy) return E_INVALIDARG;
470
471 EnterCriticalSection(&IMalloc32_SpyCS);
472
473 if (Malloc32.pSpy)
474 hres = CO_E_OBJISREG;
475 else if (SUCCEEDED(IMallocSpy_QueryInterface(pMallocSpy, &IID_IMallocSpy, (void**)&pSpy))) {
476 Malloc32.pSpy = pSpy;
477 hres = S_OK;
478 }
479
480 LeaveCriticalSection(&IMalloc32_SpyCS);
481
482 return hres;
483 }
484
485 /***********************************************************************
486 * CoRevokeMallocSpy [OLE32.@]
487 *
488 * Revokes a previously registered object that receives notifications on memory
489 * allocations and frees.
490 *
491 * PARAMS
492 * pMallocSpy [I] New spy object.
493 *
494 * RETURNS
495 * Success: S_OK.
496 * Failure: HRESULT code.
497 *
498 * NOTES
499 * we can't revoke a malloc spy as long as memory blocks allocated with
500 * the spy are active since only the spy knows how to free them
501 */
502 HRESULT WINAPI CoRevokeMallocSpy(void)
503 {
504 HRESULT hres = S_OK;
505 TRACE("\n");
506
507 EnterCriticalSection(&IMalloc32_SpyCS);
508
509 if (!Malloc32.pSpy)
510 hres = CO_E_OBJNOTREG;
511 else if (Malloc32.SpyedAllocationsLeft) {
512 TRACE("SpyReleasePending with %u allocations left\n", Malloc32.SpyedAllocationsLeft);
513 Malloc32.SpyReleasePending = TRUE;
514 hres = E_ACCESSDENIED;
515 } else {
516 IMallocSpy_Release(Malloc32.pSpy);
517 Malloc32.pSpy = NULL;
518 }
519 LeaveCriticalSection(&IMalloc32_SpyCS);
520
521 return hres;
522 }
523
524 /******************************************************************************
525 * IsValidInterface [OLE32.@]
526 *
527 * Determines whether a pointer is a valid interface.
528 *
529 * PARAMS
530 * punk [I] Interface to be tested.
531 *
532 * RETURNS
533 * TRUE, if the passed pointer is a valid interface, or FALSE otherwise.
534 */
535 BOOL WINAPI IsValidInterface(LPUNKNOWN punk)
536 {
537 return !(
538 IsBadReadPtr(punk,4) ||
539 IsBadReadPtr(punk->lpVtbl,4) ||
540 IsBadReadPtr(punk->lpVtbl->QueryInterface,9) ||
541 IsBadCodePtr((FARPROC)punk->lpVtbl->QueryInterface)
542 );
543 }