stubmanager.c 24 KB
Newer Older
1 2 3 4 5 6 7 8
/*
 * A stub manager is an object that controls interface stubs. It is
 * identified by an OID (object identifier) and acts as the network
 * identity of the object. There can be many stub managers in a
 * process or apartment.
 *
 * Copyright 2002 Marcus Meissner
 * Copyright 2004 Mike Hearn for CodeWeavers
9
 * Copyright 2004 Robert Shearman (for CodeWeavers)
10 11 12 13 14 15 16 17 18 19 20 21 22
 *
 * This library is free software; you can redistribute it and/or
 * modify it under the terms of the GNU Lesser General Public
 * License as published by the Free Software Foundation; either
 * version 2.1 of the License, or (at your option) any later version.
 *
 * This library is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
 * Lesser General Public License for more details.
 *
 * You should have received a copy of the GNU Lesser General Public
 * 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 27 28 29
 */

#define COBJMACROS

#include <assert.h>
#include <stdarg.h>
30
#include <limits.h>
31 32 33 34 35 36

#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "objbase.h"
#include "rpc.h"
37

38
#include "wine/debug.h"
39 40
#include "wine/exception.h"

41 42 43 44
#include "compobj_private.h"

WINE_DEFAULT_DEBUG_CHANNEL(ole);

45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69

/* generates an ipid in the following format (similar to native version):
 * Data1 = apartment-local ipid counter
 * Data2 = apartment creator thread ID
 * Data3 = process ID
 * Data4 = random value
 */
static inline HRESULT generate_ipid(struct stub_manager *m, IPID *ipid)
{
    HRESULT hr;
    hr = UuidCreate(ipid);
    if (FAILED(hr))
    {
        ERR("couldn't create IPID for stub manager %p\n", m);
        UuidCreateNil(ipid);
        return hr;
    }

    ipid->Data1 = InterlockedIncrement(&m->apt->ipidc);
    ipid->Data2 = (USHORT)m->apt->tid;
    ipid->Data3 = (USHORT)GetCurrentProcessId();
    return S_OK;
}

/* 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 74 75
{
    struct ifstub *stub;
    HRESULT hr;

76
    TRACE("oid=%s, stubbuffer=%p, iid=%s\n", wine_dbgstr_longlong(m->oid), sb, debugstr_guid(iid));
77 78 79 80

    stub = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct ifstub));
    if (!stub) return NULL;

81 82 83 84 85 86 87
    hr = IUnknown_QueryInterface(m->object, iid, (void **)&stub->iface);
    if (hr != S_OK)
    {
        HeapFree(GetProcessHeap(), 0, stub);
        return NULL;
    }

88
    hr = RPC_CreateServerChannel(dest_context, dest_context_data, &stub->chan);
89 90
    if (hr != S_OK)
    {
91
        IUnknown_Release(stub->iface);
92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125
        HeapFree(GetProcessHeap(), 0, stub);
        return NULL;
    }

    stub->stubbuffer = sb;
    if (sb) IRpcStubBuffer_AddRef(sb);

    stub->flags = flags;
    stub->iid = *iid;

    /* FIXME: find a cleaner way of identifying that we are creating an ifstub
     * for the remunknown interface */
    if (flags & MSHLFLAGSP_REMUNKNOWN)
        stub->ipid = m->oxid_info.ipidRemUnknown;
    else
        generate_ipid(m, &stub->ipid);

    EnterCriticalSection(&m->lock);
    list_add_head(&m->ifstubs, &stub->entry);
    /* every normal marshal is counted so we don't allow more than we should */
    if (flags & MSHLFLAGS_NORMAL) m->norm_refs++;
    LeaveCriticalSection(&m->lock);

    TRACE("ifstub %p created with ipid %s\n", stub, debugstr_guid(&stub->ipid));

    return stub;
}

