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