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