static void stub_manager_delete_ifstub(struct stub_manager *m, struct ifstub *ifstub)
{
    TRACE("m=%p, m->oid=%s, ipid=%s\n", m, wine_dbgstr_longlong(m->oid), debugstr_guid(&ifstub->ipid));

    list_remove(&ifstub->entry);

126 127
    if (!m->disconnected)
        RPC_UnregisterInterface(&ifstub->iid, TRUE);
128

129
    if (ifstub->stubbuffer) IRpcStubBuffer_Release(ifstub->stubbuffer);
130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174
    IUnknown_Release(ifstub->iface);
    IRpcChannelBuffer_Release(ifstub->chan);

    HeapFree(GetProcessHeap(), 0, ifstub);
}

static struct ifstub *stub_manager_ipid_to_ifstub(struct stub_manager *m, const IPID *ipid)
{
    struct list    *cursor;
    struct ifstub  *result = NULL;

    EnterCriticalSection(&m->lock);
    LIST_FOR_EACH( cursor, &m->ifstubs )
    {
        struct ifstub *ifstub = LIST_ENTRY( cursor, struct ifstub, entry );

        if (IsEqualGUID(ipid, &ifstub->ipid))
        {
            result = ifstub;
            break;
        }
    }
    LeaveCriticalSection(&m->lock);

    return result;
}

struct ifstub *stub_manager_find_ifstub(struct stub_manager *m, REFIID iid, MSHLFLAGS flags)
{
    struct ifstub  *result = NULL;
    struct ifstub  *ifstub;

    EnterCriticalSection(&m->lock);
    LIST_FOR_EACH_ENTRY( ifstub, &m->ifstubs, struct ifstub, entry )
    {
        if (IsEqualIID(iid, &ifstub->iid) && (ifstub->flags == flags))
        {
            result = ifstub;
            break;
        }
    }
    LeaveCriticalSection(&m->lock);

    return result;
}
175 176 177 178

/* creates a new stub manager and adds it into the apartment. caller must
 * release stub manager when it is no longer required. the apartment and
 * 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 185 186 187 188 189

    assert( apt );
    
    sm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct stub_manager));
    if (!sm) return NULL;

    list_init(&sm->ifstubs);
190

191
    InitializeCriticalSection(&sm->lock);
192 193
    DEBUG_SET_CRITSEC_NAME(&sm->lock, "stub_manager");

194 195 196
    IUnknown_AddRef(object);
    sm->object = object;
    sm->apt    = apt;
197 198 199 200

    /* start off with 2 references because the stub is in the apartment
     * and the caller will also hold a reference */
    sm->refs   = 2;
201
    sm->weakrefs = 0;
202

203 204 205 206 207 208 209 210 211 212 213 214 215
    sm->oxid_info.dwPid = GetCurrentProcessId();
    sm->oxid_info.dwTid = GetCurrentThreadId();
    /*
     * FIXME: this is a hack for marshalling IRemUnknown. In real
     * DCOM, the IPID of the IRemUnknown interface is generated like
     * any other and passed to the OXID resolver which then returns it
     * when queried. We don't have an OXID resolver yet so instead we
     * use a magic IPID reserved for IRemUnknown.
     */
    sm->oxid_info.ipidRemUnknown.Data1 = 0xffffffff;
    sm->oxid_info.ipidRemUnknown.Data2 = 0xffff;
    sm->oxid_info.ipidRemUnknown.Data3 = 0xffff;
    assert(sizeof(sm->oxid_info.ipidRemUnknown.Data4) == sizeof(apt->oxid));
216
    memcpy(sm->oxid_info.ipidRemUnknown.Data4, &apt->oxid, sizeof(OXID));
217 218 219
    sm->oxid_info.dwAuthnHint = RPC_C_AUTHN_LEVEL_NONE;
    sm->oxid_info.psa = NULL /* FIXME */;

Austin English's avatar
Austin English committed
220 221
    /* Yes, that's right, this starts at zero. that's zero EXTERNAL
     * refs, i.e., nobody has unmarshalled anything yet. We can't have
222 223 224 225
     * negative refs because the stub manager cannot be explicitly
     * killed, it has to die by somebody unmarshalling then releasing
     * the marshalled ifptr.
     */
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 232
        sm->extern_conn = NULL;

