ole32: Fix a memory leak of moniker in RunningObjectTableImpl_Register.
[wine] / dlls / ole32 / stubmanager.c
1 /*
2  * A stub manager is an object that controls interface stubs. It is
3  * identified by an OID (object identifier) and acts as the network
4  * identity of the object. There can be many stub managers in a
5  * process or apartment.
6  *
7  * Copyright 2002 Marcus Meissner
8  * Copyright 2004 Mike Hearn for CodeWeavers
9  * Copyright 2004 Robert Shearman (for CodeWeavers)
10  *
11  * This library is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU Lesser General Public
13  * License as published by the Free Software Foundation; either
14  * version 2.1 of the License, or (at your option) any later version.
15  *
16  * This library is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
19  * Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public
22  * License along with this library; if not, write to the Free Software
23  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24  */
25
26 #define COBJMACROS
27 #define NONAMELESSUNION
28 #define NONAMELESSSTRUCT
29
30 #include <assert.h>
31 #include <stdarg.h>
32 #include <limits.h>
33
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winuser.h"
37 #include "objbase.h"
38 #include "rpc.h"
39
40 #include "wine/debug.h"
41 #include "compobj_private.h"
42
43 WINE_DEFAULT_DEBUG_CHANNEL(ole);
44
45 static void stub_manager_delete_ifstub(struct stub_manager *m, struct ifstub *ifstub);
46 static struct ifstub *stub_manager_ipid_to_ifstub(struct stub_manager *m, const IPID *ipid);
47
48 /* creates a new stub manager and adds it into the apartment. caller must
49  * release stub manager when it is no longer required. the apartment and
50  * external refs together take one implicit ref */
51 struct stub_manager *new_stub_manager(APARTMENT *apt, IUnknown *object)
52 {
53     struct stub_manager *sm;
54
55     assert( apt );
56     
57     sm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct stub_manager));
58     if (!sm) return NULL;
59
60     list_init(&sm->ifstubs);
61
62     InitializeCriticalSection(&sm->lock);
63     DEBUG_SET_CRITSEC_NAME(&sm->lock, "stub_manager");
64
65     IUnknown_AddRef(object);
66     sm->object = object;
67     sm->apt    = apt;
68
69     /* start off with 2 references because the stub is in the apartment
70      * and the caller will also hold a reference */
71     sm->refs   = 2;
72
73     sm->oxid_info.dwPid = GetCurrentProcessId();
74     sm->oxid_info.dwTid = GetCurrentThreadId();
75     /*
76      * FIXME: this is a hack for marshalling IRemUnknown. In real
77      * DCOM, the IPID of the IRemUnknown interface is generated like
78      * any other and passed to the OXID resolver which then returns it
79      * when queried. We don't have an OXID resolver yet so instead we
80      * use a magic IPID reserved for IRemUnknown.
81      */
82     sm->oxid_info.ipidRemUnknown.Data1 = 0xffffffff;
83     sm->oxid_info.ipidRemUnknown.Data2 = 0xffff;
84     sm->oxid_info.ipidRemUnknown.Data3 = 0xffff;
85     assert(sizeof(sm->oxid_info.ipidRemUnknown.Data4) == sizeof(apt->oxid));
86     memcpy(&sm->oxid_info.ipidRemUnknown.Data4, &apt->oxid, sizeof(OXID));
87     sm->oxid_info.dwAuthnHint = RPC_C_AUTHN_LEVEL_NONE;
88     sm->oxid_info.psa = NULL /* FIXME */;
89
90     /* yes, that's right, this starts at zero. that's zero EXTERNAL
91      * refs, ie nobody has unmarshalled anything yet. we can't have
92      * negative refs because the stub manager cannot be explicitly
93      * killed, it has to die by somebody unmarshalling then releasing
94      * the marshalled ifptr.
95      */
96     sm->extrefs = 0;
97
98     EnterCriticalSection(&apt->cs);
99     sm->oid = apt->oidc++;
100     list_add_head(&apt->stubmgrs, &sm->entry);
101     LeaveCriticalSection(&apt->cs);
102
103     TRACE("Created new stub manager (oid=%s) at %p for object with IUnknown %p\n", wine_dbgstr_longlong(sm->oid), sm, object);
104     
105     return sm;
106 }
107
108 /* caller must remove stub manager from apartment prior to calling this function */
109 static void stub_manager_delete(struct stub_manager *m)
110 {
111     struct list *cursor;
112
113     TRACE("destroying %p (oid=%s)\n", m, wine_dbgstr_longlong(m->oid));
114
115     /* release every ifstub */
116     while ((cursor = list_head(&m->ifstubs)))
117     {
118         struct ifstub *ifstub = LIST_ENTRY(cursor, struct ifstub, entry);
119         stub_manager_delete_ifstub(m, ifstub);
120     }
121
122     CoTaskMemFree(m->oxid_info.psa);
123     IUnknown_Release(m->object);
124
125     DEBUG_CLEAR_CRITSEC_NAME(&m->lock);
126     DeleteCriticalSection(&m->lock);
127
128     HeapFree(GetProcessHeap(), 0, m);
129 }
130
131 /* gets the stub manager associated with an object - caller must have
132  * a reference to the apartment while a reference to the stub manager is held.
133  * it must also call release on the stub manager when it is no longer needed */
134 struct stub_manager *get_stub_manager_from_object(APARTMENT *apt, void *object)
135 {
136     struct stub_manager *result = NULL;
137     struct list         *cursor;
138
139     EnterCriticalSection(&apt->cs);
140     LIST_FOR_EACH( cursor, &apt->stubmgrs )
141     {
142         struct stub_manager *m = LIST_ENTRY( cursor, struct stub_manager, entry );
143
144         if (m->object == object)
145         {
146             result = m;
147             stub_manager_int_addref(result);
148             break;
149         }
150     }
151     LeaveCriticalSection(&apt->cs);
152
153     if (result)
154         TRACE("found %p for object %p\n", result, object);
155     else
156         TRACE("not found for object %p\n", object);
157
158     return result;    
159 }
160
161 /* removes the apartment reference to an object, destroying it when no other
162  * threads have a reference to it */
163 void apartment_disconnectobject(struct apartment *apt, void *object)
164 {
165     int found = FALSE;
166     struct stub_manager *stubmgr;
167
168     EnterCriticalSection(&apt->cs);
169     LIST_FOR_EACH_ENTRY( stubmgr, &apt->stubmgrs, struct stub_manager, entry )
170     {
171         if (stubmgr->object == object)
172         {
173             found = TRUE;
174             stub_manager_int_release(stubmgr);
175             break;
176         }
177     }
178     LeaveCriticalSection(&apt->cs);
179
180     if (found)
181         TRACE("disconnect object %p\n", object);
182     else
183         WARN("couldn't find object %p\n", object);
184 }
185
186 /* gets the stub manager associated with an object id - caller must have
187  * a reference to the apartment while a reference to the stub manager is held.
188  * it must also call release on the stub manager when it is no longer needed */
189 struct stub_manager *get_stub_manager(APARTMENT *apt, OID oid)
190 {
191     struct stub_manager *result = NULL;
192     struct list         *cursor;
193
194     EnterCriticalSection(&apt->cs);
195     LIST_FOR_EACH( cursor, &apt->stubmgrs )
196     {
197         struct stub_manager *m = LIST_ENTRY( cursor, struct stub_manager, entry );
198
199         if (m->oid == oid)
200         {
201             result = m;
202             stub_manager_int_addref(result);
203             break;
204         }
205     }
206     LeaveCriticalSection(&apt->cs);
207
208     if (result)
209         TRACE("found %p for oid %s\n", result, wine_dbgstr_longlong(oid));
210     else
211         TRACE("not found for oid %s\n", wine_dbgstr_longlong(oid));
212
213     return result;
214 }
215
216 /* increments the internal refcount */
217 ULONG stub_manager_int_addref(struct stub_manager *This)
218 {
219     ULONG refs;
220
221     EnterCriticalSection(&This->apt->cs);
222     refs = ++This->refs;
223     LeaveCriticalSection(&This->apt->cs);
224
225     TRACE("before %d\n", refs - 1);
226
227     return refs;
228 }
229
230 /* decrements the internal refcount */
231 ULONG stub_manager_int_release(struct stub_manager *This)
232 {
233     ULONG refs;
234     APARTMENT *apt = This->apt;
235
236     EnterCriticalSection(&apt->cs);
237     refs = --This->refs;
238
239     TRACE("after %d\n", refs);
240
241     /* remove from apartment so no other thread can access it... */
242     if (!refs)
243         list_remove(&This->entry);
244
245     LeaveCriticalSection(&apt->cs);
246
247     /* ... so now we can delete it without being inside the apartment critsec */
248     if (!refs)
249         stub_manager_delete(This);
250
251     return refs;
252 }
253
254 /* add some external references (ie from a client that unmarshaled an ifptr) */
255 ULONG stub_manager_ext_addref(struct stub_manager *m, ULONG refs)
256 {
257     ULONG rc;
258
259     EnterCriticalSection(&m->lock);
260     
261     /* make sure we don't overflow extrefs */
262     refs = min(refs, (ULONG_MAX-1 - m->extrefs));
263     rc = (m->extrefs += refs);
264
265     LeaveCriticalSection(&m->lock);
266     
267     TRACE("added %u refs to %p (oid %s), rc is now %u\n", refs, m, wine_dbgstr_longlong(m->oid), rc);
268
269     return rc;
270 }
271
272 /* remove some external references */
273 ULONG stub_manager_ext_release(struct stub_manager *m, ULONG refs, BOOL last_unlock_releases)
274 {
275     ULONG rc;
276
277     EnterCriticalSection(&m->lock);
278
279     /* make sure we don't underflow extrefs */
280     refs = min(refs, m->extrefs);
281     rc = (m->extrefs -= refs);
282
283     LeaveCriticalSection(&m->lock);
284     
285     TRACE("removed %u refs from %p (oid %s), rc is now %u\n", refs, m, wine_dbgstr_longlong(m->oid), rc);
286
287     if (rc == 0 && last_unlock_releases)
288         stub_manager_int_release(m);
289
290     return rc;
291 }
292
293 static struct ifstub *stub_manager_ipid_to_ifstub(struct stub_manager *m, const IPID *ipid)
294 {
295     struct list    *cursor;
296     struct ifstub  *result = NULL;
297     
298     EnterCriticalSection(&m->lock);
299     LIST_FOR_EACH( cursor, &m->ifstubs )
300     {
301         struct ifstub *ifstub = LIST_ENTRY( cursor, struct ifstub, entry );
302
303         if (IsEqualGUID(ipid, &ifstub->ipid))
304         {
305             result = ifstub;
306             break;
307         }
308     }
309     LeaveCriticalSection(&m->lock);
310
311     return result;
312 }
313
314 struct ifstub *stub_manager_find_ifstub(struct stub_manager *m, REFIID iid, MSHLFLAGS flags)
315 {
316     struct ifstub  *result = NULL;
317     struct ifstub  *ifstub;
318
319     EnterCriticalSection(&m->lock);
320     LIST_FOR_EACH_ENTRY( ifstub, &m->ifstubs, struct ifstub, entry )
321     {
322         if (IsEqualIID(iid, &ifstub->iid) && (ifstub->flags == flags))
323         {
324             result = ifstub;
325             break;
326         }
327     }
328     LeaveCriticalSection(&m->lock);
329
330     return result;
331 }
332
333 /* gets the stub manager associated with an ipid - caller must have
334  * a reference to the apartment while a reference to the stub manager is held.
335  * it must also call release on the stub manager when it is no longer needed */
336 static struct stub_manager *get_stub_manager_from_ipid(APARTMENT *apt, const IPID *ipid)
337 {
338     struct stub_manager *result = NULL;
339     struct list         *cursor;
340
341     EnterCriticalSection(&apt->cs);
342     LIST_FOR_EACH( cursor, &apt->stubmgrs )
343     {
344         struct stub_manager *m = LIST_ENTRY( cursor, struct stub_manager, entry );
345
346         if (stub_manager_ipid_to_ifstub(m, ipid))
347         {
348             result = m;
349             stub_manager_int_addref(result);
350             break;
351         }
352     }
353     LeaveCriticalSection(&apt->cs);
354
355     if (result)
356         TRACE("found %p for ipid %s\n", result, debugstr_guid(ipid));
357     else
358         ERR("not found for ipid %s\n", debugstr_guid(ipid));
359
360     return result;
361 }
362
363 HRESULT ipid_to_stub_manager(const IPID *ipid, APARTMENT **stub_apt, struct stub_manager **stubmgr_ret)
364 {
365     /* FIXME: hack for IRemUnknown */
366     if (ipid->Data2 == 0xffff)
367         *stub_apt = apartment_findfromoxid(*(const OXID *)ipid->Data4, TRUE);
368     else
369         *stub_apt = apartment_findfromtid(ipid->Data2);
370     if (!*stub_apt)
371     {
372         TRACE("Couldn't find apartment corresponding to TID 0x%04x\n", ipid->Data2);
373         return RPC_E_INVALID_OBJECT;
374     }
375     *stubmgr_ret = get_stub_manager_from_ipid(*stub_apt, ipid);
376     if (!*stubmgr_ret)
377     {
378         apartment_release(*stub_apt);
379         *stub_apt = NULL;
380         return RPC_E_INVALID_OBJECT;
381     }
382     return S_OK;
383 }
384
385 /* gets the apartment, stub and channel of an object. the caller must
386  * release the references to all objects (except iface) if the function
387  * returned success, otherwise no references are returned. */
388 HRESULT ipid_get_dispatch_params(const IPID *ipid, APARTMENT **stub_apt,
389                                  IRpcStubBuffer **stub, IRpcChannelBuffer **chan,
390                                  IID *iid, IUnknown **iface)
391 {
392     struct stub_manager *stubmgr;
393     struct ifstub *ifstub;
394     APARTMENT *apt;
395     HRESULT hr;
396
397     hr = ipid_to_stub_manager(ipid, &apt, &stubmgr);
398     if (hr != S_OK) return RPC_E_DISCONNECTED;
399
400     ifstub = stub_manager_ipid_to_ifstub(stubmgr, ipid);
401     if (ifstub)
402     {
403         *stub = ifstub->stubbuffer;
404         IRpcStubBuffer_AddRef(*stub);
405         *chan = ifstub->chan;
406         IRpcChannelBuffer_AddRef(*chan);
407         *stub_apt = apt;
408         *iid = ifstub->iid;
409         *iface = ifstub->iface;
410
411         stub_manager_int_release(stubmgr);
412         return S_OK;
413     }
414     else
415     {
416         stub_manager_int_release(stubmgr);
417         apartment_release(apt);
418         return RPC_E_DISCONNECTED;
419     }
420 }
421
422 /* generates an ipid in the following format (similar to native version):
423  * Data1 = apartment-local ipid counter
424  * Data2 = apartment creator thread ID
425  * Data3 = process ID
426  * Data4 = random value
427  */
428 static inline HRESULT generate_ipid(struct stub_manager *m, IPID *ipid)
429 {
430     HRESULT hr;
431     hr = UuidCreate(ipid);
432     if (FAILED(hr))
433     {
434         ERR("couldn't create IPID for stub manager %p\n", m);
435         UuidCreateNil(ipid);
436         return hr;
437     }
438
439     ipid->Data1 = InterlockedIncrement(&m->apt->ipidc);
440     ipid->Data2 = (USHORT)m->apt->tid;
441     ipid->Data3 = (USHORT)GetCurrentProcessId();
442     return S_OK;
443 }
444
445 /* registers a new interface stub COM object with the stub manager and returns registration record */
446 struct ifstub *stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *sb, IUnknown *iptr, REFIID iid, MSHLFLAGS flags)
447 {
448     struct ifstub *stub;
449     HRESULT hr;
450
451     TRACE("oid=%s, stubbuffer=%p, iptr=%p, iid=%s\n",
452           wine_dbgstr_longlong(m->oid), sb, iptr, debugstr_guid(iid));
453
454     stub = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct ifstub));
455     if (!stub) return NULL;
456
457     hr = RPC_CreateServerChannel(&stub->chan);
458     if (hr != S_OK)
459     {
460         HeapFree(GetProcessHeap(), 0, stub);
461         return NULL;
462     }
463
464     stub->stubbuffer = sb;
465     if (sb) IRpcStubBuffer_AddRef(sb);
466
467     IUnknown_AddRef(iptr);
468     stub->iface = iptr;
469     stub->flags = flags;
470     stub->iid = *iid;
471
472     /* FIXME: find a cleaner way of identifying that we are creating an ifstub
473      * for the remunknown interface */
474     if (flags & MSHLFLAGSP_REMUNKNOWN)
475         stub->ipid = m->oxid_info.ipidRemUnknown;
476     else
477         generate_ipid(m, &stub->ipid);
478
479     EnterCriticalSection(&m->lock);
480     list_add_head(&m->ifstubs, &stub->entry);
481     /* every normal marshal is counted so we don't allow more than we should */
482     if (flags & MSHLFLAGS_NORMAL) m->norm_refs++;
483     LeaveCriticalSection(&m->lock);
484
485     TRACE("ifstub %p created with ipid %s\n", stub, debugstr_guid(&stub->ipid));
486
487     return stub;
488 }
489
490 static void stub_manager_delete_ifstub(struct stub_manager *m, struct ifstub *ifstub)
491 {
492     TRACE("m=%p, m->oid=%s, ipid=%s\n", m, wine_dbgstr_longlong(m->oid), debugstr_guid(&ifstub->ipid));
493
494     list_remove(&ifstub->entry);
495
496     RPC_UnregisterInterface(&ifstub->iid);
497
498     if (ifstub->stubbuffer) IUnknown_Release(ifstub->stubbuffer);
499     IUnknown_Release(ifstub->iface);
500     IRpcChannelBuffer_Release(ifstub->chan);
501
502     HeapFree(GetProcessHeap(), 0, ifstub);
503 }
504
505 /* returns TRUE if it is possible to unmarshal, FALSE otherwise. */
506 BOOL stub_manager_notify_unmarshal(struct stub_manager *m, const IPID *ipid)
507 {
508     BOOL ret = TRUE;
509     struct ifstub *ifstub;
510
511     if (!(ifstub = stub_manager_ipid_to_ifstub(m, ipid)))
512     {
513         ERR("attempted unmarshal of unknown IPID %s\n", debugstr_guid(ipid));
514         return FALSE;
515     }
516
517     EnterCriticalSection(&m->lock);
518
519     /* track normal marshals so we can enforce rules whilst in-process */
520     if (ifstub->flags & MSHLFLAGS_NORMAL)
521     {
522         if (m->norm_refs)
523             m->norm_refs--;
524         else
525         {
526             ERR("attempted invalid normal unmarshal, norm_refs is zero\n");
527             ret = FALSE;
528         }
529     }
530
531     LeaveCriticalSection(&m->lock);
532
533     return ret;
534 }
535
536 /* handles refcounting for CoReleaseMarshalData */
537 void stub_manager_release_marshal_data(struct stub_manager *m, ULONG refs, const IPID *ipid)
538 {
539     struct ifstub *ifstub;
540  
541     if (!(ifstub = stub_manager_ipid_to_ifstub(m, ipid)))
542         return;
543  
544     if (ifstub->flags & MSHLFLAGS_TABLEWEAK)
545         refs = 0;
546     else if (ifstub->flags & MSHLFLAGS_TABLESTRONG)
547         refs = 1;
548
549     stub_manager_ext_release(m, refs, TRUE);
550 }
551
552 /* is an ifstub table marshaled? */
553 BOOL stub_manager_is_table_marshaled(struct stub_manager *m, const IPID *ipid)
554 {
555     struct ifstub *ifstub = stub_manager_ipid_to_ifstub(m, ipid);
556  
557     assert( ifstub );
558     
559     return ifstub->flags & (MSHLFLAGS_TABLESTRONG | MSHLFLAGS_TABLEWEAK);
560 }
561
562
563 /*****************************************************************************
564  *
565  * IRemUnknown implementation
566  *
567  *
568  * Note: this object is not related to the lifetime of a stub_manager, but it
569  * interacts with stub managers.
570  */
571
572 typedef struct rem_unknown
573 {
574     const IRemUnknownVtbl *lpVtbl;
575     LONG refs;
576 } RemUnknown;
577
578 static const IRemUnknownVtbl RemUnknown_Vtbl;
579
580
581 /* construct an IRemUnknown object with one outstanding reference */
582 static HRESULT RemUnknown_Construct(IRemUnknown **ppRemUnknown)
583 {
584     RemUnknown *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
585
586     if (!This) return E_OUTOFMEMORY;
587
588     This->lpVtbl = &RemUnknown_Vtbl;
589     This->refs = 1;
590
591     *ppRemUnknown = (IRemUnknown *)This;
592     return S_OK;
593 }
594
595 static HRESULT WINAPI RemUnknown_QueryInterface(IRemUnknown *iface, REFIID riid, void **ppv)
596 {
597     TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
598
599     if (IsEqualIID(riid, &IID_IUnknown) ||
600         IsEqualIID(riid, &IID_IRemUnknown))
601     {
602         *ppv = (LPVOID)iface;
603         IRemUnknown_AddRef(iface);
604         return S_OK;
605     }
606
607     FIXME("No interface for iid %s\n", debugstr_guid(riid));
608
609     *ppv = NULL;
610     return E_NOINTERFACE;
611 }
612
613 static ULONG WINAPI RemUnknown_AddRef(IRemUnknown *iface)
614 {
615     ULONG refs;
616     RemUnknown *This = (RemUnknown *)iface;
617
618     refs = InterlockedIncrement(&This->refs);
619
620     TRACE("%p before: %d\n", iface, refs-1);
621     return refs;
622 }
623
624 static ULONG WINAPI RemUnknown_Release(IRemUnknown *iface)
625 {
626     ULONG refs;
627     RemUnknown *This = (RemUnknown *)iface;
628
629     refs = InterlockedDecrement(&This->refs);
630     if (!refs)
631         HeapFree(GetProcessHeap(), 0, This);
632
633     TRACE("%p after: %d\n", iface, refs);
634     return refs;
635 }
636
637 static HRESULT WINAPI RemUnknown_RemQueryInterface(IRemUnknown *iface,
638     REFIPID ripid, ULONG cRefs, USHORT cIids, IID *iids /* [size_is(cIids)] */,
639     REMQIRESULT **ppQIResults /* [size_is(,cIids)] */)
640 {
641     HRESULT hr;
642     USHORT i;
643     USHORT successful_qis = 0;
644     APARTMENT *apt;
645     struct stub_manager *stubmgr;
646
647     TRACE("(%p)->(%s, %d, %d, %p, %p)\n", iface, debugstr_guid(ripid), cRefs, cIids, iids, ppQIResults);
648
649     hr = ipid_to_stub_manager(ripid, &apt, &stubmgr);
650     if (hr != S_OK) return hr;
651
652     *ppQIResults = CoTaskMemAlloc(sizeof(REMQIRESULT) * cIids);
653
654     for (i = 0; i < cIids; i++)
655     {
656         HRESULT hrobj = marshal_object(apt, &(*ppQIResults)[i].std, &iids[i],
657                                        stubmgr->object, MSHLFLAGS_NORMAL);
658         if (hrobj == S_OK)
659             successful_qis++;
660         (*ppQIResults)[i].hResult = hrobj;
661     }
662
663     stub_manager_int_release(stubmgr);
664     apartment_release(apt);
665
666     if (successful_qis == cIids)
667         return S_OK; /* we got all requested interfaces */
668     else if (successful_qis == 0)
669         return E_NOINTERFACE; /* we didn't get any interfaces */
670     else
671         return S_FALSE; /* we got some interfaces */
672 }
673
674 static HRESULT WINAPI RemUnknown_RemAddRef(IRemUnknown *iface,
675     USHORT cInterfaceRefs,
676     REMINTERFACEREF* InterfaceRefs /* [size_is(cInterfaceRefs)] */,
677     HRESULT *pResults /* [size_is(cInterfaceRefs)] */)
678 {
679     HRESULT hr = S_OK;
680     USHORT i;
681
682     TRACE("(%p)->(%d, %p, %p)\n", iface, cInterfaceRefs, InterfaceRefs, pResults);
683
684     for (i = 0; i < cInterfaceRefs; i++)
685     {
686         APARTMENT *apt;
687         struct stub_manager *stubmgr;
688
689         pResults[i] = ipid_to_stub_manager(&InterfaceRefs[i].ipid, &apt, &stubmgr);
690         if (pResults[i] != S_OK)
691         {
692             hr = S_FALSE;
693             continue;
694         }
695
696         stub_manager_ext_addref(stubmgr, InterfaceRefs[i].cPublicRefs);
697         if (InterfaceRefs[i].cPrivateRefs)
698             FIXME("Adding %ld refs securely not implemented\n", InterfaceRefs[i].cPrivateRefs);
699
700         stub_manager_int_release(stubmgr);
701         apartment_release(apt);
702     }
703
704     return hr;
705 }
706
707 static HRESULT WINAPI RemUnknown_RemRelease(IRemUnknown *iface,
708     USHORT cInterfaceRefs,
709     REMINTERFACEREF* InterfaceRefs /* [size_is(cInterfaceRefs)] */)
710 {
711     HRESULT hr = S_OK;
712     USHORT i;
713
714     TRACE("(%p)->(%d, %p)\n", iface, cInterfaceRefs, InterfaceRefs);
715
716     for (i = 0; i < cInterfaceRefs; i++)
717     {
718         APARTMENT *apt;
719         struct stub_manager *stubmgr;
720
721         hr = ipid_to_stub_manager(&InterfaceRefs[i].ipid, &apt, &stubmgr);
722         if (hr != S_OK)
723         {
724             hr = E_INVALIDARG;
725             /* FIXME: we should undo any changes already made in this function */
726             break;
727         }
728
729         stub_manager_ext_release(stubmgr, InterfaceRefs[i].cPublicRefs, TRUE);
730         if (InterfaceRefs[i].cPrivateRefs)
731             FIXME("Releasing %ld refs securely not implemented\n", InterfaceRefs[i].cPrivateRefs);
732
733         stub_manager_int_release(stubmgr);
734         apartment_release(apt);
735     }
736
737     return hr;
738 }
739
740 static const IRemUnknownVtbl RemUnknown_Vtbl =
741 {
742     RemUnknown_QueryInterface,
743     RemUnknown_AddRef,
744     RemUnknown_Release,
745     RemUnknown_RemQueryInterface,
746     RemUnknown_RemAddRef,
747     RemUnknown_RemRelease
748 };
749
750 /* starts the IRemUnknown listener for the current apartment */
751 HRESULT start_apartment_remote_unknown(void)
752 {
753     IRemUnknown *pRemUnknown;
754     HRESULT hr = S_OK;
755     APARTMENT *apt = COM_CurrentApt();
756
757     EnterCriticalSection(&apt->cs);
758     if (!apt->remunk_exported)
759     {
760         /* create the IRemUnknown object */
761         hr = RemUnknown_Construct(&pRemUnknown);
762         if (hr == S_OK)
763         {
764             STDOBJREF stdobjref; /* dummy - not used */
765             /* register it with the stub manager */
766             hr = marshal_object(apt, &stdobjref, &IID_IRemUnknown, (IUnknown *)pRemUnknown, MSHLFLAGS_NORMAL|MSHLFLAGSP_REMUNKNOWN);
767             /* release our reference to the object as the stub manager will manage the life cycle for us */
768             IRemUnknown_Release(pRemUnknown);
769             if (hr == S_OK)
770                 apt->remunk_exported = TRUE;
771         }
772     }
773     LeaveCriticalSection(&apt->cs);
774     return hr;
775 }