⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 marshal.c

📁 这是一个开放源代码的与WINNT/WIN2K/WIN2003兼容的操作系统
💻 C
📖 第 1 页 / 共 5 页
字号:
/*
 *	Marshalling library
 *
 * Copyright 2002 Marcus Meissner
 * Copyright 2004 Mike Hearn, for CodeWeavers
 * Copyright 2004 Rob Shearman, for CodeWeavers
 *
 * 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
 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
 */

#include <stdarg.h>
#include <string.h>
#include <assert.h>

#define COBJMACROS

#include "windef.h"
#include "winbase.h"
#include "winuser.h"
#include "objbase.h"
#include "ole2.h"
#include "winerror.h"
#include "wine/unicode.h"

#include "compobj_private.h"

#include "wine/debug.h"

WINE_DEFAULT_DEBUG_CHANNEL(ole);

extern const CLSID CLSID_DfMarshal;

/* number of refs given out for normal marshaling */
#define NORMALEXTREFS 5

/* private flag indicating that the caller does not want to notify the stub
 * when the proxy disconnects or is destroyed */
#define SORFP_NOLIFETIMEMGMT SORF_OXRES1

static HRESULT unmarshal_object(const STDOBJREF *stdobjref, APARTMENT *apt,
                                MSHCTX dest_context, void *dest_context_data,
                                REFIID riid, const OXID_INFO *oxid_info,
                                void **object);

/* Marshalling just passes a unique identifier to the remote client,
 * that makes it possible to find the passed interface again.
 *
 * So basically we need a set of values that make it unique.
 *
 * Note that the IUnknown_QI(ob,xiid,&ppv) always returns the SAME ppv value!
 *
 * A triple is used: OXID (apt id), OID (stub manager id),
 * IPID (interface ptr/stub id).
 *
 * OXIDs identify an apartment and are network scoped
 * OIDs identify a stub manager and are apartment scoped
 * IPIDs identify an interface stub and are apartment scoped
 */

static inline HRESULT get_facbuf_for_iid(REFIID riid, IPSFactoryBuffer **facbuf)
{
    HRESULT       hr;
    CLSID         clsid;

    if ((hr = CoGetPSClsid(riid, &clsid)))
        return hr;
    return CoGetClassObject(&clsid, CLSCTX_INPROC_SERVER, NULL,
        &IID_IPSFactoryBuffer, (LPVOID*)facbuf);
}

/* marshals an object into a STDOBJREF structure */
HRESULT marshal_object(APARTMENT *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *object, MSHLFLAGS mshlflags)
{
    struct stub_manager *manager;
    struct ifstub       *ifstub;
    BOOL                 tablemarshal;
    IRpcStubBuffer      *stub = NULL;
    HRESULT              hr;
    IUnknown            *iobject = NULL; /* object of type riid */

    hr = apartment_getoxid(apt, &stdobjref->oxid);
    if (hr != S_OK)
        return hr;

    hr = apartment_createwindowifneeded(apt);
    if (hr != S_OK)
        return hr;

    hr = IUnknown_QueryInterface(object, riid, (void **)&iobject);
    if (hr != S_OK)
    {
        ERR("object doesn't expose interface %s, failing with error 0x%08x\n",
            debugstr_guid(riid), hr);
        return E_NOINTERFACE;
    }
  
    /* IUnknown doesn't require a stub buffer, because it never goes out on
     * the wire */
    if (!IsEqualIID(riid, &IID_IUnknown))
    {
        IPSFactoryBuffer *psfb;

        hr = get_facbuf_for_iid(riid, &psfb);
        if (hr != S_OK)
        {
            ERR("couldn't get IPSFactory buffer for interface %s\n", debugstr_guid(riid));
            IUnknown_Release(iobject);
            return hr;
        }
    
        hr = IPSFactoryBuffer_CreateStub(psfb, riid, iobject, &stub);
        IPSFactoryBuffer_Release(psfb);
        if (hr != S_OK)
        {
            ERR("Failed to create an IRpcStubBuffer from IPSFactory for %s with error 0x%08x\n",
                debugstr_guid(riid), hr);
            IUnknown_Release(iobject);
            return hr;
        }
    }

    if (mshlflags & MSHLFLAGS_NOPING)
        stdobjref->flags = SORF_NOPING;
    else
        stdobjref->flags = SORF_NULL;

    if ((manager = get_stub_manager_from_object(apt, object)))
        TRACE("registering new ifstub on pre-existing manager\n");
    else
    {
        TRACE("constructing new stub manager\n");

        manager = new_stub_manager(apt, object);
        if (!manager)
        {
            if (stub) IRpcStubBuffer_Release(stub);
            IUnknown_Release(iobject);
            return E_OUTOFMEMORY;
        }
    }
    stdobjref->oid = manager->oid;

    tablemarshal = ((mshlflags & MSHLFLAGS_TABLESTRONG) || (mshlflags & MSHLFLAGS_TABLEWEAK));

    /* make sure ifstub that we are creating is unique */
    ifstub = stub_manager_find_ifstub(manager, riid, mshlflags);
    if (!ifstub)
        ifstub = stub_manager_new_ifstub(manager, stub, iobject, riid, mshlflags);

    if (stub) IRpcStubBuffer_Release(stub);
    IUnknown_Release(iobject);

    if (!ifstub)
    {
        stub_manager_int_release(manager);
        /* destroy the stub manager if it has no ifstubs by releasing
         * zero external references */
        stub_manager_ext_release(manager, 0, TRUE);
        return E_OUTOFMEMORY;
    }

    if (!tablemarshal)
    {
        stdobjref->cPublicRefs = NORMALEXTREFS;
        stub_manager_ext_addref(manager, stdobjref->cPublicRefs);
    }
    else
    {
        stdobjref->cPublicRefs = 0;
        if (mshlflags & MSHLFLAGS_TABLESTRONG)
            stub_manager_ext_addref(manager, 1);
    }

    /* FIXME: check return value */
    RPC_RegisterInterface(riid);

    stdobjref->ipid = ifstub->ipid;

    stub_manager_int_release(manager);
    return S_OK;
}



