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