233
    EnterCriticalSection(&apt->cs);
234
    sm->oid = apt->oidc++;
235 236 237 238 239 240 241 242
    list_add_head(&apt->stubmgrs, &sm->entry);
    LeaveCriticalSection(&apt->cs);

    TRACE("Created new stub manager (oid=%s) at %p for object with IUnknown %p\n", wine_dbgstr_longlong(sm->oid), sm, object);
    
    return sm;
}

243 244 245 246 247 248 249 250 251 252 253 254 255 256 257
void stub_manager_disconnect(struct stub_manager *m)
{
    struct ifstub *ifstub;

    EnterCriticalSection(&m->lock);
    if (!m->disconnected)
    {
        LIST_FOR_EACH_ENTRY(ifstub, &m->ifstubs, struct ifstub, entry)
            RPC_UnregisterInterface(&ifstub->iid, FALSE);

        m->disconnected = TRUE;
    }
    LeaveCriticalSection(&m->lock);
}

258
/* caller must remove stub manager from apartment prior to calling this function */
259 260 261 262 263 264
static void stub_manager_delete(struct stub_manager *m)
{
    struct list *cursor;

    TRACE("destroying %p (oid=%s)\n", m, wine_dbgstr_longlong(m->oid));

265
    /* release every ifstub */
266 267 268 269 270 271
    while ((cursor = list_head(&m->ifstubs)))
    {
        struct ifstub *ifstub = LIST_ENTRY(cursor, struct ifstub, entry);
        stub_manager_delete_ifstub(m, ifstub);
    }

272
    if(m->extern_conn)
273 274
        IExternalConnection_Release(m->extern_conn);

275
    CoTaskMemFree(m->oxid_info.psa);
276 277 278 279 280 281 282 283 284 285 286 287

    /* Some broken apps crash in object destructors. We have a test showing
     * that on winxp+ those crashes are caught and ignored. */
    __TRY
    {
        IUnknown_Release(m->object);
    }
    __EXCEPT_PAGE_FAULT
    {
        ERR("Got page fault when releasing stub!\n");
    }
    __ENDTRY
288

289
    DEBUG_CLEAR_CRITSEC_NAME(&m->lock);
290 291 292 293 294
    DeleteCriticalSection(&m->lock);

    HeapFree(GetProcessHeap(), 0, m);
}

295
/* increments the internal refcount */
296
static ULONG stub_manager_int_addref(struct stub_manager *This)
297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332
{
    ULONG refs;

    EnterCriticalSection(&This->apt->cs);
    refs = ++This->refs;
    LeaveCriticalSection(&This->apt->cs);

    TRACE("before %d\n", refs - 1);

    return refs;
}

/* decrements the internal refcount */
ULONG stub_manager_int_release(struct stub_manager *This)
{
    ULONG refs;
    APARTMENT *apt = This->apt;

    EnterCriticalSection(&apt->cs);
    refs = --This->refs;

    TRACE("after %d\n", refs);

    /* remove from apartment so no other thread can access it... */
    if (!refs)
        list_remove(&This->entry);

    LeaveCriticalSection(&apt->cs);

    /* ... so now we can delete it without being inside the apartment critsec */
    if (!refs)
        stub_manager_delete(This);

    return refs;
}