/* Client-side identity of the server object */

static HRESULT proxy_manager_get_remunknown(struct proxy_manager * This, IRemUnknown **remunk);
static void proxy_manager_destroy(struct proxy_manager * This);
static HRESULT proxy_manager_find_ifproxy(struct proxy_manager * This, REFIID riid, struct ifproxy ** ifproxy_found);
static HRESULT proxy_manager_query_local_interface(struct proxy_manager * This, REFIID riid, void ** ppv);

static HRESULT WINAPI ClientIdentity_QueryInterface(IMultiQI * iface, REFIID riid, void ** ppv)
{
    HRESULT hr;
    MULTI_QI mqi;

    TRACE("%s\n", debugstr_guid(riid));

    mqi.pIID = riid;
    hr = IMultiQI_QueryMultipleInterfaces(iface, 1, &mqi);
    *ppv = (void *)mqi.pItf;

    return hr;
}

static ULONG WINAPI ClientIdentity_AddRef(IMultiQI * iface)
{
    struct proxy_manager * This = (struct proxy_manager *)iface;
    TRACE("%p - before %d\n", iface, This->refs);
    return InterlockedIncrement(&This->refs);
}

static ULONG WINAPI ClientIdentity_Release(IMultiQI * iface)
{
    struct proxy_manager * This = (struct proxy_manager *)iface;
    ULONG refs = InterlockedDecrement(&This->refs);
    TRACE("%p - after %d\n", iface, refs);
    if (!refs)
        proxy_manager_destroy(This);
    return refs;
}

static HRESULT WINAPI ClientIdentity_QueryMultipleInterfaces(IMultiQI *iface, ULONG cMQIs, MULTI_QI *pMQIs)
{
    struct proxy_manager * This = (struct proxy_manager *)iface;
    REMQIRESULT *qiresults = NULL;
    ULONG nonlocal_mqis = 0;
    ULONG i;
    ULONG successful_mqis = 0;
    IID *iids = HeapAlloc(GetProcessHeap(), 0, cMQIs * sizeof(*iids));
    /* mapping of RemQueryInterface index to QueryMultipleInterfaces index */
    ULONG *mapping = HeapAlloc(GetProcessHeap(), 0, cMQIs * sizeof(*mapping));

    TRACE("cMQIs: %d\n", cMQIs);

    /* try to get a local interface - this includes already active proxy
     * interfaces and also interfaces exposed by the proxy manager */
    for (i = 0; i < cMQIs; i++)
    {
        TRACE("iid[%d] = %s\n", i, debugstr_guid(pMQIs[i].pIID));
        pMQIs[i].hr = proxy_manager_query_local_interface(This, pMQIs[i].pIID, (void **)&pMQIs[i].pItf);
        if (pMQIs[i].hr == S_OK)
            successful_mqis++;
        else
        {
            iids[nonlocal_mqis] = *pMQIs[i].pIID;
            mapping[nonlocal_mqis] = i;
            nonlocal_mqis++;
        }
    }

    TRACE("%d interfaces not found locally\n", nonlocal_mqis);

    /* if we have more than one interface not found locally then we must try
     * to query the remote object for it */
    if (nonlocal_mqis != 0)
    {
        IRemUnknown *remunk;
        HRESULT hr;
        IPID *ipid;

        /* get the ipid of the first entry */
        /* FIXME: should we implement ClientIdentity on the ifproxies instead
         * of the proxy_manager so we use the correct ipid here? */
        ipid = &LIST_ENTRY(list_head(&This->interfaces), struct ifproxy, entry)->stdobjref.ipid;

        /* get IRemUnknown proxy so we can communicate with the remote object */
        hr = proxy_manager_get_remunknown(This, &remunk);

        if (hr == S_OK)
        {
            hr = IRemUnknown_RemQueryInterface(remunk, ipid, NORMALEXTREFS,
                                               nonlocal_mqis, iids, &qiresults);
            IRemUnknown_Release(remunk);
            if (FAILED(hr))
                ERR("IRemUnknown_RemQueryInterface failed with error 0x%08x\n", hr);
        }

        /* IRemUnknown_RemQueryInterface can return S_FALSE if only some of
         * the interfaces were returned */
        if (SUCCEEDED(hr))
        {
            /* try to unmarshal each object returned to us */
            for (i = 0; i < nonlocal_mqis; i++)
            {
                ULONG index = mapping[i];
                HRESULT hrobj = qiresults[i].hResult;
                if (hrobj == S_OK)
                    hrobj = unmarshal_object(&qiresults[i].std, COM_CurrentApt(),
                                             This->dest_context,
                                             This->dest_context_data,
                                             pMQIs[index].pIID, &This->oxid_info,
                                             (void **)&pMQIs[index].pItf);

                if (hrobj == S_OK)
                    successful_mqis++;
                else
                    ERR("Failed to get pointer to interface %s\n", debugstr_guid(pMQIs[index].pIID));
                pMQIs[index].hr = hrobj;
            }
        }

        /* free the memory allocated by the proxy */
        CoTaskMemFree(qiresults);
    }

    TRACE("%d/%d successfully queried\n", successful_mqis, cMQIs);

    HeapFree(GetProcessHeap(), 0, iids);
    HeapFree(GetProcessHeap(), 0, mapping);

    if (successful_mqis == cMQIs)
        return S_OK; /* we got all requested interfaces */
    else if (successful_mqis == 0)
        return E_NOINTERFACE; /* we didn't get any interfaces */
    else
        return S_FALSE; /* we got some interfaces */
}

