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