333 334 335
/* gets the stub manager associated with an object - caller must have
 * a reference to the apartment while a reference to the stub manager is held.
 * 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 339
{
    struct stub_manager *result = NULL;
    struct list         *cursor;
340 341 342 343 344 345 346 347
    IUnknown *object;
    HRESULT hres;

    hres = IUnknown_QueryInterface(obj, &IID_IUnknown, (void**)&object);
    if (FAILED(hres)) {
        ERR("QueryInterface(IID_IUnknown failed): %08x\n", hres);
        return NULL;
    }
348

349 350
    EnterCriticalSection(&apt->cs);
    LIST_FOR_EACH( cursor, &apt->stubmgrs )
351
    {
352 353 354 355 356 357 358 359
        struct stub_manager *m = LIST_ENTRY( cursor, struct stub_manager, entry );

        if (m->object == object)
        {
            result = m;
            stub_manager_int_addref(result);
            break;
        }
360
    }
361 362
    LeaveCriticalSection(&apt->cs);

363
    if (result) {
364
        TRACE("found %p for object %p\n", result, object);
365 366 367 368
    }else if (alloc) {
        TRACE("not found, creating new stub manager...\n");
        result = new_stub_manager(apt, object);
    }else {
369
        TRACE("not found for object %p\n", object);
370
    }
371

372
    IUnknown_Release(object);
373 374 375 376 377 378 379 380 381 382
    return result;    
}

/* gets the stub manager associated with an object id - caller must have
 * a reference to the apartment while a reference to the stub manager is held.
 * it must also call release on the stub manager when it is no longer needed */
struct stub_manager *get_stub_manager(APARTMENT *apt, OID oid)
{
    struct stub_manager *result = NULL;
    struct list         *cursor;
383 384 385 386 387 388

    EnterCriticalSection(&apt->cs);
    LIST_FOR_EACH( cursor, &apt->stubmgrs )
    {
        struct stub_manager *m = LIST_ENTRY( cursor, struct stub_manager, entry );

389
        if (m->oid == oid)
390 391
        {
            result = m;
392
            stub_manager_int_addref(result);
393 394 395 396 397
            break;
        }
    }
    LeaveCriticalSection(&apt->cs);

398 399 400 401
    if (result)
        TRACE("found %p for oid %s\n", result, wine_dbgstr_longlong(oid));
    else
        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 411 412
    ULONG rc;

    EnterCriticalSection(&m->lock);
413

414 415
    first_extern_ref = refs && !m->extrefs;

416 417 418
    /* make sure we don't overflow extrefs */
    refs = min(refs, (ULONG_MAX-1 - m->extrefs));
    rc = (m->extrefs += refs);
419

420 421 422
    if (tableweak)
        rc += ++m->weakrefs;

423 424
    LeaveCriticalSection(&m->lock);
    
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 429 430 431 432 433
    /*
     * NOTE: According to tests, creating a stub causes two AddConnection calls followed by
     * one ReleaseConnection call (with fLastReleaseCloses=FALSE).
     */
    if(first_extern_ref && m->extern_conn)
        IExternalConnection_AddConnection(m->extern_conn, EXTCONN_STRONG, 0);

434 435 436 437
    return rc;
}

/* 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 442 443 444 445 446 447 448
    ULONG rc;

    EnterCriticalSection(&m->lock);

    /* make sure we don't underflow extrefs */
    refs = min(refs, m->extrefs);
    rc = (m->extrefs -= refs);

449
    if (tableweak)
450 451 452
        --m->weakrefs;
    if (!last_unlock_releases)
        rc += m->weakrefs;
453

454 455
    last_extern_ref = refs && !m->extrefs;

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 465
        if (!(m->extern_conn && last_unlock_releases && m->weakrefs))
            stub_manager_int_release(m);
466

467
    return rc;
468 469
}

470 471 472 473
/* gets the stub manager associated with an ipid - caller must have
 * a reference to the apartment while a reference to the stub manager is held.
 * it must also call release on the stub manager when it is no longer needed */
static struct stub_manager *get_stub_manager_from_ipid(APARTMENT *apt, const IPID *ipid)
474
{
475 476 477 478 479 480 481 482 483 484 485 486 487 488 489 490 491 492 493 494 495 496 497 498 499
    struct stub_manager *result = NULL;
    struct list         *cursor;

    EnterCriticalSection(&apt->cs);
    LIST_FOR_EACH( cursor, &apt->stubmgrs )
    {
        struct stub_manager *m = LIST_ENTRY( cursor, struct stub_manager, entry );

        if (stub_manager_ipid_to_ifstub(m, ipid))
        {
            result = m;
            stub_manager_int_addref(result);
            break;
        }
    }
    LeaveCriticalSection(&apt->cs);

    if (result)
        TRACE("found %p for ipid %s\n", result, debugstr_guid(ipid));
    else
        ERR("not found for ipid %s\n", debugstr_guid(ipid));

    return result;
}