static const IMultiQIVtbl ClientIdentity_Vtbl =
{
    ClientIdentity_QueryInterface,
    ClientIdentity_AddRef,
    ClientIdentity_Release,
    ClientIdentity_QueryMultipleInterfaces
};

/* FIXME: remove these */
static HRESULT WINAPI StdMarshalImpl_GetUnmarshalClass(LPMARSHAL iface, REFIID riid, void* pv, DWORD dwDestContext, void* pvDestContext, DWORD mshlflags, CLSID* pCid);
static HRESULT WINAPI StdMarshalImpl_GetMarshalSizeMax(LPMARSHAL iface, REFIID riid, void* pv, DWORD dwDestContext, void* pvDestContext, DWORD mshlflags, DWORD* pSize);
static HRESULT WINAPI StdMarshalImpl_UnmarshalInterface(LPMARSHAL iface, IStream *pStm, REFIID riid, void **ppv);
static HRESULT WINAPI StdMarshalImpl_ReleaseMarshalData(LPMARSHAL iface, IStream *pStm);
static HRESULT WINAPI StdMarshalImpl_DisconnectObject(LPMARSHAL iface, DWORD dwReserved);

static HRESULT WINAPI Proxy_QueryInterface(IMarshal *iface, REFIID riid, void **ppvObject)
{
    ICOM_THIS_MULTI(struct proxy_manager, lpVtblMarshal, iface);
    return IMultiQI_QueryInterface((IMultiQI *)&This->lpVtbl, riid, ppvObject);
}

static ULONG WINAPI Proxy_AddRef(IMarshal *iface)
{
    ICOM_THIS_MULTI(struct proxy_manager, lpVtblMarshal, iface);
    return IMultiQI_AddRef((IMultiQI *)&This->lpVtbl);
}

static ULONG WINAPI Proxy_Release(IMarshal *iface)
{
    ICOM_THIS_MULTI(struct proxy_manager, lpVtblMarshal, iface);
    return IMultiQI_Release((IMultiQI *)&This->lpVtbl);
}

static HRESULT WINAPI Proxy_MarshalInterface(
    LPMARSHAL iface, IStream *pStm, REFIID riid, void* pv, DWORD dwDestContext,
    void* pvDestContext, DWORD mshlflags)
{
    ICOM_THIS_MULTI(struct proxy_manager, lpVtblMarshal, iface);
    HRESULT hr;
    struct ifproxy *ifproxy;

    TRACE("(...,%s,...)\n", debugstr_guid(riid));

    hr = proxy_manager_find_ifproxy(This, riid, &ifproxy);
    if (SUCCEEDED(hr))
    {
        STDOBJREF stdobjref = ifproxy->stdobjref;

        stdobjref.cPublicRefs = 0;

        if ((mshlflags != MSHLFLAGS_TABLEWEAK) &&
            (mshlflags != MSHLFLAGS_TABLESTRONG))
        {
            ULONG cPublicRefs = ifproxy->refs;
            ULONG cPublicRefsOld;
            /* optimization - share out proxy's public references if possible

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -