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