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