500
static HRESULT ipid_to_stub_manager(const IPID *ipid, APARTMENT **stub_apt, struct stub_manager **stubmgr_ret)
501 502 503
{
    /* FIXME: hack for IRemUnknown */
    if (ipid->Data2 == 0xffff)
504
        *stub_apt = apartment_findfromoxid(*(const OXID *)ipid->Data4, TRUE);
505
    else
506
        *stub_apt = apartment_findfromtid(ipid->Data2);
507 508
    if (!*stub_apt)
    {
509
        TRACE("Couldn't find apartment corresponding to TID 0x%04x\n", ipid->Data2);
510 511 512 513 514
        return RPC_E_INVALID_OBJECT;
    }
    *stubmgr_ret = get_stub_manager_from_ipid(*stub_apt, ipid);
    if (!*stubmgr_ret)
    {
515
        apartment_release(*stub_apt);
516 517 518 519 520 521
        *stub_apt = NULL;
        return RPC_E_INVALID_OBJECT;
    }
    return S_OK;
}

522
/* gets the apartment, stub and channel of an object. the caller must
523 524
 * release the references to all objects (except iface) if the function
 * 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 528
                                 IRpcStubBuffer **stub, IRpcChannelBuffer **chan,
                                 IID *iid, IUnknown **iface)
529 530 531
{
    struct stub_manager *stubmgr;
    struct ifstub *ifstub;
532
    APARTMENT *apt;
533 534
    HRESULT hr;

535 536
    hr = ipid_to_stub_manager(ipid, &apt, &stubmgr);
    if (hr != S_OK) return RPC_E_DISCONNECTED;
537 538 539

    ifstub = stub_manager_ipid_to_ifstub(stubmgr, ipid);
    if (ifstub)
540 541 542 543 544 545
    {
        *stub = ifstub->stubbuffer;
        IRpcStubBuffer_AddRef(*stub);
        *chan = ifstub->chan;
        IRpcChannelBuffer_AddRef(*chan);
        *stub_apt = apt;
546 547
        *iid = ifstub->iid;
        *iface = ifstub->iface;
548

549 550 551 552
        if (manager)
            *manager = stubmgr;
        else
            stub_manager_int_release(stubmgr);
553 554 555 556 557 558 559 560
        return S_OK;
    }
    else
    {
        stub_manager_int_release(stubmgr);
        apartment_release(apt);
        return RPC_E_DISCONNECTED;
    }
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 567 568 569 570 571 572 573
    BOOL ret = TRUE;
    struct ifstub *ifstub;

    if (!(ifstub = stub_manager_ipid_to_ifstub(m, ipid)))
    {
        ERR("attempted unmarshal of unknown IPID %s\n", debugstr_guid(ipid));
        return FALSE;
    }
574 575 576

    EnterCriticalSection(&m->lock);

577 578
    /* track normal marshals so we can enforce rules whilst in-process */
    if (ifstub->flags & MSHLFLAGS_NORMAL)
579
    {
580 581 582 583 584 585 586
        if (m->norm_refs)
            m->norm_refs--;
        else
        {
            ERR("attempted invalid normal unmarshal, norm_refs is zero\n");
            ret = FALSE;
        }
587 588 589 590 591 592 593
    }

    LeaveCriticalSection(&m->lock);

    return ret;
}

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 598 599 600 601 602 603
    struct ifstub *ifstub;
 
    if (!(ifstub = stub_manager_ipid_to_ifstub(m, ipid)))
        return;
 
    if (ifstub->flags & MSHLFLAGS_TABLEWEAK)
        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 614 615 616 617
    struct ifstub *ifstub = stub_manager_ipid_to_ifstub(m, ipid);
 
    assert( ifstub );
    
    return ifstub->flags & (MSHLFLAGS_TABLESTRONG | MSHLFLAGS_TABLEWEAK);
