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