Swap arguments of LocalAlloc and only update Malloc32 block on successful
[reactos.git] / reactos / lib / 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 LPVOID *NewSpyedBlocks;
77
78 if (!Malloc32.SpyedBlocks) NewSpyedBlocks = (LPVOID*)LocalAlloc(GMEM_ZEROINIT, NewLength);
79 else NewSpyedBlocks = (LPVOID*)LocalReAlloc((HLOCAL)Malloc32.SpyedBlocks, NewLength, GMEM_ZEROINIT);
80 if (NewSpyedBlocks) {
81 Malloc32.SpyedBlocks = NewSpyedBlocks;
82 Malloc32.SpyedBlockTableLength = NewLength;
83 }
84
85 return NewSpyedBlocks ? 1 : 0;
86 }
87
88 /* add a location to the table */
89 static int AddMemoryLocation(LPVOID * pMem)
90 {
91 LPVOID * Current;
92
93 /* allocate the table if not already allocated */
94 if (!Malloc32.SpyedBlockTableLength) {
95 if (!SetSpyedBlockTableLength(0x1000)) return 0;
96 }
97
98 /* find a free location */
99 Current = Malloc32.SpyedBlocks;
100 while (*Current) {
101 Current++;
102 if (Current >= Malloc32.SpyedBlocks + Malloc32.SpyedBlockTableLength) {
103 /* no more space in table, grow it */
104 if (!SetSpyedBlockTableLength( Malloc32.SpyedBlockTableLength + 0x1000 )) return 0;
105 }
106 };
107
108 /* put the location in our table */
109 *Current = pMem;
110 Malloc32.SpyedAllocationsLeft++;
111 /*TRACE("%lu\n",Malloc32.SpyedAllocationsLeft);*/
112 return 1;
113 }
114
115 static int RemoveMemoryLocation(LPVOID * pMem)
116 {
117 LPVOID * Current;
118
119 /* allocate the table if not already allocated */
120 if (!Malloc32.SpyedBlockTableLength) {
121 if (!SetSpyedBlockTableLength(0x1000)) return 0;
122 }
123
124 Current = Malloc32.SpyedBlocks;
125
126 /* find the location */
127 while (*Current != pMem) {
128 Current++;
129 if (Current >= Malloc32.SpyedBlocks + Malloc32.SpyedBlockTableLength) return 0; /* not found */
130 }
131
132 /* location found */
133 Malloc32.SpyedAllocationsLeft--;
134 /*TRACE("%lu\n",Malloc32.SpyedAllocationsLeft);*/
135 *Current = NULL;
136 return 1;
137 }
138
139 /******************************************************************************
140 * IMalloc32_QueryInterface [VTABLE]
141 */
142 static HRESULT WINAPI IMalloc_fnQueryInterface(LPMALLOC iface,REFIID refiid,LPVOID *obj) {
143
144 TRACE("(%s,%p)\n",debugstr_guid(refiid),obj);
145
146 if (IsEqualIID(&IID_IUnknown,refiid) || IsEqualIID(&IID_IMalloc,refiid)) {
147 *obj = (LPMALLOC)&Malloc32;
148 return S_OK;
149 }
150 return E_NOINTERFACE;
151 }
152
153 /******************************************************************************
154 * IMalloc32_AddRefRelease [VTABLE]
155 */
156 static ULONG WINAPI IMalloc_fnAddRefRelease (LPMALLOC iface) {
157 return 1;
158 }
159
160 /******************************************************************************
161 * IMalloc32_Alloc [VTABLE]
162 */
163 static LPVOID WINAPI IMalloc_fnAlloc(LPMALLOC iface, DWORD cb) {
164
165 LPVOID addr;
166
167 TRACE("(%ld)\n",cb);
168
169 if(Malloc32.pSpy) {
170 DWORD preAllocResult;
171
172 EnterCriticalSection(&IMalloc32_SpyCS);
173 preAllocResult = IMallocSpy_PreAlloc(Malloc32.pSpy, cb);
174 if ((cb != 0) && (preAllocResult == 0)) {
175 /* PreAlloc can force Alloc to fail, but not if cb == 0 */
176 TRACE("returning null\n");
177 LeaveCriticalSection(&IMalloc32_SpyCS);
178 return NULL;
179 }
180 }
181
182 addr = HeapAlloc(GetProcessHeap(),0,cb);
183
184 if(Malloc32.pSpy) {
185 addr = IMallocSpy_PostAlloc(Malloc32.pSpy, addr);
186 if (addr) AddMemoryLocation(addr);
187 LeaveCriticalSection(&IMalloc32_SpyCS);
188 }
189
190 TRACE("--(%p)\n",addr);
191 return addr;
192 }
193
194 /******************************************************************************
195 * IMalloc32_Realloc [VTABLE]
196 */
197 static LPVOID WINAPI IMalloc_fnRealloc(LPMALLOC iface,LPVOID pv,DWORD cb) {
198
199 LPVOID pNewMemory;
200
201 TRACE("(%p,%ld)\n",pv,cb);
202
203 if(Malloc32.pSpy) {
204 LPVOID pRealMemory;
205 BOOL fSpyed;
206
207 EnterCriticalSection(&IMalloc32_SpyCS);
208 fSpyed = RemoveMemoryLocation(pv);
209 cb = IMallocSpy_PreRealloc(Malloc32.pSpy, pv, cb, &pRealMemory, fSpyed);
210
211 /* check if can release the spy */
212 if(Malloc32.SpyReleasePending && !Malloc32.SpyedAllocationsLeft) {
213 IMallocSpy_Release(Malloc32.pSpy);
214 Malloc32.SpyReleasePending = FALSE;
215 Malloc32.pSpy = NULL;
216 }
217
218 if (0==cb) {
219 /* PreRealloc can force Realloc to fail */
220 LeaveCriticalSection(&IMalloc32_SpyCS);
221 return NULL;
222 }
223 pv = pRealMemory;
224 }
225
226 if (!pv) pNewMemory = HeapAlloc(GetProcessHeap(),0,cb);
227 else if (cb) pNewMemory = HeapReAlloc(GetProcessHeap(),0,pv,cb);
228 else {
229 HeapFree(GetProcessHeap(),0,pv);
230 pNewMemory = NULL;
231 }
232
233 if(Malloc32.pSpy) {
234 pNewMemory = IMallocSpy_PostRealloc(Malloc32.pSpy, pNewMemory, TRUE);
235 if (pNewMemory) AddMemoryLocation(pNewMemory);
236 LeaveCriticalSection(&IMalloc32_SpyCS);
237 }
238
239 TRACE("--(%p)\n",pNewMemory);
240 return pNewMemory;
241 }
242
243 /******************************************************************************
244 * IMalloc32_Free [VTABLE]
245 */
246 static VOID WINAPI IMalloc_fnFree(LPMALLOC iface,LPVOID pv) {
247
248 BOOL fSpyed = 0;
249
250 TRACE("(%p)\n",pv);
251
252 if(Malloc32.pSpy) {
253 EnterCriticalSection(&IMalloc32_SpyCS);
254 fSpyed = RemoveMemoryLocation(pv);
255 pv = IMallocSpy_PreFree(Malloc32.pSpy, pv, fSpyed);
256 }
257
258 HeapFree(GetProcessHeap(),0,pv);
259
260 if(Malloc32.pSpy) {
261 IMallocSpy_PostFree(Malloc32.pSpy, fSpyed);
262
263 /* check if can release the spy */
264 if(Malloc32.SpyReleasePending && !Malloc32.SpyedAllocationsLeft) {
265 IMallocSpy_Release(Malloc32.pSpy);
266 Malloc32.SpyReleasePending = FALSE;
267 Malloc32.pSpy = NULL;
268 }
269
270 LeaveCriticalSection(&IMalloc32_SpyCS);
271 }
272 }
273
274 /******************************************************************************
275 * IMalloc32_GetSize [VTABLE]
276 *
277 * NOTES
278 * FIXME returns:
279 * win95: size allocated (4 byte boundarys)
280 * win2k: size originally requested !!! (allocated on 8 byte boundarys)
281 */
282 static DWORD WINAPI IMalloc_fnGetSize(LPMALLOC iface,LPVOID pv) {
283
284 DWORD cb;
285 BOOL fSpyed = 0;
286
287 TRACE("(%p)\n",pv);
288
289 if(Malloc32.pSpy) {
290 EnterCriticalSection(&IMalloc32_SpyCS);
291 pv = IMallocSpy_PreGetSize(Malloc32.pSpy, pv, fSpyed);
292 }
293
294 cb = HeapSize(GetProcessHeap(),0,pv);
295
296 if(Malloc32.pSpy) {
297 cb = IMallocSpy_PostGetSize(Malloc32.pSpy, cb, fSpyed);
298 LeaveCriticalSection(&IMalloc32_SpyCS);
299 }
300
301 return cb;
302 }
303
304 /******************************************************************************
305 * IMalloc32_DidAlloc [VTABLE]
306 */
307 static INT WINAPI IMalloc_fnDidAlloc(LPMALLOC iface,LPVOID pv) {
308
309 BOOL fSpyed = 0;
310 int didAlloc;
311
312 TRACE("(%p)\n",pv);
313
314 if(Malloc32.pSpy) {
315 EnterCriticalSection(&IMalloc32_SpyCS);
316 pv = IMallocSpy_PreDidAlloc(Malloc32.pSpy, pv, fSpyed);
317 }
318
319 didAlloc = -1;
320
321 if(Malloc32.pSpy) {
322 didAlloc = IMallocSpy_PostDidAlloc(Malloc32.pSpy, pv, fSpyed, didAlloc);
323 LeaveCriticalSection(&IMalloc32_SpyCS);
324 }
325 return didAlloc;
326 }
327
328 /******************************************************************************
329 * IMalloc32_HeapMinimize [VTABLE]
330 */
331 static VOID WINAPI IMalloc_fnHeapMinimize(LPMALLOC iface) {
332 TRACE("()\n");
333
334 if(Malloc32.pSpy) {
335 EnterCriticalSection(&IMalloc32_SpyCS);
336 IMallocSpy_PreHeapMinimize(Malloc32.pSpy);
337 }
338
339 if(Malloc32.pSpy) {
340 IMallocSpy_PostHeapMinimize(Malloc32.pSpy);
341 LeaveCriticalSection(&IMalloc32_SpyCS);
342 }
343 }
344
345 static ICOM_VTABLE(IMalloc) VT_IMalloc32 =
346 {
347 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
348 IMalloc_fnQueryInterface,
349 IMalloc_fnAddRefRelease,
350 IMalloc_fnAddRefRelease,
351 IMalloc_fnAlloc,
352 IMalloc_fnRealloc,
353 IMalloc_fnFree,
354 IMalloc_fnGetSize,
355 IMalloc_fnDidAlloc,
356 IMalloc_fnHeapMinimize
357 };
358
359 /******************************************************************************
360 * IMallocSpy implementation
361 *****************************************************************************/
362
363 /* set the vtable later */
364 extern ICOM_VTABLE(IMallocSpy) VT_IMallocSpy;
365
366 typedef struct {
367 ICOM_VFIELD(IMallocSpy);
368 DWORD ref;
369 } _MallocSpy;
370
371 /* this is the static object instance */
372 _MallocSpy MallocSpy = {&VT_IMallocSpy, 0};
373
374 /******************************************************************************
375 * IMalloc32_QueryInterface [VTABLE]
376 */
377 static HRESULT WINAPI IMallocSpy_fnQueryInterface(LPMALLOCSPY iface,REFIID refiid,LPVOID *obj)
378 {
379
380 TRACE("(%s,%p)\n",debugstr_guid(refiid),obj);
381
382 if (IsEqualIID(&IID_IUnknown,refiid) || IsEqualIID(&IID_IMallocSpy,refiid)) {
383 *obj = (LPMALLOC)&MallocSpy;
384 return S_OK;
385 }
386 return E_NOINTERFACE;
387 }
388
389 /******************************************************************************
390 * IMalloc32_AddRef [VTABLE]
391 */
392 static ULONG WINAPI IMallocSpy_fnAddRef (LPMALLOCSPY iface)
393 {
394
395 ICOM_THIS (_MallocSpy, iface);
396
397 TRACE ("(%p)->(count=%lu)\n", This, This->ref);
398
399 return ++(This->ref);
400 }
401
402 /******************************************************************************
403 * IMalloc32_AddRelease [VTABLE]
404 *
405 * NOTES
406 * Our MallocSpy is static. If the count reaches 0 we dump the leaks
407 */
408 static ULONG WINAPI IMallocSpy_fnRelease (LPMALLOCSPY iface)
409 {
410
411 ICOM_THIS (_MallocSpy, iface);
412
413 TRACE ("(%p)->(count=%lu)\n", This, This->ref);
414
415 if (!--(This->ref)) {
416 /* our allocation list MUST be empty here */
417 }
418 return This->ref;
419 }
420
421 static ULONG WINAPI IMallocSpy_fnPreAlloc(LPMALLOCSPY iface, ULONG cbRequest)
422 {
423 ICOM_THIS (_MallocSpy, iface);
424 TRACE ("(%p)->(%lu)\n", This, cbRequest);
425 return cbRequest;
426 }
427 static PVOID WINAPI IMallocSpy_fnPostAlloc(LPMALLOCSPY iface, void* pActual)
428 {
429 ICOM_THIS (_MallocSpy, iface);
430 TRACE ("(%p)->(%p)\n", This, pActual);
431 return pActual;
432 }
433
434 static PVOID WINAPI IMallocSpy_fnPreFree(LPMALLOCSPY iface, void* pRequest, BOOL fSpyed)
435 {
436 ICOM_THIS (_MallocSpy, iface);
437 TRACE ("(%p)->(%p %u)\n", This, pRequest, fSpyed);
438 return pRequest;
439 }
440 static void WINAPI IMallocSpy_fnPostFree(LPMALLOCSPY iface, BOOL fSpyed)
441 {
442 ICOM_THIS (_MallocSpy, iface);
443 TRACE ("(%p)->(%u)\n", This, fSpyed);
444 }
445
446 static ULONG WINAPI IMallocSpy_fnPreRealloc(LPMALLOCSPY iface, void* pRequest, ULONG cbRequest, void** ppNewRequest, BOOL fSpyed)
447 {
448 ICOM_THIS (_MallocSpy, iface);
449 TRACE ("(%p)->(%p %lu %u)\n", This, pRequest, cbRequest, fSpyed);
450 *ppNewRequest = pRequest;
451 return cbRequest;
452 }
453
454 static PVOID WINAPI IMallocSpy_fnPostRealloc(LPMALLOCSPY iface, void* pActual, BOOL fSpyed)
455 {
456 ICOM_THIS (_MallocSpy, iface);
457 TRACE ("(%p)->(%p %u)\n", This, pActual, fSpyed);
458 return pActual;
459 }
460
461 static PVOID WINAPI IMallocSpy_fnPreGetSize(LPMALLOCSPY iface, void* pRequest, BOOL fSpyed)
462 {
463 ICOM_THIS (_MallocSpy, iface);
464 TRACE ("(%p)->(%p %u)\n", This, pRequest, fSpyed);
465 return pRequest;
466 }
467
468 static ULONG WINAPI IMallocSpy_fnPostGetSize(LPMALLOCSPY iface, ULONG cbActual, BOOL fSpyed)
469 {
470 ICOM_THIS (_MallocSpy, iface);
471 TRACE ("(%p)->(%lu %u)\n", This, cbActual, fSpyed);
472 return cbActual;
473 }
474
475 static PVOID WINAPI IMallocSpy_fnPreDidAlloc(LPMALLOCSPY iface, void* pRequest, BOOL fSpyed)
476 {
477 ICOM_THIS (_MallocSpy, iface);
478 TRACE ("(%p)->(%p %u)\n", This, pRequest, fSpyed);
479 return pRequest;
480 }
481
482 static int WINAPI IMallocSpy_fnPostDidAlloc(LPMALLOCSPY iface, void* pRequest, BOOL fSpyed, int fActual)
483 {
484 ICOM_THIS (_MallocSpy, iface);
485 TRACE ("(%p)->(%p %u %u)\n", This, pRequest, fSpyed, fActual);
486 return fActual;
487 }
488
489 static void WINAPI IMallocSpy_fnPreHeapMinimize(LPMALLOCSPY iface)
490 {
491 ICOM_THIS (_MallocSpy, iface);
492 TRACE ("(%p)->()\n", This);
493 }
494
495 static void WINAPI IMallocSpy_fnPostHeapMinimize(LPMALLOCSPY iface)
496 {
497 ICOM_THIS (_MallocSpy, iface);
498 TRACE ("(%p)->()\n", This);
499 }
500
501 static void MallocSpyDumpLeaks() {
502 TRACE("leaks: %lu\n", Malloc32.SpyedAllocationsLeft);
503 }
504
505 static ICOM_VTABLE(IMallocSpy) VT_IMallocSpy =
506 {
507 ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
508 IMallocSpy_fnQueryInterface,
509 IMallocSpy_fnAddRef,
510 IMallocSpy_fnRelease,
511 IMallocSpy_fnPreAlloc,
512 IMallocSpy_fnPostAlloc,
513 IMallocSpy_fnPreFree,
514 IMallocSpy_fnPostFree,
515 IMallocSpy_fnPreRealloc,
516 IMallocSpy_fnPostRealloc,
517 IMallocSpy_fnPreGetSize,
518 IMallocSpy_fnPostGetSize,
519 IMallocSpy_fnPreDidAlloc,
520 IMallocSpy_fnPostDidAlloc,
521 IMallocSpy_fnPreHeapMinimize,
522 IMallocSpy_fnPostHeapMinimize
523 };
524
525 /******************************************************************************
526 * CoGetMalloc [OLE32.@]
527 *
528 * RETURNS
529 * The win32 IMalloc
530 */
531 HRESULT WINAPI CoGetMalloc(DWORD dwMemContext, LPMALLOC *lpMalloc)
532 {
533 *lpMalloc = (LPMALLOC)&Malloc32;
534 return S_OK;
535 }
536
537 /***********************************************************************
538 * CoTaskMemAlloc [OLE32.@]
539 * RETURNS
540 * pointer to newly allocated block
541 */
542 LPVOID WINAPI CoTaskMemAlloc(ULONG size)
543 {
544 return IMalloc_Alloc((LPMALLOC)&Malloc32,size);
545 }
546 /***********************************************************************
547 * CoTaskMemFree [OLE32.@]
548 */
549 VOID WINAPI CoTaskMemFree(LPVOID ptr)
550 {
551 IMalloc_Free((LPMALLOC)&Malloc32, ptr);
552 }
553
554 /***********************************************************************
555 * CoTaskMemRealloc [OLE32.@]
556 * RETURNS
557 * pointer to newly allocated block
558 */
559 LPVOID WINAPI CoTaskMemRealloc(LPVOID pvOld, ULONG size)
560 {
561 return IMalloc_Realloc((LPMALLOC)&Malloc32, pvOld, size);
562 }
563
564 /***********************************************************************
565 * CoRegisterMallocSpy [OLE32.@]
566 *
567 * NOTES
568 * if a mallocspy is already registered, we cant do it again since
569 * only the spy knows, how to free a memory block
570 */
571 HRESULT WINAPI CoRegisterMallocSpy(LPMALLOCSPY pMallocSpy)
572 {
573 IMallocSpy* pSpy;
574 HRESULT hres = E_INVALIDARG;
575
576 TRACE("\n");
577
578 /* HACK TO ACTIVATE OUT SPY */
579 if (pMallocSpy == (LPVOID)-1) pMallocSpy =(IMallocSpy*)&MallocSpy;
580
581 if(Malloc32.pSpy) return CO_E_OBJISREG;
582
583 EnterCriticalSection(&IMalloc32_SpyCS);
584
585 if (SUCCEEDED(IUnknown_QueryInterface(pMallocSpy, &IID_IMallocSpy, (LPVOID*)&pSpy))) {
586 Malloc32.pSpy = pSpy;
587 hres = S_OK;
588 }
589
590 LeaveCriticalSection(&IMalloc32_SpyCS);
591
592 return hres;
593 }
594
595 /***********************************************************************
596 * CoRevokeMallocSpy [OLE32.@]
597 *
598 * NOTES
599 * we can't rewoke a malloc spy as long as memory blocks allocated with
600 * the spy are active since only the spy knows how to free them
601 */
602 HRESULT WINAPI CoRevokeMallocSpy(void)
603 {
604 HRESULT hres = S_OK;
605 TRACE("\n");
606
607 EnterCriticalSection(&IMalloc32_SpyCS);
608
609 /* if it's our spy it's time to dump the leaks */
610 if (Malloc32.pSpy == (IMallocSpy*)&MallocSpy) {
611 MallocSpyDumpLeaks();
612 }
613
614 if (Malloc32.SpyedAllocationsLeft) {
615 TRACE("SpyReleasePending with %lu allocations left\n", Malloc32.SpyedAllocationsLeft);
616 Malloc32.SpyReleasePending = TRUE;
617 hres = E_ACCESSDENIED;
618 } else {
619 IMallocSpy_Release(Malloc32.pSpy);
620 Malloc32.pSpy = NULL;
621 }
622 LeaveCriticalSection(&IMalloc32_SpyCS);
623
624 return S_OK;
625 }
626
627 /******************************************************************************
628 * IsValidInterface [OLE32.@]
629 *
630 * RETURNS
631 * True, if the passed pointer is a valid interface
632 */
633 BOOL WINAPI IsValidInterface(
634 LPUNKNOWN punk /* [in] interface to be tested */
635 ) {
636 return !(
637 IsBadReadPtr(punk,4) ||
638 IsBadReadPtr(punk->lpVtbl,4) ||
639 IsBadReadPtr(punk->lpVtbl->QueryInterface,9) ||
640 IsBadCodePtr((FARPROC)punk->lpVtbl->QueryInterface)
641 );
642 }