618
}
619

620 621 622 623 624 625 626 627 628 629 630 631

/*****************************************************************************
 *
 * IRemUnknown implementation
 *
 *
 * Note: this object is not related to the lifetime of a stub_manager, but it
 * interacts with stub managers.
 */

typedef struct rem_unknown
{
632
    IRemUnknown IRemUnknown_iface;
633
    LONG refs;
634 635 636 637
} RemUnknown;

static const IRemUnknownVtbl RemUnknown_Vtbl;

638 639 640 641 642
static inline RemUnknown *impl_from_IRemUnknown(IRemUnknown *iface)
{
    return CONTAINING_RECORD(iface, RemUnknown, IRemUnknown_iface);
}

643 644 645 646 647 648 649 650

/* construct an IRemUnknown object with one outstanding reference */
static HRESULT RemUnknown_Construct(IRemUnknown **ppRemUnknown)
{
    RemUnknown *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));

    if (!This) return E_OUTOFMEMORY;

651
    This->IRemUnknown_iface.lpVtbl = &RemUnknown_Vtbl;
652 653
    This->refs = 1;

654
    *ppRemUnknown = &This->IRemUnknown_iface;
655 656 657 658 659 660 661 662 663 664
    return S_OK;
}

static HRESULT WINAPI RemUnknown_QueryInterface(IRemUnknown *iface, REFIID riid, void **ppv)
{
    TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);

    if (IsEqualIID(riid, &IID_IUnknown) ||
        IsEqualIID(riid, &IID_IRemUnknown))
    {
665
        *ppv = iface;
666 667 668 669
        IRemUnknown_AddRef(iface);
        return S_OK;
    }

670 671
    if (!IsEqualIID(riid, &IID_IExternalConnection))
        FIXME("No interface for iid %s\n", debugstr_guid(riid));
672 673 674 675 676 677 678 679

    *ppv = NULL;
    return E_NOINTERFACE;
}

static ULONG WINAPI RemUnknown_AddRef(IRemUnknown *iface)
{
    ULONG refs;
680
    RemUnknown *This = impl_from_IRemUnknown(iface);
681 682 683

    refs = InterlockedIncrement(&This->refs);

684
    TRACE("%p before: %d\n", iface, refs-1);
685 686 687 688 689 690
    return refs;
}

static ULONG WINAPI RemUnknown_Release(IRemUnknown *iface)
{
    ULONG refs;
691
    RemUnknown *This = impl_from_IRemUnknown(iface);
692 693 694 695 696

    refs = InterlockedDecrement(&This->refs);
    if (!refs)
        HeapFree(GetProcessHeap(), 0, This);

697
    TRACE("%p after: %d\n", iface, refs);
698 699 700 701 702 703 704 705 706
    return refs;
}

static HRESULT WINAPI RemUnknown_RemQueryInterface(IRemUnknown *iface,
    REFIPID ripid, ULONG cRefs, USHORT cIids, IID *iids /* [size_is(cIids)] */,
    REMQIRESULT **ppQIResults /* [size_is(,cIids)] */)
{
    HRESULT hr;
    USHORT i;
707
    USHORT successful_qis = 0;
708 709 710
    APARTMENT *apt;
    struct stub_manager *stubmgr;

711
    TRACE("(%p)->(%s, %d, %d, %p, %p)\n", iface, debugstr_guid(ripid), cRefs, cIids, iids, ppQIResults);
712 713 714 715 716 717 718 719

    hr = ipid_to_stub_manager(ripid, &apt, &stubmgr);
    if (hr != S_OK) return hr;

    *ppQIResults = CoTaskMemAlloc(sizeof(REMQIRESULT) * cIids);

    for (i = 0; i < cIids; i++)
    {
720
        HRESULT hrobj = marshal_object(apt, &(*ppQIResults)[i].std, &iids[i],
721
                                       stubmgr->object, MSHCTX_DIFFERENTMACHINE, NULL, MSHLFLAGS_NORMAL);
722 723 724
        if (hrobj == S_OK)
            successful_qis++;
        (*ppQIResults)[i].hResult = hrobj;
725 726 727
    }

    stub_manager_int_release(stubmgr);
728
    apartment_release(apt);
729

730 731 732 733 734 735
    if (successful_qis == cIids)
        return S_OK; /* we got all requested interfaces */
    else if (successful_qis == 0)
        return E_NOINTERFACE; /* we didn't get any interfaces */
    else
        return S_FALSE; /* we got some interfaces */
736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759
}

