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