| /* | |
| * 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 object was marshaled as table-weak */ | |
| #define SORFP_TABLEWEAK SORF_OXRES1 | |
| /* private flag indicating that the caller does not want to notify the stub | |
| * when the proxy disconnects or is destroyed */ | |
| #define SORFP_NOLIFETIMEMGMT SORF_OXRES2 | |
| /* imported object / proxy manager */ | |
| struct proxy_manager | |
| { | |
| IMultiQI IMultiQI_iface; | |
| IMarshal IMarshal_iface; | |
| IClientSecurity IClientSecurity_iface; | |
| struct apartment *parent; /* owning apartment (RO) */ | |
| struct list entry; /* entry in apartment (CS parent->cs) */ | |
| OXID oxid; /* object exported ID (RO) */ | |
| OXID_INFO oxid_info; /* string binding, ipid of rem unknown and other information (RO) */ | |
| OID oid; /* object ID (RO) */ | |
| struct list interfaces; /* imported interfaces (CS cs) */ | |
| LONG refs; /* proxy reference count (LOCK) */ | |
| CRITICAL_SECTION cs; /* thread safety for this object and children */ | |
| ULONG sorflags; /* STDOBJREF flags (RO) */ | |
| IRemUnknown *remunk; /* proxy to IRemUnknown used for lifecycle management (CS cs) */ | |
| HANDLE remoting_mutex; /* mutex used for synchronizing access to IRemUnknown */ | |
| MSHCTX dest_context; /* context used for activating optimisations (LOCK) */ | |
| void *dest_context_data; /* reserved context value (LOCK) */ | |
| }; | |
| static inline struct proxy_manager *impl_from_IMultiQI( IMultiQI *iface ) | |
| { | |
| return CONTAINING_RECORD(iface, struct proxy_manager, IMultiQI_iface); | |
| } | |
| static inline struct proxy_manager *impl_from_IMarshal( IMarshal *iface ) | |
| { | |
| return CONTAINING_RECORD(iface, struct proxy_manager, IMarshal_iface); | |
| } | |
| static inline struct proxy_manager *impl_from_IClientSecurity( IClientSecurity *iface ) | |
| { | |
| return CONTAINING_RECORD(iface, struct proxy_manager, IClientSecurity_iface); | |
| } | |
| 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; | |
| hr = CoGetPSClsid(riid, &clsid); | |
| if (hr != S_OK) | |
| return hr; | |
| return CoGetClassObject(&clsid, CLSCTX_INPROC_SERVER | WINE_CLSCTX_DONT_HOST, | |
| NULL, &IID_IPSFactoryBuffer, (LPVOID*)facbuf); | |
| } | |
| /* marshals an object into a STDOBJREF structure */ | |
| HRESULT marshal_object(APARTMENT *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *object, | |
| DWORD dest_context, void *dest_context_data, MSHLFLAGS mshlflags) | |
| { | |
| struct stub_manager *manager; | |
| struct ifstub *ifstub; | |
| BOOL tablemarshal; | |
| HRESULT hr; | |
| hr = apartment_getoxid(apt, &stdobjref->oxid); | |
| if (hr != S_OK) | |
| return hr; | |
| hr = apartment_createwindowifneeded(apt); | |
| if (hr != S_OK) | |
| return hr; | |
| if (!(manager = get_stub_manager_from_object(apt, object, TRUE))) | |
| return E_OUTOFMEMORY; | |
| stdobjref->flags = SORF_NULL; | |
| if (mshlflags & MSHLFLAGS_TABLEWEAK) | |
| stdobjref->flags |= SORFP_TABLEWEAK; | |
| if (mshlflags & MSHLFLAGS_NOPING) | |
| stdobjref->flags |= SORF_NOPING; | |
| 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) { | |
| IRpcStubBuffer *stub = NULL; | |
| /* 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) { | |
| hr = IPSFactoryBuffer_CreateStub(psfb, riid, manager->object, &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); | |
| }else { | |
| ERR("couldn't get IPSFactory buffer for interface %s\n", debugstr_guid(riid)); | |
| hr = E_NOINTERFACE; | |
| } | |
| } | |
| if (hr == S_OK) { | |
| ifstub = stub_manager_new_ifstub(manager, stub, riid, dest_context, dest_context_data, mshlflags); | |
| if (!ifstub) | |
| hr = E_OUTOFMEMORY; | |
| } | |
| if (stub) IRpcStubBuffer_Release(stub); | |
| if (hr != S_OK) { | |
| 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, FALSE, TRUE); | |
| return hr; | |
| } | |
| } | |
| if (!tablemarshal) | |
| { | |
| stdobjref->cPublicRefs = NORMALEXTREFS; | |
| stub_manager_ext_addref(manager, stdobjref->cPublicRefs, FALSE); | |
| } | |
| else | |
| { | |
| stdobjref->cPublicRefs = 0; | |
| if (mshlflags & MSHLFLAGS_TABLESTRONG) | |
| stub_manager_ext_addref(manager, 1, FALSE); | |
| else | |
| stub_manager_ext_addref(manager, 0, TRUE); | |
| } | |
| /* 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 = mqi.pItf; | |
| return hr; | |
| } | |
| static ULONG WINAPI ClientIdentity_AddRef(IMultiQI *iface) | |
| { | |
| struct proxy_manager *This = impl_from_IMultiQI(iface); | |
| TRACE("%p - before %d\n", iface, This->refs); | |
| return InterlockedIncrement(&This->refs); | |
| } | |
| static ULONG WINAPI ClientIdentity_Release(IMultiQI *iface) | |
| { | |
| struct proxy_manager *This = impl_from_IMultiQI(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 = impl_from_IMultiQI(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 (SUCCEEDED(hr)) | |
| { | |
| 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) | |
| { | |
| struct proxy_manager *This = impl_from_IMarshal( iface ); | |
| return IMultiQI_QueryInterface(&This->IMultiQI_iface, riid, ppvObject); | |
| } | |
| static ULONG WINAPI Proxy_AddRef(IMarshal *iface) | |
| { | |
| struct proxy_manager *This = impl_from_IMarshal( iface ); | |
| return IMultiQI_AddRef(&This->IMultiQI_iface); | |
| } | |
| static ULONG WINAPI Proxy_Release(IMarshal *iface) | |
| { | |
| struct proxy_manager *This = impl_from_IMarshal( iface ); | |
| return IMultiQI_Release(&This->IMultiQI_iface); | |
| } | |
| static HRESULT WINAPI Proxy_MarshalInterface( | |
| LPMARSHAL iface, IStream *pStm, REFIID riid, void* pv, DWORD dwDestContext, | |
| void* pvDestContext, DWORD mshlflags) | |
| { | |
| struct proxy_manager *This = impl_from_IMarshal( 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 | |
| * instead of making new proxy do a roundtrip through the server */ | |
| do | |
| { | |
| ULONG cPublicRefsNew; | |
| cPublicRefsOld = cPublicRefs; | |
| stdobjref.cPublicRefs = cPublicRefs / 2; | |
| cPublicRefsNew = cPublicRefs - stdobjref.cPublicRefs; | |
| cPublicRefs = InterlockedCompareExchange( | |
| (LONG *)&ifproxy->refs, cPublicRefsNew, cPublicRefsOld); | |
| } while (cPublicRefs != cPublicRefsOld); | |
| } | |
| /* normal and table-strong marshaling need at least one reference */ | |
| if (!stdobjref.cPublicRefs && (mshlflags != MSHLFLAGS_TABLEWEAK)) | |
| { | |
| IRemUnknown *remunk; | |
| hr = proxy_manager_get_remunknown(This, &remunk); | |
| if (hr == S_OK) | |
| { | |
| HRESULT hrref = S_OK; | |
| REMINTERFACEREF rif; | |
| rif.ipid = ifproxy->stdobjref.ipid; | |
| rif.cPublicRefs = (mshlflags == MSHLFLAGS_TABLESTRONG) ? 1 : NORMALEXTREFS; | |
| rif.cPrivateRefs = 0; | |
| hr = IRemUnknown_RemAddRef(remunk, 1, &rif, &hrref); | |
| IRemUnknown_Release(remunk); | |
| if (hr == S_OK && hrref == S_OK) | |
| { | |
| /* table-strong marshaling doesn't give the refs to the | |
| * client that unmarshals the STDOBJREF */ | |
| if (mshlflags != MSHLFLAGS_TABLESTRONG) | |
| stdobjref.cPublicRefs = rif.cPublicRefs; | |
| } | |
| else | |
| ERR("IRemUnknown_RemAddRef returned with 0x%08x, hrref = 0x%08x\n", hr, hrref); | |
| } | |
| } | |
| if (SUCCEEDED(hr)) | |
| { | |
| TRACE("writing stdobjref: flags = %04x cPublicRefs = %d oxid = %s oid = %s ipid = %s\n", | |
| stdobjref.flags, stdobjref.cPublicRefs, | |
| wine_dbgstr_longlong(stdobjref.oxid), | |
| wine_dbgstr_longlong(stdobjref.oid), | |
| debugstr_guid(&stdobjref.ipid)); | |
| hr = IStream_Write(pStm, &stdobjref, sizeof(stdobjref), NULL); | |
| } | |
| } | |
| else | |
| { | |
| /* we don't have the interface already unmarshaled so we have to | |
| * request the object from the server */ | |
| IRemUnknown *remunk; | |
| IPID *ipid; | |
| REMQIRESULT *qiresults = NULL; | |
| IID iid = *riid; | |
| /* 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, | |
| 1, &iid, &qiresults); | |
| if (SUCCEEDED(hr)) | |
| { | |
| hr = IStream_Write(pStm, &qiresults->std, sizeof(qiresults->std), NULL); | |
| if (FAILED(hr)) | |
| { | |
| REMINTERFACEREF rif; | |
| rif.ipid = qiresults->std.ipid; | |
| rif.cPublicRefs = qiresults->std.cPublicRefs; | |
| rif.cPrivateRefs = 0; | |
| IRemUnknown_RemRelease(remunk, 1, &rif); | |
| } | |
| CoTaskMemFree(qiresults); | |
| } | |
| else | |
| ERR("IRemUnknown_RemQueryInterface failed with error 0x%08x\n", hr); | |
| IRemUnknown_Release(remunk); | |
| } | |
| } | |
| return hr; | |
| } | |
| static const IMarshalVtbl ProxyMarshal_Vtbl = | |
| { | |
| Proxy_QueryInterface, | |
| Proxy_AddRef, | |
| Proxy_Release, | |
| StdMarshalImpl_GetUnmarshalClass, | |
| StdMarshalImpl_GetMarshalSizeMax, | |
| Proxy_MarshalInterface, | |
| StdMarshalImpl_UnmarshalInterface, | |
| StdMarshalImpl_ReleaseMarshalData, | |
| StdMarshalImpl_DisconnectObject | |
| }; | |
| static HRESULT WINAPI ProxyCliSec_QueryInterface(IClientSecurity *iface, REFIID riid, void **ppvObject) | |
| { | |
| struct proxy_manager *This = impl_from_IClientSecurity( iface ); | |
| return IMultiQI_QueryInterface(&This->IMultiQI_iface, riid, ppvObject); | |
| } | |
| static ULONG WINAPI ProxyCliSec_AddRef(IClientSecurity *iface) | |
| { | |
| struct proxy_manager *This = impl_from_IClientSecurity( iface ); | |
| return IMultiQI_AddRef(&This->IMultiQI_iface); | |
| } | |
| static ULONG WINAPI ProxyCliSec_Release(IClientSecurity *iface) | |
| { | |
| struct proxy_manager *This = impl_from_IClientSecurity( iface ); | |
| return IMultiQI_Release(&This->IMultiQI_iface); | |
| } | |
| static HRESULT WINAPI ProxyCliSec_QueryBlanket(IClientSecurity *iface, | |
| IUnknown *pProxy, | |
| DWORD *pAuthnSvc, | |
| DWORD *pAuthzSvc, | |
| OLECHAR **ppServerPrincName, | |
| DWORD *pAuthnLevel, | |
| DWORD *pImpLevel, | |
| void **pAuthInfo, | |
| DWORD *pCapabilities) | |
| { | |
| FIXME("(%p, %p, %p, %p, %p, %p, %p, %p): stub\n", pProxy, pAuthnSvc, | |
| pAuthzSvc, ppServerPrincName, pAuthnLevel, pImpLevel, pAuthInfo, | |
| pCapabilities); | |
| if (pAuthnSvc) | |
| *pAuthnSvc = 0; | |
| if (pAuthzSvc) | |
| *pAuthzSvc = 0; | |
| if (ppServerPrincName) | |
| *ppServerPrincName = NULL; | |
| if (pAuthnLevel) | |
| *pAuthnLevel = RPC_C_AUTHN_LEVEL_DEFAULT; | |
| if (pImpLevel) | |
| *pImpLevel = RPC_C_IMP_LEVEL_DEFAULT; | |
| if (pAuthInfo) | |
| *pAuthInfo = NULL; | |
| if (pCapabilities) | |
| *pCapabilities = EOAC_NONE; | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI ProxyCliSec_SetBlanket(IClientSecurity *iface, | |
| IUnknown *pProxy, DWORD AuthnSvc, | |
| DWORD AuthzSvc, | |
| OLECHAR *pServerPrincName, | |
| DWORD AuthnLevel, DWORD ImpLevel, | |
| void *pAuthInfo, | |
| DWORD Capabilities) | |
| { | |
| FIXME("(%p, %d, %d, %s, %d, %d, %p, 0x%x): stub\n", pProxy, AuthnSvc, AuthzSvc, | |
| pServerPrincName == COLE_DEFAULT_PRINCIPAL ? "<default principal>" : debugstr_w(pServerPrincName), | |
| AuthnLevel, ImpLevel, pAuthInfo, Capabilities); | |
| return E_NOTIMPL; | |
| } | |
| static HRESULT WINAPI ProxyCliSec_CopyProxy(IClientSecurity *iface, | |
| IUnknown *pProxy, IUnknown **ppCopy) | |
| { | |
| FIXME("(%p, %p): stub\n", pProxy, ppCopy); | |
| *ppCopy = NULL; | |
| return E_NOTIMPL; | |
| } | |
| static const IClientSecurityVtbl ProxyCliSec_Vtbl = | |
| { | |
| ProxyCliSec_QueryInterface, | |
| ProxyCliSec_AddRef, | |
| ProxyCliSec_Release, | |
| ProxyCliSec_QueryBlanket, | |
| ProxyCliSec_SetBlanket, | |
| ProxyCliSec_CopyProxy | |
| }; | |
| static HRESULT ifproxy_get_public_ref(struct ifproxy * This) | |
| { | |
| HRESULT hr = S_OK; | |
| if (WAIT_OBJECT_0 != WaitForSingleObject(This->parent->remoting_mutex, INFINITE)) | |
| { | |
| ERR("Wait failed for ifproxy %p\n", This); | |
| return E_UNEXPECTED; | |
| } | |
| if (This->refs == 0) | |
| { | |
| IRemUnknown *remunk = NULL; | |
| TRACE("getting public ref for ifproxy %p\n", This); | |
| hr = proxy_manager_get_remunknown(This->parent, &remunk); | |
| if (hr == S_OK) | |
| { | |
| HRESULT hrref = S_OK; | |
| REMINTERFACEREF rif; | |
| rif.ipid = This->stdobjref.ipid; | |
| rif.cPublicRefs = NORMALEXTREFS; | |
| rif.cPrivateRefs = 0; | |
| hr = IRemUnknown_RemAddRef(remunk, 1, &rif, &hrref); | |
| IRemUnknown_Release(remunk); | |
| if (hr == S_OK && hrref == S_OK) | |
| InterlockedExchangeAdd((LONG *)&This->refs, NORMALEXTREFS); | |
| else | |
| ERR("IRemUnknown_RemAddRef returned with 0x%08x, hrref = 0x%08x\n", hr, hrref); | |
| } | |
| } | |
| ReleaseMutex(This->parent->remoting_mutex); | |
| return hr; | |
| } | |
| static HRESULT ifproxy_release_public_refs(struct ifproxy * This) | |
| { | |
| HRESULT hr = S_OK; | |
| LONG public_refs; | |
| if (WAIT_OBJECT_0 != WaitForSingleObject(This->parent->remoting_mutex, INFINITE)) | |
| { | |
| ERR("Wait failed for ifproxy %p\n", This); | |
| return E_UNEXPECTED; | |
| } | |
| public_refs = This->refs; | |
| if (public_refs > 0) | |
| { | |
| IRemUnknown *remunk = NULL; | |
| TRACE("releasing %d refs\n", public_refs); | |
| hr = proxy_manager_get_remunknown(This->parent, &remunk); | |
| if (hr == S_OK) | |
| { | |
| REMINTERFACEREF rif; | |
| rif.ipid = This->stdobjref.ipid; | |
| rif.cPublicRefs = public_refs; | |
| rif.cPrivateRefs = 0; | |
| hr = IRemUnknown_RemRelease(remunk, 1, &rif); | |
| IRemUnknown_Release(remunk); | |
| if (hr == S_OK) | |
| InterlockedExchangeAdd((LONG *)&This->refs, -public_refs); | |
| else if (hr == RPC_E_DISCONNECTED) | |
| WARN("couldn't release references because object was " | |
| "disconnected: oxid = %s, oid = %s\n", | |
| wine_dbgstr_longlong(This->parent->oxid), | |
| wine_dbgstr_longlong(This->parent->oid)); | |
| else | |
| ERR("IRemUnknown_RemRelease failed with error 0x%08x\n", hr); | |
| } | |
| } | |
| ReleaseMutex(This->parent->remoting_mutex); | |
| return hr; | |
| } | |
| /* should be called inside This->parent->cs critical section */ | |
| static void ifproxy_disconnect(struct ifproxy * This) | |
| { | |
| ifproxy_release_public_refs(This); | |
| if (This->proxy) IRpcProxyBuffer_Disconnect(This->proxy); | |
| IRpcChannelBuffer_Release(This->chan); | |
| This->chan = NULL; | |
| } | |
| /* should be called in This->parent->cs critical section if it is an entry in parent's list */ | |
| static void ifproxy_destroy(struct ifproxy * This) | |
| { | |
| TRACE("%p\n", This); | |
| /* release public references to this object so that the stub can know | |
| * when to destroy itself */ | |
| ifproxy_release_public_refs(This); | |
| list_remove(&This->entry); | |
| if (This->chan) | |
| { | |
| IRpcChannelBuffer_Release(This->chan); | |
| This->chan = NULL; | |
| } | |
| if (This->proxy) IRpcProxyBuffer_Release(This->proxy); | |
| HeapFree(GetProcessHeap(), 0, This); | |
| } | |
| static HRESULT proxy_manager_construct( | |
| APARTMENT * apt, ULONG sorflags, OXID oxid, OID oid, | |
| const OXID_INFO *oxid_info, struct proxy_manager ** proxy_manager) | |
| { | |
| struct proxy_manager * This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); | |
| if (!This) return E_OUTOFMEMORY; | |
| This->remoting_mutex = CreateMutexW(NULL, FALSE, NULL); | |
| if (!This->remoting_mutex) | |
| { | |
| HeapFree(GetProcessHeap(), 0, This); | |
| return HRESULT_FROM_WIN32(GetLastError()); | |
| } | |
| if (oxid_info) | |
| { | |
| This->oxid_info.dwPid = oxid_info->dwPid; | |
| This->oxid_info.dwTid = oxid_info->dwTid; | |
| This->oxid_info.ipidRemUnknown = oxid_info->ipidRemUnknown; | |
| This->oxid_info.dwAuthnHint = oxid_info->dwAuthnHint; | |
| This->oxid_info.psa = NULL /* FIXME: copy from oxid_info */; | |
| } | |
| else | |
| { | |
| HRESULT hr = RPC_ResolveOxid(oxid, &This->oxid_info); | |
| if (FAILED(hr)) | |
| { | |
| CloseHandle(This->remoting_mutex); | |
| HeapFree(GetProcessHeap(), 0, This); | |
| return hr; | |
| } | |
| } | |
| This->IMultiQI_iface.lpVtbl = &ClientIdentity_Vtbl; | |
| This->IMarshal_iface.lpVtbl = &ProxyMarshal_Vtbl; | |
| This->IClientSecurity_iface.lpVtbl = &ProxyCliSec_Vtbl; | |
| list_init(&This->entry); | |
| list_init(&This->interfaces); | |
| InitializeCriticalSection(&This->cs); | |
| DEBUG_SET_CRITSEC_NAME(&This->cs, "proxy_manager"); | |
| /* the apartment the object was unmarshaled into */ | |
| This->parent = apt; | |
| /* the source apartment and id of the object */ | |
| This->oxid = oxid; | |
| This->oid = oid; | |
| This->refs = 1; | |
| /* the DCOM draft specification states that the SORF_NOPING flag is | |
| * proxy manager specific, not ifproxy specific, so this implies that we | |
| * should store the STDOBJREF flags here in the proxy manager. */ | |
| This->sorflags = sorflags; | |
| /* we create the IRemUnknown proxy on demand */ | |
| This->remunk = NULL; | |
| /* initialise these values to the weakest values and they will be | |
| * overwritten in proxy_manager_set_context */ | |
| This->dest_context = MSHCTX_INPROC; | |
| This->dest_context_data = NULL; | |
| EnterCriticalSection(&apt->cs); | |
| /* FIXME: we are dependent on the ordering in here to make sure a proxy's | |
| * IRemUnknown proxy doesn't get destroyed before the regular proxy does | |
| * because we need the IRemUnknown proxy during the destruction of the | |
| * regular proxy. Ideally, we should maintain a separate list for the | |
| * IRemUnknown proxies that need late destruction */ | |
| list_add_tail(&apt->proxies, &This->entry); | |
| LeaveCriticalSection(&apt->cs); | |
| TRACE("%p created for OXID %s, OID %s\n", This, | |
| wine_dbgstr_longlong(oxid), wine_dbgstr_longlong(oid)); | |
| *proxy_manager = This; | |
| return S_OK; | |
| } | |
| static inline void proxy_manager_set_context(struct proxy_manager *This, MSHCTX dest_context, void *dest_context_data) | |
| { | |
| MSHCTX old_dest_context; | |
| MSHCTX new_dest_context; | |
| do | |
| { | |
| old_dest_context = This->dest_context; | |
| new_dest_context = old_dest_context; | |
| /* "stronger" values overwrite "weaker" values. stronger values are | |
| * ones that disable more optimisations */ | |
| switch (old_dest_context) | |
| { | |
| case MSHCTX_INPROC: | |
| new_dest_context = dest_context; | |
| break; | |
| case MSHCTX_CROSSCTX: | |
| switch (dest_context) | |
| { | |
| case MSHCTX_INPROC: | |
| break; | |
| default: | |
| new_dest_context = dest_context; | |
| } | |
| break; | |
| case MSHCTX_LOCAL: | |
| switch (dest_context) | |
| { | |
| case MSHCTX_INPROC: | |
| case MSHCTX_CROSSCTX: | |
| break; | |
| default: | |
| new_dest_context = dest_context; | |
| } | |
| break; | |
| case MSHCTX_NOSHAREDMEM: | |
| switch (dest_context) | |
| { | |
| case MSHCTX_DIFFERENTMACHINE: | |
| new_dest_context = dest_context; | |
| break; | |
| default: | |
| break; | |
| } | |
| break; | |
| default: | |
| break; | |
| } | |
| if (old_dest_context == new_dest_context) break; | |
| new_dest_context = InterlockedCompareExchange((PLONG)&This->dest_context, new_dest_context, old_dest_context); | |
| } while (new_dest_context != old_dest_context); | |
| if (dest_context_data) | |
| InterlockedExchangePointer(&This->dest_context_data, dest_context_data); | |
| } | |
| static HRESULT proxy_manager_query_local_interface(struct proxy_manager * This, REFIID riid, void ** ppv) | |
| { | |
| HRESULT hr; | |
| struct ifproxy * ifproxy; | |
| TRACE("%s\n", debugstr_guid(riid)); | |
| if (IsEqualIID(riid, &IID_IUnknown) || | |
| IsEqualIID(riid, &IID_IMultiQI)) | |
| { | |
| *ppv = &This->IMultiQI_iface; | |
| IMultiQI_AddRef(&This->IMultiQI_iface); | |
| return S_OK; | |
| } | |
| if (IsEqualIID(riid, &IID_IMarshal)) | |
| { | |
| *ppv = &This->IMarshal_iface; | |
| IMarshal_AddRef(&This->IMarshal_iface); | |
| return S_OK; | |
| } | |
| if (IsEqualIID(riid, &IID_IClientSecurity)) | |
| { | |
| *ppv = &This->IClientSecurity_iface; | |
| IClientSecurity_AddRef(&This->IClientSecurity_iface); | |
| return S_OK; | |
| } | |
| hr = proxy_manager_find_ifproxy(This, riid, &ifproxy); | |
| if (hr == S_OK) | |
| { | |
| *ppv = ifproxy->iface; | |
| IUnknown_AddRef((IUnknown *)*ppv); | |
| return S_OK; | |
| } | |
| *ppv = NULL; | |
| return E_NOINTERFACE; | |
| } | |
| static HRESULT proxy_manager_create_ifproxy( | |
| struct proxy_manager * This, const STDOBJREF *stdobjref, REFIID riid, | |
| IRpcChannelBuffer * channel, struct ifproxy ** iif_out) | |
| { | |
| HRESULT hr; | |
| IPSFactoryBuffer * psfb; | |
| struct ifproxy * ifproxy = HeapAlloc(GetProcessHeap(), 0, sizeof(*ifproxy)); | |
| if (!ifproxy) return E_OUTOFMEMORY; | |
| list_init(&ifproxy->entry); | |
| ifproxy->parent = This; | |
| ifproxy->stdobjref = *stdobjref; | |
| ifproxy->iid = *riid; | |
| ifproxy->refs = 0; | |
| ifproxy->proxy = NULL; | |
| assert(channel); | |
| ifproxy->chan = channel; /* FIXME: we should take the binding strings and construct the channel in this function */ | |
| /* the IUnknown interface is special because it does not have a | |
| * proxy associated with the ifproxy as we handle IUnknown ourselves */ | |
| if (IsEqualIID(riid, &IID_IUnknown)) | |
| { | |
| ifproxy->iface = &This->IMultiQI_iface; | |
| IMultiQI_AddRef(&This->IMultiQI_iface); | |
| hr = S_OK; | |
| } | |
| else | |
| { | |
| hr = get_facbuf_for_iid(riid, &psfb); | |
| if (hr == S_OK) | |
| { | |
| /* important note: the outer unknown is set to the proxy manager. | |
| * This ensures the COM identity rules are not violated, by having a | |
| * one-to-one mapping of objects on the proxy side to objects on the | |
| * stub side, no matter which interface you view the object through */ | |
| hr = IPSFactoryBuffer_CreateProxy(psfb, (IUnknown*)&This->IMultiQI_iface, riid, | |
| &ifproxy->proxy, &ifproxy->iface); | |
| IPSFactoryBuffer_Release(psfb); | |
| if (hr != S_OK) | |
| ERR("Could not create proxy for interface %s, error 0x%08x\n", | |
| debugstr_guid(riid), hr); | |
| } | |
| else | |
| ERR("Could not get IPSFactoryBuffer for interface %s, error 0x%08x\n", | |
| debugstr_guid(riid), hr); | |
| if (hr == S_OK) | |
| hr = IRpcProxyBuffer_Connect(ifproxy->proxy, ifproxy->chan); | |
| } | |
| if (hr == S_OK) | |
| { | |
| EnterCriticalSection(&This->cs); | |
| list_add_tail(&This->interfaces, &ifproxy->entry); | |
| LeaveCriticalSection(&This->cs); | |
| *iif_out = ifproxy; | |
| TRACE("ifproxy %p created for IPID %s, interface %s with %u public refs\n", | |
| ifproxy, debugstr_guid(&stdobjref->ipid), debugstr_guid(riid), stdobjref->cPublicRefs); | |
| } | |
| else | |
| ifproxy_destroy(ifproxy); | |
| return hr; | |
| } | |
| static HRESULT proxy_manager_find_ifproxy(struct proxy_manager * This, REFIID riid, struct ifproxy ** ifproxy_found) | |
| { | |
| HRESULT hr = E_NOINTERFACE; /* assume not found */ | |
| struct list * cursor; | |
| EnterCriticalSection(&This->cs); | |
| LIST_FOR_EACH(cursor, &This->interfaces) | |
| { | |
| struct ifproxy * ifproxy = LIST_ENTRY(cursor, struct ifproxy, entry); | |
| if (IsEqualIID(riid, &ifproxy->iid)) | |
| { | |
| *ifproxy_found = ifproxy; | |
| hr = S_OK; | |
| break; | |
| } | |
| } | |
| LeaveCriticalSection(&This->cs); | |
| return hr; | |
| } | |
| static void proxy_manager_disconnect(struct proxy_manager * This) | |
| { | |
| struct list * cursor; | |
| TRACE("oxid = %s, oid = %s\n", wine_dbgstr_longlong(This->oxid), | |
| wine_dbgstr_longlong(This->oid)); | |
| EnterCriticalSection(&This->cs); | |
| /* SORFP_NOLIFTIMEMGMT proxies (for IRemUnknown) shouldn't be | |
| * disconnected - it won't do anything anyway, except cause | |
| * problems for other objects that depend on this proxy always | |
| * working */ | |
| if (!(This->sorflags & SORFP_NOLIFETIMEMGMT)) | |
| { | |
| LIST_FOR_EACH(cursor, &This->interfaces) | |
| { | |
| struct ifproxy * ifproxy = LIST_ENTRY(cursor, struct ifproxy, entry); | |
| ifproxy_disconnect(ifproxy); | |
| } | |
| } | |
| /* apartment is being destroyed so don't keep a pointer around to it */ | |
| This->parent = NULL; | |
| LeaveCriticalSection(&This->cs); | |
| } | |
| static HRESULT proxy_manager_get_remunknown(struct proxy_manager * This, IRemUnknown **remunk) | |
| { | |
| HRESULT hr = S_OK; | |
| struct apartment *apt; | |
| BOOL called_in_original_apt; | |
| /* we don't want to try and unmarshal or use IRemUnknown if we don't want | |
| * lifetime management */ | |
| if (This->sorflags & SORFP_NOLIFETIMEMGMT) | |
| return S_FALSE; | |
| apt = COM_CurrentApt(); | |
| if (!apt) | |
| return CO_E_NOTINITIALIZED; | |
| called_in_original_apt = This->parent && (This->parent->oxid == apt->oxid); | |
| EnterCriticalSection(&This->cs); | |
| /* only return the cached object if called from the original apartment. | |
| * in future, we might want to make the IRemUnknown proxy callable from any | |
| * apartment to avoid these checks */ | |
| if (This->remunk && called_in_original_apt) | |
| { | |
| /* already created - return existing object */ | |
| *remunk = This->remunk; | |
| IRemUnknown_AddRef(*remunk); | |
| } | |
| else if (!This->parent) | |
| { | |
| /* disconnected - we can't create IRemUnknown */ | |
| *remunk = NULL; | |
| hr = S_FALSE; | |
| } | |
| else | |
| { | |
| STDOBJREF stdobjref; | |
| /* Don't want IRemUnknown lifetime management as this is IRemUnknown! | |
| * We also don't care about whether or not the stub is still alive */ | |
| stdobjref.flags = SORFP_NOLIFETIMEMGMT | SORF_NOPING; | |
| stdobjref.cPublicRefs = 1; | |
| /* oxid of destination object */ | |
| stdobjref.oxid = This->oxid; | |
| /* FIXME: what should be used for the oid? The DCOM draft doesn't say */ | |
| stdobjref.oid = (OID)-1; | |
| stdobjref.ipid = This->oxid_info.ipidRemUnknown; | |
| /* do the unmarshal */ | |
| hr = unmarshal_object(&stdobjref, COM_CurrentApt(), This->dest_context, | |
| This->dest_context_data, &IID_IRemUnknown, | |
| &This->oxid_info, (void**)remunk); | |
| if (hr == S_OK && called_in_original_apt) | |
| { | |
| This->remunk = *remunk; | |
| IRemUnknown_AddRef(This->remunk); | |
| } | |
| } | |
| LeaveCriticalSection(&This->cs); | |
| TRACE("got IRemUnknown* pointer %p, hr = 0x%08x\n", *remunk, hr); | |
| return hr; | |
| } | |
| /* destroys a proxy manager, freeing the memory it used. | |
| * Note: this function should not be called from a list iteration in the | |
| * apartment, due to the fact that it removes itself from the apartment and | |
| * it could add a proxy to IRemUnknown into the apartment. */ | |
| static void proxy_manager_destroy(struct proxy_manager * This) | |
| { | |
| struct list * cursor; | |
| TRACE("oxid = %s, oid = %s\n", wine_dbgstr_longlong(This->oxid), | |
| wine_dbgstr_longlong(This->oid)); | |
| if (This->parent) | |
| { | |
| EnterCriticalSection(&This->parent->cs); | |
| /* remove ourself from the list of proxy objects in the apartment */ | |
| LIST_FOR_EACH(cursor, &This->parent->proxies) | |
| { | |
| if (cursor == &This->entry) | |
| { | |
| list_remove(&This->entry); | |
| break; | |
| } | |
| } | |
| LeaveCriticalSection(&This->parent->cs); | |
| } | |
| /* destroy all of the interface proxies */ | |
| while ((cursor = list_head(&This->interfaces))) | |
| { | |
| struct ifproxy * ifproxy = LIST_ENTRY(cursor, struct ifproxy, entry); | |
| ifproxy_destroy(ifproxy); | |
| } | |
| if (This->remunk) IRemUnknown_Release(This->remunk); | |
| CoTaskMemFree(This->oxid_info.psa); | |
| DEBUG_CLEAR_CRITSEC_NAME(&This->cs); | |
| DeleteCriticalSection(&This->cs); | |
| CloseHandle(This->remoting_mutex); | |
| HeapFree(GetProcessHeap(), 0, This); | |
| } | |
| /* finds the proxy manager corresponding to a given OXID and OID that has | |
| * been unmarshaled in the specified apartment. The caller must release the | |
| * reference to the proxy_manager when the object is no longer used. */ | |
| static BOOL find_proxy_manager(APARTMENT * apt, OXID oxid, OID oid, struct proxy_manager ** proxy_found) | |
| { | |
| BOOL found = FALSE; | |
| struct list * cursor; | |
| EnterCriticalSection(&apt->cs); | |
| LIST_FOR_EACH(cursor, &apt->proxies) | |
| { | |
| struct proxy_manager * proxy = LIST_ENTRY(cursor, struct proxy_manager, entry); | |
| if ((oxid == proxy->oxid) && (oid == proxy->oid)) | |
| { | |
| /* be careful of a race with ClientIdentity_Release, which would | |
| * cause us to return a proxy which is in the process of being | |
| * destroyed */ | |
| if (IMultiQI_AddRef(&proxy->IMultiQI_iface) != 0) | |
| { | |
| *proxy_found = proxy; | |
| found = TRUE; | |
| break; | |
| } | |
| } | |
| } | |
| LeaveCriticalSection(&apt->cs); | |
| return found; | |
| } | |
| HRESULT apartment_disconnectproxies(struct apartment *apt) | |
| { | |
| struct list * cursor; | |
| LIST_FOR_EACH(cursor, &apt->proxies) | |
| { | |
| struct proxy_manager * proxy = LIST_ENTRY(cursor, struct proxy_manager, entry); | |
| proxy_manager_disconnect(proxy); | |
| } | |
| return S_OK; | |
| } | |
| /********************** StdMarshal implementation ****************************/ | |
| typedef struct _StdMarshalImpl | |
| { | |
| IMarshal IMarshal_iface; | |
| LONG ref; | |
| DWORD dest_context; | |
| void *dest_context_data; | |
| } StdMarshalImpl; | |
| static inline StdMarshalImpl *impl_from_StdMarshal(IMarshal *iface) | |
| { | |
| return CONTAINING_RECORD(iface, StdMarshalImpl, IMarshal_iface); | |
| } | |
| static HRESULT WINAPI | |
| StdMarshalImpl_QueryInterface(IMarshal *iface, REFIID riid, void **ppv) | |
| { | |
| *ppv = NULL; | |
| if (IsEqualIID(&IID_IUnknown, riid) || IsEqualIID(&IID_IMarshal, riid)) | |
| { | |
| *ppv = iface; | |
| IMarshal_AddRef(iface); | |
| return S_OK; | |
| } | |
| FIXME("No interface for %s.\n", debugstr_guid(riid)); | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI | |
| StdMarshalImpl_AddRef(IMarshal *iface) | |
| { | |
| StdMarshalImpl *This = impl_from_StdMarshal(iface); | |
| return InterlockedIncrement(&This->ref); | |
| } | |
| static ULONG WINAPI | |
| StdMarshalImpl_Release(IMarshal *iface) | |
| { | |
| StdMarshalImpl *This = impl_from_StdMarshal(iface); | |
| ULONG ref = InterlockedDecrement(&This->ref); | |
| if (!ref) HeapFree(GetProcessHeap(),0,This); | |
| return ref; | |
| } | |
| static HRESULT WINAPI | |
| StdMarshalImpl_GetUnmarshalClass( | |
| IMarshal *iface, REFIID riid, void* pv, DWORD dwDestContext, | |
| void* pvDestContext, DWORD mshlflags, CLSID* pCid) | |
| { | |
| *pCid = CLSID_DfMarshal; | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI | |
| StdMarshalImpl_GetMarshalSizeMax( | |
| IMarshal *iface, REFIID riid, void* pv, DWORD dwDestContext, | |
| void* pvDestContext, DWORD mshlflags, DWORD* pSize) | |
| { | |
| *pSize = sizeof(STDOBJREF); | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI | |
| StdMarshalImpl_MarshalInterface( | |
| IMarshal *iface, IStream *pStm,REFIID riid, void* pv, DWORD dest_context, | |
| void* dest_context_data, DWORD mshlflags) | |
| { | |
| STDOBJREF stdobjref; | |
| ULONG res; | |
| HRESULT hres; | |
| APARTMENT *apt = COM_CurrentApt(); | |
| TRACE("(...,%s,...)\n", debugstr_guid(riid)); | |
| if (!apt) | |
| { | |
| ERR("Apartment not initialized\n"); | |
| return CO_E_NOTINITIALIZED; | |
| } | |
| /* make sure this apartment can be reached from other threads / processes */ | |
| RPC_StartRemoting(apt); | |
| hres = marshal_object(apt, &stdobjref, riid, pv, dest_context, dest_context_data, mshlflags); | |
| if (hres != S_OK) | |
| { | |
| ERR("Failed to create ifstub, hres=0x%x\n", hres); | |
| return hres; | |
| } | |
| return IStream_Write(pStm, &stdobjref, sizeof(stdobjref), &res); | |
| } | |
| /* helper for StdMarshalImpl_UnmarshalInterface - does the unmarshaling with | |
| * no questions asked about the rules surrounding same-apartment unmarshals | |
| * and table marshaling */ | |
| 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) | |
| { | |
| struct proxy_manager *proxy_manager = NULL; | |
| HRESULT hr = S_OK; | |
| assert(apt); | |
| TRACE("stdobjref: flags = %04x cPublicRefs = %d oxid = %s oid = %s ipid = %s\n", | |
| stdobjref->flags, stdobjref->cPublicRefs, | |
| wine_dbgstr_longlong(stdobjref->oxid), | |
| wine_dbgstr_longlong(stdobjref->oid), | |
| debugstr_guid(&stdobjref->ipid)); | |
| /* create a new proxy manager if one doesn't already exist for the | |
| * object */ | |
| if (!find_proxy_manager(apt, stdobjref->oxid, stdobjref->oid, &proxy_manager)) | |
| { | |
| hr = proxy_manager_construct(apt, stdobjref->flags, | |
| stdobjref->oxid, stdobjref->oid, oxid_info, | |
| &proxy_manager); | |
| } | |
| else | |
| TRACE("proxy manager already created, using\n"); | |
| if (hr == S_OK) | |
| { | |
| struct ifproxy * ifproxy; | |
| proxy_manager_set_context(proxy_manager, dest_context, dest_context_data); | |
| hr = proxy_manager_find_ifproxy(proxy_manager, riid, &ifproxy); | |
| if (hr == E_NOINTERFACE) | |
| { | |
| IRpcChannelBuffer *chanbuf; | |
| hr = RPC_CreateClientChannel(&stdobjref->oxid, &stdobjref->ipid, | |
| &proxy_manager->oxid_info, | |
| proxy_manager->dest_context, | |
| proxy_manager->dest_context_data, | |
| &chanbuf); | |
| if (hr == S_OK) | |
| hr = proxy_manager_create_ifproxy(proxy_manager, stdobjref, | |
| riid, chanbuf, &ifproxy); | |
| } | |
| else | |
| IUnknown_AddRef((IUnknown *)ifproxy->iface); | |
| if (hr == S_OK) | |
| { | |
| InterlockedExchangeAdd((LONG *)&ifproxy->refs, stdobjref->cPublicRefs); | |
| /* get at least one external reference to the object to keep it alive */ | |
| hr = ifproxy_get_public_ref(ifproxy); | |
| if (FAILED(hr)) | |
| ifproxy_destroy(ifproxy); | |
| } | |
| if (hr == S_OK) | |
| *object = ifproxy->iface; | |
| } | |
| /* release our reference to the proxy manager - the client/apartment | |
| * will hold on to the remaining reference for us */ | |
| if (proxy_manager) IMultiQI_Release(&proxy_manager->IMultiQI_iface); | |
| return hr; | |
| } | |
| static HRESULT WINAPI | |
| StdMarshalImpl_UnmarshalInterface(IMarshal *iface, IStream *pStm, REFIID riid, void **ppv) | |
| { | |
| StdMarshalImpl *This = impl_from_StdMarshal(iface); | |
| struct stub_manager *stubmgr = NULL; | |
| STDOBJREF stdobjref; | |
| ULONG res; | |
| HRESULT hres; | |
| APARTMENT *apt = COM_CurrentApt(); | |
| APARTMENT *stub_apt; | |
| OXID oxid; | |
| TRACE("(...,%s,....)\n", debugstr_guid(riid)); | |
| /* we need an apartment to unmarshal into */ | |
| if (!apt) | |
| { | |
| ERR("Apartment not initialized\n"); | |
| return CO_E_NOTINITIALIZED; | |
| } | |
| /* read STDOBJREF from wire */ | |
| hres = IStream_Read(pStm, &stdobjref, sizeof(stdobjref), &res); | |
| if (hres != S_OK) return STG_E_READFAULT; | |
| hres = apartment_getoxid(apt, &oxid); | |
| if (hres != S_OK) return hres; | |
| /* check if we're marshalling back to ourselves */ | |
| if ((oxid == stdobjref.oxid) && (stubmgr = get_stub_manager(apt, stdobjref.oid))) | |
| { | |
| TRACE("Unmarshalling object marshalled in same apartment for iid %s, " | |
| "returning original object %p\n", debugstr_guid(riid), stubmgr->object); | |
| hres = IUnknown_QueryInterface(stubmgr->object, riid, ppv); | |
| /* unref the ifstub. FIXME: only do this on success? */ | |
| if (!stub_manager_is_table_marshaled(stubmgr, &stdobjref.ipid)) | |
| stub_manager_ext_release(stubmgr, stdobjref.cPublicRefs, stdobjref.flags & SORFP_TABLEWEAK, FALSE); | |
| stub_manager_int_release(stubmgr); | |
| return hres; | |
| } | |
| /* notify stub manager about unmarshal if process-local object. | |
| * note: if the oxid is not found then we and native will quite happily | |
| * ignore table marshaling and normal marshaling rules regarding number of | |
| * unmarshals, etc, but if you abuse these rules then your proxy could end | |
| * up returning RPC_E_DISCONNECTED. */ | |
| if ((stub_apt = apartment_findfromoxid(stdobjref.oxid, TRUE))) | |
| { | |
| if ((stubmgr = get_stub_manager(stub_apt, stdobjref.oid))) | |
| { | |
| if (!stub_manager_notify_unmarshal(stubmgr, &stdobjref.ipid)) | |
| hres = CO_E_OBJNOTCONNECTED; | |
| } | |
| else | |
| { | |
| WARN("Couldn't find object for OXID %s, OID %s, assuming disconnected\n", | |
| wine_dbgstr_longlong(stdobjref.oxid), | |
| wine_dbgstr_longlong(stdobjref.oid)); | |
| hres = CO_E_OBJNOTCONNECTED; | |
| } | |
| } | |
| else | |
| TRACE("Treating unmarshal from OXID %s as inter-process\n", | |
| wine_dbgstr_longlong(stdobjref.oxid)); | |
| if (hres == S_OK) | |
| hres = unmarshal_object(&stdobjref, apt, This->dest_context, | |
| This->dest_context_data, riid, | |
| stubmgr ? &stubmgr->oxid_info : NULL, ppv); | |
| if (stubmgr) stub_manager_int_release(stubmgr); | |
| if (stub_apt) apartment_release(stub_apt); | |
| if (hres != S_OK) WARN("Failed with error 0x%08x\n", hres); | |
| else TRACE("Successfully created proxy %p\n", *ppv); | |
| return hres; | |
| } | |
| static HRESULT WINAPI | |
| StdMarshalImpl_ReleaseMarshalData(IMarshal *iface, IStream *pStm) | |
| { | |
| STDOBJREF stdobjref; | |
| ULONG res; | |
| HRESULT hres; | |
| struct stub_manager *stubmgr; | |
| APARTMENT *apt; | |
| TRACE("iface=%p, pStm=%p\n", iface, pStm); | |
| hres = IStream_Read(pStm, &stdobjref, sizeof(stdobjref), &res); | |
| if (hres != S_OK) return STG_E_READFAULT; | |
| TRACE("oxid = %s, oid = %s, ipid = %s\n", | |
| wine_dbgstr_longlong(stdobjref.oxid), | |
| wine_dbgstr_longlong(stdobjref.oid), | |
| wine_dbgstr_guid(&stdobjref.ipid)); | |
| if (!(apt = apartment_findfromoxid(stdobjref.oxid, TRUE))) | |
| { | |
| WARN("Could not map OXID %s to apartment object\n", | |
| wine_dbgstr_longlong(stdobjref.oxid)); | |
| return RPC_E_INVALID_OBJREF; | |
| } | |
| if (!(stubmgr = get_stub_manager(apt, stdobjref.oid))) | |
| { | |
| apartment_release(apt); | |
| ERR("could not map object ID to stub manager, oxid=%s, oid=%s\n", | |
| wine_dbgstr_longlong(stdobjref.oxid), wine_dbgstr_longlong(stdobjref.oid)); | |
| return RPC_E_INVALID_OBJREF; | |
| } | |
| stub_manager_release_marshal_data(stubmgr, stdobjref.cPublicRefs, &stdobjref.ipid, stdobjref.flags & SORFP_TABLEWEAK); | |
| stub_manager_int_release(stubmgr); | |
| apartment_release(apt); | |
| return S_OK; | |
| } | |
| static HRESULT WINAPI | |
| StdMarshalImpl_DisconnectObject(IMarshal *iface, DWORD dwReserved) | |
| { | |
| FIXME("(), stub!\n"); | |
| return S_OK; | |
| } | |
| static const IMarshalVtbl StdMarshalVtbl = | |
| { | |
| StdMarshalImpl_QueryInterface, | |
| StdMarshalImpl_AddRef, | |
| StdMarshalImpl_Release, | |
| StdMarshalImpl_GetUnmarshalClass, | |
| StdMarshalImpl_GetMarshalSizeMax, | |
| StdMarshalImpl_MarshalInterface, | |
| StdMarshalImpl_UnmarshalInterface, | |
| StdMarshalImpl_ReleaseMarshalData, | |
| StdMarshalImpl_DisconnectObject | |
| }; | |
| static HRESULT StdMarshalImpl_Construct(REFIID riid, DWORD dest_context, void *dest_context_data, void** ppvObject) | |
| { | |
| HRESULT hr; | |
| StdMarshalImpl *pStdMarshal = HeapAlloc(GetProcessHeap(), 0, sizeof(StdMarshalImpl)); | |
| if (!pStdMarshal) | |
| return E_OUTOFMEMORY; | |
| pStdMarshal->IMarshal_iface.lpVtbl = &StdMarshalVtbl; | |
| pStdMarshal->ref = 0; | |
| pStdMarshal->dest_context = dest_context; | |
| pStdMarshal->dest_context_data = dest_context_data; | |
| hr = IMarshal_QueryInterface(&pStdMarshal->IMarshal_iface, riid, ppvObject); | |
| if (FAILED(hr)) | |
| HeapFree(GetProcessHeap(), 0, pStdMarshal); | |
| return hr; | |
| } | |
| /*********************************************************************** | |
| * CoGetStandardMarshal [OLE32.@] | |
| * | |
| * Gets or creates a standard marshal object. | |
| * | |
| * PARAMS | |
| * riid [I] Interface identifier of the pUnk object. | |
| * pUnk [I] Optional. Object to get the marshal object for. | |
| * dwDestContext [I] Destination. Used to enable or disable optimizations. | |
| * pvDestContext [I] Reserved. Must be NULL. | |
| * mshlflags [I] Flags affecting the marshaling process. | |
| * ppMarshal [O] Address where marshal object will be stored. | |
| * | |
| * RETURNS | |
| * Success: S_OK. | |
| * Failure: HRESULT code. | |
| * | |
| * NOTES | |
| * | |
| * The function retrieves the IMarshal object associated with an object if | |
| * that object is currently an active stub, otherwise a new marshal object is | |
| * created. | |
| */ | |
| HRESULT WINAPI CoGetStandardMarshal(REFIID riid, IUnknown *pUnk, | |
| DWORD dwDestContext, LPVOID pvDestContext, | |
| DWORD mshlflags, LPMARSHAL *ppMarshal) | |
| { | |
| if (pUnk == NULL) | |
| { | |
| FIXME("(%s,NULL,%x,%p,%x,%p), unimplemented yet.\n", | |
| debugstr_guid(riid),dwDestContext,pvDestContext,mshlflags,ppMarshal); | |
| return E_NOTIMPL; | |
| } | |
| TRACE("(%s,%p,%x,%p,%x,%p)\n", | |
| debugstr_guid(riid),pUnk,dwDestContext,pvDestContext,mshlflags,ppMarshal); | |
| return StdMarshalImpl_Construct(&IID_IMarshal, dwDestContext, pvDestContext, (void**)ppMarshal); | |
| } | |
| /*********************************************************************** | |
| * get_marshaler [internal] | |
| * | |
| * Retrieves an IMarshal interface for an object. | |
| */ | |
| static HRESULT get_marshaler(REFIID riid, IUnknown *pUnk, DWORD dwDestContext, | |
| void *pvDestContext, DWORD mshlFlags, | |
| LPMARSHAL *pMarshal) | |
| { | |
| HRESULT hr; | |
| if (!pUnk) | |
| return E_POINTER; | |
| hr = IUnknown_QueryInterface(pUnk, &IID_IMarshal, (LPVOID*)pMarshal); | |
| if (hr != S_OK) | |
| hr = CoGetStandardMarshal(riid, pUnk, dwDestContext, pvDestContext, | |
| mshlFlags, pMarshal); | |
| return hr; | |
| } | |
| /*********************************************************************** | |
| * get_unmarshaler_from_stream [internal] | |
| * | |
| * Creates an IMarshal* object according to the data marshaled to the stream. | |
| * The function leaves the stream pointer at the start of the data written | |
| * to the stream by the IMarshal* object. | |
| */ | |
| static HRESULT get_unmarshaler_from_stream(IStream *stream, IMarshal **marshal, IID *iid) | |
| { | |
| HRESULT hr; | |
| ULONG res; | |
| OBJREF objref; | |
| /* read common OBJREF header */ | |
| hr = IStream_Read(stream, &objref, FIELD_OFFSET(OBJREF, u_objref), &res); | |
| if (hr != S_OK || (res != FIELD_OFFSET(OBJREF, u_objref))) | |
| { | |
| ERR("Failed to read common OBJREF header, 0x%08x\n", hr); | |
| return STG_E_READFAULT; | |
| } | |
| /* sanity check on header */ | |
| if (objref.signature != OBJREF_SIGNATURE) | |
| { | |
| ERR("Bad OBJREF signature 0x%08x\n", objref.signature); | |
| return RPC_E_INVALID_OBJREF; | |
| } | |
| if (iid) *iid = objref.iid; | |
| /* FIXME: handler marshaling */ | |
| if (objref.flags & OBJREF_STANDARD) | |
| { | |
| TRACE("Using standard unmarshaling\n"); | |
| hr = StdMarshalImpl_Construct(&IID_IMarshal, 0, NULL, (LPVOID*)marshal); | |
| } | |
| else if (objref.flags & OBJREF_CUSTOM) | |
| { | |
| ULONG custom_header_size = FIELD_OFFSET(OBJREF, u_objref.u_custom.pData) - | |
| FIELD_OFFSET(OBJREF, u_objref.u_custom); | |
| TRACE("Using custom unmarshaling\n"); | |
| /* read constant sized OR_CUSTOM data from stream */ | |
| hr = IStream_Read(stream, &objref.u_objref.u_custom, | |
| custom_header_size, &res); | |
| if (hr != S_OK || (res != custom_header_size)) | |
| { | |
| ERR("Failed to read OR_CUSTOM header, 0x%08x\n", hr); | |
| return STG_E_READFAULT; | |
| } | |
| /* now create the marshaler specified in the stream */ | |
| hr = CoCreateInstance(&objref.u_objref.u_custom.clsid, NULL, | |
| CLSCTX_INPROC_SERVER, &IID_IMarshal, | |
| (LPVOID*)marshal); | |
| } | |
| else | |
| { | |
| FIXME("Invalid or unimplemented marshaling type specified: %x\n", | |
| objref.flags); | |
| return RPC_E_INVALID_OBJREF; | |
| } | |
| if (hr != S_OK) | |
| ERR("Failed to create marshal, 0x%08x\n", hr); | |
| return hr; | |
| } | |
| /*********************************************************************** | |
| * CoGetMarshalSizeMax [OLE32.@] | |
| * | |
| * Gets the maximum amount of data that will be needed by a marshal. | |
| * | |
| * PARAMS | |
| * pulSize [O] Address where maximum marshal size will be stored. | |
| * riid [I] Identifier of the interface to marshal. | |
| * pUnk [I] Pointer to the object to marshal. | |
| * dwDestContext [I] Destination. Used to enable or disable optimizations. | |
| * pvDestContext [I] Reserved. Must be NULL. | |
| * mshlFlags [I] Flags that affect the marshaling. See CoMarshalInterface(). | |
| * | |
| * RETURNS | |
| * Success: S_OK. | |
| * Failure: HRESULT code. | |
| * | |
| * SEE ALSO | |
| * CoMarshalInterface(). | |
| */ | |
| HRESULT WINAPI CoGetMarshalSizeMax(ULONG *pulSize, REFIID riid, IUnknown *pUnk, | |
| DWORD dwDestContext, void *pvDestContext, | |
| DWORD mshlFlags) | |
| { | |
| HRESULT hr; | |
| LPMARSHAL pMarshal; | |
| CLSID marshaler_clsid; | |
| hr = get_marshaler(riid, pUnk, dwDestContext, pvDestContext, mshlFlags, &pMarshal); | |
| if (hr != S_OK) | |
| return hr; | |
| hr = IMarshal_GetUnmarshalClass(pMarshal, riid, pUnk, dwDestContext, | |
| pvDestContext, mshlFlags, &marshaler_clsid); | |
| if (hr != S_OK) | |
| { | |
| ERR("IMarshal::GetUnmarshalClass failed, 0x%08x\n", hr); | |
| IMarshal_Release(pMarshal); | |
| return hr; | |
| } | |
| hr = IMarshal_GetMarshalSizeMax(pMarshal, riid, pUnk, dwDestContext, | |
| pvDestContext, mshlFlags, pulSize); | |
| if (IsEqualCLSID(&marshaler_clsid, &CLSID_DfMarshal)) | |
| /* add on the size of the common header */ | |
| *pulSize += FIELD_OFFSET(OBJREF, u_objref); | |
| else | |
| /* custom marshaling: add on the size of the whole OBJREF structure | |
| * like native does */ | |
| *pulSize += sizeof(OBJREF); | |
| IMarshal_Release(pMarshal); | |
| return hr; | |
| } | |
| static void dump_MSHLFLAGS(MSHLFLAGS flags) | |
| { | |
| if (flags & MSHLFLAGS_TABLESTRONG) | |
| TRACE(" MSHLFLAGS_TABLESTRONG"); | |
| if (flags & MSHLFLAGS_TABLEWEAK) | |
| TRACE(" MSHLFLAGS_TABLEWEAK"); | |
| if (!(flags & (MSHLFLAGS_TABLESTRONG|MSHLFLAGS_TABLEWEAK))) | |
| TRACE(" MSHLFLAGS_NORMAL"); | |
| if (flags & MSHLFLAGS_NOPING) | |
| TRACE(" MSHLFLAGS_NOPING"); | |
| } | |
| /*********************************************************************** | |
| * CoMarshalInterface [OLE32.@] | |
| * | |
| * Marshals an interface into a stream so that the object can then be | |
| * unmarshaled from another COM apartment and used remotely. | |
| * | |
| * PARAMS | |
| * pStream [I] Stream the object will be marshaled into. | |
| * riid [I] Identifier of the interface to marshal. | |
| * pUnk [I] Pointer to the object to marshal. | |
| * dwDestContext [I] Destination. Used to enable or disable optimizations. | |
| * pvDestContext [I] Reserved. Must be NULL. | |
| * mshlFlags [I] Flags that affect the marshaling. See notes. | |
| * | |
| * RETURNS | |
| * Success: S_OK. | |
| * Failure: HRESULT code. | |
| * | |
| * NOTES | |
| * | |
| * The mshlFlags parameter can take one or more of the following flags: | |
| *| MSHLFLAGS_NORMAL - Unmarshal once, releases stub on last proxy release. | |
| *| MSHLFLAGS_TABLESTRONG - Unmarshal many, release when CoReleaseMarshalData() called. | |
| *| MSHLFLAGS_TABLEWEAK - Unmarshal many, releases stub on last proxy release. | |
| *| MSHLFLAGS_NOPING - No automatic garbage collection (and so reduces network traffic). | |
| * | |
| * If a marshaled object is not unmarshaled, then CoReleaseMarshalData() must | |
| * be called in order to release the resources used in the marshaling. | |
| * | |
| * SEE ALSO | |
| * CoUnmarshalInterface(), CoReleaseMarshalData(). | |
| */ | |
| HRESULT WINAPI CoMarshalInterface(IStream *pStream, REFIID riid, IUnknown *pUnk, | |
| DWORD dwDestContext, void *pvDestContext, | |
| DWORD mshlFlags) | |
| { | |
| HRESULT hr; | |
| CLSID marshaler_clsid; | |
| OBJREF objref; | |
| LPMARSHAL pMarshal; | |
| TRACE("(%p, %s, %p, %x, %p, ", pStream, debugstr_guid(riid), pUnk, | |
| dwDestContext, pvDestContext); | |
| dump_MSHLFLAGS(mshlFlags); | |
| TRACE(")\n"); | |
| if (!pUnk || !pStream) | |
| return E_INVALIDARG; | |
| objref.signature = OBJREF_SIGNATURE; | |
| objref.iid = *riid; | |
| /* get the marshaler for the specified interface */ | |
| hr = get_marshaler(riid, pUnk, dwDestContext, pvDestContext, mshlFlags, &pMarshal); | |
| if (hr != S_OK) | |
| { | |
| ERR("Failed to get marshaller, 0x%08x\n", hr); | |
| return hr; | |
| } | |
| hr = IMarshal_GetUnmarshalClass(pMarshal, riid, pUnk, dwDestContext, | |
| pvDestContext, mshlFlags, &marshaler_clsid); | |
| if (hr != S_OK) | |
| { | |
| ERR("IMarshal::GetUnmarshalClass failed, 0x%08x\n", hr); | |
| goto cleanup; | |
| } | |
| /* FIXME: implement handler marshaling too */ | |
| if (IsEqualCLSID(&marshaler_clsid, &CLSID_DfMarshal)) | |
| { | |
| TRACE("Using standard marshaling\n"); | |
| objref.flags = OBJREF_STANDARD; | |
| /* write the common OBJREF header to the stream */ | |
| hr = IStream_Write(pStream, &objref, FIELD_OFFSET(OBJREF, u_objref), NULL); | |
| if (hr != S_OK) | |
| { | |
| ERR("Failed to write OBJREF header to stream, 0x%08x\n", hr); | |
| goto cleanup; | |
| } | |
| } | |
| else | |
| { | |
| TRACE("Using custom marshaling\n"); | |
| objref.flags = OBJREF_CUSTOM; | |
| objref.u_objref.u_custom.clsid = marshaler_clsid; | |
| objref.u_objref.u_custom.cbExtension = 0; | |
| objref.u_objref.u_custom.size = 0; | |
| hr = IMarshal_GetMarshalSizeMax(pMarshal, riid, pUnk, dwDestContext, | |
| pvDestContext, mshlFlags, | |
| &objref.u_objref.u_custom.size); | |
| if (hr != S_OK) | |
| { | |
| ERR("Failed to get max size of marshal data, error 0x%08x\n", hr); | |
| goto cleanup; | |
| } | |
| /* write constant sized common header and OR_CUSTOM data into stream */ | |
| hr = IStream_Write(pStream, &objref, | |
| FIELD_OFFSET(OBJREF, u_objref.u_custom.pData), NULL); | |
| if (hr != S_OK) | |
| { | |
| ERR("Failed to write OR_CUSTOM header to stream with 0x%08x\n", hr); | |
| goto cleanup; | |
| } | |
| } | |
| TRACE("Calling IMarshal::MarshalInterface\n"); | |
| /* call helper object to do the actual marshaling */ | |
| hr = IMarshal_MarshalInterface(pMarshal, pStream, riid, pUnk, dwDestContext, | |
| pvDestContext, mshlFlags); | |
| if (hr != S_OK) | |
| { | |
| ERR("Failed to marshal the interface %s, %x\n", debugstr_guid(riid), hr); | |
| goto cleanup; | |
| } | |
| cleanup: | |
| IMarshal_Release(pMarshal); | |
| TRACE("completed with hr 0x%08x\n", hr); | |
| return hr; | |
| } | |
| /*********************************************************************** | |
| * CoUnmarshalInterface [OLE32.@] | |
| * | |
| * Unmarshals an object from a stream by creating a proxy to the remote | |
| * object, if necessary. | |
| * | |
| * PARAMS | |
| * | |
| * pStream [I] Stream containing the marshaled object. | |
| * riid [I] Interface identifier of the object to create a proxy to. | |
| * ppv [O] Address where proxy will be stored. | |
| * | |
| * RETURNS | |
| * | |
| * Success: S_OK. | |
| * Failure: HRESULT code. | |
| * | |
| * SEE ALSO | |
| * CoMarshalInterface(). | |
| */ | |
| HRESULT WINAPI CoUnmarshalInterface(IStream *pStream, REFIID riid, LPVOID *ppv) | |
| { | |
| HRESULT hr; | |
| LPMARSHAL pMarshal; | |
| IID iid; | |
| IUnknown *object; | |
| TRACE("(%p, %s, %p)\n", pStream, debugstr_guid(riid), ppv); | |
| if (!pStream || !ppv) | |
| return E_INVALIDARG; | |
| hr = get_unmarshaler_from_stream(pStream, &pMarshal, &iid); | |
| if (hr != S_OK) | |
| return hr; | |
| /* call the helper object to do the actual unmarshaling */ | |
| hr = IMarshal_UnmarshalInterface(pMarshal, pStream, &iid, (LPVOID*)&object); | |
| if (hr != S_OK) | |
| ERR("IMarshal::UnmarshalInterface failed, 0x%08x\n", hr); | |
| if (hr == S_OK) | |
| { | |
| /* IID_NULL means use the interface ID of the marshaled object */ | |
| if (!IsEqualIID(riid, &IID_NULL) && !IsEqualIID(riid, &iid)) | |
| { | |
| TRACE("requested interface != marshalled interface, additional QI needed\n"); | |
| hr = IUnknown_QueryInterface(object, riid, ppv); | |
| if (hr != S_OK) | |
| ERR("Couldn't query for interface %s, hr = 0x%08x\n", | |
| debugstr_guid(riid), hr); | |
| IUnknown_Release(object); | |
| } | |
| else | |
| { | |
| *ppv = object; | |
| } | |
| } | |
| IMarshal_Release(pMarshal); | |
| TRACE("completed with hr 0x%x\n", hr); | |
| return hr; | |
| } | |
| /*********************************************************************** | |
| * CoReleaseMarshalData [OLE32.@] | |
| * | |
| * Releases resources associated with an object that has been marshaled into | |
| * a stream. | |
| * | |
| * PARAMS | |
| * | |
| * pStream [I] The stream that the object has been marshaled into. | |
| * | |
| * RETURNS | |
| * Success: S_OK. | |
| * Failure: HRESULT error code. | |
| * | |
| * NOTES | |
| * | |
| * Call this function to release resources associated with a normal or | |
| * table-weak marshal that will not be unmarshaled, and all table-strong | |
| * marshals when they are no longer needed. | |
| * | |
| * SEE ALSO | |
| * CoMarshalInterface(), CoUnmarshalInterface(). | |
| */ | |
| HRESULT WINAPI CoReleaseMarshalData(IStream *pStream) | |
| { | |
| HRESULT hr; | |
| LPMARSHAL pMarshal; | |
| TRACE("(%p)\n", pStream); | |
| hr = get_unmarshaler_from_stream(pStream, &pMarshal, NULL); | |
| if (hr != S_OK) | |
| return hr; | |
| /* call the helper object to do the releasing of marshal data */ | |
| hr = IMarshal_ReleaseMarshalData(pMarshal, pStream); | |
| if (hr != S_OK) | |
| ERR("IMarshal::ReleaseMarshalData failed with error 0x%08x\n", hr); | |
| IMarshal_Release(pMarshal); | |
| return hr; | |
| } | |
| /*********************************************************************** | |
| * CoMarshalInterThreadInterfaceInStream [OLE32.@] | |
| * | |
| * Marshal an interface across threads in the same process. | |
| * | |
| * PARAMS | |
| * riid [I] Identifier of the interface to be marshalled. | |
| * pUnk [I] Pointer to IUnknown-derived interface that will be marshalled. | |
| * ppStm [O] Pointer to IStream object that is created and then used to store the marshalled interface. | |
| * | |
| * RETURNS | |
| * Success: S_OK | |
| * Failure: E_OUTOFMEMORY and other COM error codes | |
| * | |
| * SEE ALSO | |
| * CoMarshalInterface(), CoUnmarshalInterface() and CoGetInterfaceAndReleaseStream() | |
| */ | |
| HRESULT WINAPI CoMarshalInterThreadInterfaceInStream( | |
| REFIID riid, LPUNKNOWN pUnk, LPSTREAM * ppStm) | |
| { | |
| ULARGE_INTEGER xpos; | |
| LARGE_INTEGER seekto; | |
| HRESULT hres; | |
| TRACE("(%s, %p, %p)\n",debugstr_guid(riid), pUnk, ppStm); | |
| hres = CreateStreamOnHGlobal(NULL, TRUE, ppStm); | |
| if (FAILED(hres)) return hres; | |
| hres = CoMarshalInterface(*ppStm, riid, pUnk, MSHCTX_INPROC, NULL, MSHLFLAGS_NORMAL); | |
| if (SUCCEEDED(hres)) | |
| { | |
| memset(&seekto, 0, sizeof(seekto)); | |
| IStream_Seek(*ppStm, seekto, STREAM_SEEK_SET, &xpos); | |
| } | |
| else | |
| { | |
| IStream_Release(*ppStm); | |
| *ppStm = NULL; | |
| } | |
| return hres; | |
| } | |
| /*********************************************************************** | |
| * CoGetInterfaceAndReleaseStream [OLE32.@] | |
| * | |
| * Unmarshalls an interface from a stream and then releases the stream. | |
| * | |
| * PARAMS | |
| * pStm [I] Stream that contains the marshalled interface. | |
| * riid [I] Interface identifier of the object to unmarshall. | |
| * ppv [O] Address of pointer where the requested interface object will be stored. | |
| * | |
| * RETURNS | |
| * Success: S_OK | |
| * Failure: A COM error code | |
| * | |
| * SEE ALSO | |
| * CoMarshalInterThreadInterfaceInStream() and CoUnmarshalInterface() | |
| */ | |
| HRESULT WINAPI CoGetInterfaceAndReleaseStream(LPSTREAM pStm, REFIID riid, | |
| LPVOID *ppv) | |
| { | |
| HRESULT hres; | |
| TRACE("(%p, %s, %p)\n", pStm, debugstr_guid(riid), ppv); | |
| if(!pStm) return E_INVALIDARG; | |
| hres = CoUnmarshalInterface(pStm, riid, ppv); | |
| IStream_Release(pStm); | |
| return hres; | |
| } | |
| static HRESULT WINAPI StdMarshalCF_QueryInterface(LPCLASSFACTORY iface, | |
| REFIID riid, LPVOID *ppv) | |
| { | |
| *ppv = NULL; | |
| if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IClassFactory)) | |
| { | |
| *ppv = iface; | |
| return S_OK; | |
| } | |
| return E_NOINTERFACE; | |
| } | |
| static ULONG WINAPI StdMarshalCF_AddRef(LPCLASSFACTORY iface) | |
| { | |
| return 2; /* non-heap based object */ | |
| } | |
| static ULONG WINAPI StdMarshalCF_Release(LPCLASSFACTORY iface) | |
| { | |
| return 1; /* non-heap based object */ | |
| } | |
| static HRESULT WINAPI StdMarshalCF_CreateInstance(LPCLASSFACTORY iface, | |
| LPUNKNOWN pUnk, REFIID riid, LPVOID *ppv) | |
| { | |
| if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IMarshal)) | |
| return StdMarshalImpl_Construct(riid, 0, NULL, ppv); | |
| FIXME("(%s), not supported.\n",debugstr_guid(riid)); | |
| return E_NOINTERFACE; | |
| } | |
| static HRESULT WINAPI StdMarshalCF_LockServer(LPCLASSFACTORY iface, BOOL fLock) | |
| { | |
| FIXME("(%d), stub!\n",fLock); | |
| return S_OK; | |
| } | |
| static const IClassFactoryVtbl StdMarshalCFVtbl = | |
| { | |
| StdMarshalCF_QueryInterface, | |
| StdMarshalCF_AddRef, | |
| StdMarshalCF_Release, | |
| StdMarshalCF_CreateInstance, | |
| StdMarshalCF_LockServer | |
| }; | |
| static const IClassFactoryVtbl *StdMarshalCF = &StdMarshalCFVtbl; | |
| HRESULT MARSHAL_GetStandardMarshalCF(LPVOID *ppv) | |
| { | |
| *ppv = &StdMarshalCF; | |
| return S_OK; | |
| } | |
| /*********************************************************************** | |
| * CoMarshalHresult [OLE32.@] | |
| * | |
| * Marshals an HRESULT value into a stream. | |
| * | |
| * PARAMS | |
| * pStm [I] Stream that hresult will be marshalled into. | |
| * hresult [I] HRESULT to be marshalled. | |
| * | |
| * RETURNS | |
| * Success: S_OK | |
| * Failure: A COM error code | |
| * | |
| * SEE ALSO | |
| * CoUnmarshalHresult(). | |
| */ | |
| HRESULT WINAPI CoMarshalHresult(LPSTREAM pStm, HRESULT hresult) | |
| { | |
| return IStream_Write(pStm, &hresult, sizeof(hresult), NULL); | |
| } | |
| /*********************************************************************** | |
| * CoUnmarshalHresult [OLE32.@] | |
| * | |
| * Unmarshals an HRESULT value from a stream. | |
| * | |
| * PARAMS | |
| * pStm [I] Stream that hresult will be unmarshalled from. | |
| * phresult [I] Pointer to HRESULT where the value will be unmarshalled to. | |
| * | |
| * RETURNS | |
| * Success: S_OK | |
| * Failure: A COM error code | |
| * | |
| * SEE ALSO | |
| * CoMarshalHresult(). | |
| */ | |
| HRESULT WINAPI CoUnmarshalHresult(LPSTREAM pStm, HRESULT * phresult) | |
| { | |
| return IStream_Read(pStm, phresult, sizeof(*phresult), NULL); | |
| } |