static HRESULT WINAPI RemUnknown_RemAddRef(IRemUnknown *iface,
    USHORT cInterfaceRefs,
    REMINTERFACEREF* InterfaceRefs /* [size_is(cInterfaceRefs)] */,
    HRESULT *pResults /* [size_is(cInterfaceRefs)] */)
{
    HRESULT hr = S_OK;
    USHORT i;

    TRACE("(%p)->(%d, %p, %p)\n", iface, cInterfaceRefs, InterfaceRefs, pResults);

    for (i = 0; i < cInterfaceRefs; i++)
    {
        APARTMENT *apt;
        struct stub_manager *stubmgr;

        pResults[i] = ipid_to_stub_manager(&InterfaceRefs[i].ipid, &apt, &stubmgr);
        if (pResults[i] != S_OK)
        {
            hr = S_FALSE;
            continue;
        }

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 769 770 771 772 773 774 775 776 777 778 779 780 781 782 783 784 785 786 787 788 789 790 791 792
    }

    return hr;
}

static HRESULT WINAPI RemUnknown_RemRelease(IRemUnknown *iface,
    USHORT cInterfaceRefs,
    REMINTERFACEREF* InterfaceRefs /* [size_is(cInterfaceRefs)] */)
{
    HRESULT hr = S_OK;
    USHORT i;

    TRACE("(%p)->(%d, %p)\n", iface, cInterfaceRefs, InterfaceRefs);

    for (i = 0; i < cInterfaceRefs; i++)
    {
        APARTMENT *apt;
        struct stub_manager *stubmgr;

        hr = ipid_to_stub_manager(&InterfaceRefs[i].ipid, &apt, &stubmgr);
        if (hr != S_OK)
        {
            hr = E_INVALIDARG;
            /* FIXME: we should undo any changes already made in this function */
            break;
        }

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 802 803 804 805 806 807 808 809 810 811 812 813 814
    }

    return hr;
}

static const IRemUnknownVtbl RemUnknown_Vtbl =
{
    RemUnknown_QueryInterface,
    RemUnknown_AddRef,
    RemUnknown_Release,
    RemUnknown_RemQueryInterface,
    RemUnknown_RemAddRef,
    RemUnknown_RemRelease
};

/* starts the IRemUnknown listener for the current apartment */
815
HRESULT start_apartment_remote_unknown(void)
816 817 818 819 820 821 822 823 824 825 826 827 828 829
{
    IRemUnknown *pRemUnknown;
    HRESULT hr = S_OK;
    APARTMENT *apt = COM_CurrentApt();

    EnterCriticalSection(&apt->cs);
    if (!apt->remunk_exported)
    {
        /* create the IRemUnknown object */
        hr = RemUnknown_Construct(&pRemUnknown);
        if (hr == S_OK)
        {
            STDOBJREF stdobjref; /* dummy - not used */
            /* register it with the stub manager */
830
            hr = marshal_object(apt, &stdobjref, &IID_IRemUnknown, (IUnknown *)pRemUnknown, MSHCTX_DIFFERENTMACHINE, NULL, MSHLFLAGS_NORMAL|MSHLFLAGSP_REMUNKNOWN);
831 832 833 834 835 836 837 838 839
            /* release our reference to the object as the stub manager will manage the life cycle for us */
            IRemUnknown_Release(pRemUnknown);
            if (hr == S_OK)
                apt->remunk_exported = TRUE;
        }
    }
    LeaveCriticalSection(&apt->cs);
    return hr;
}