Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/combase/apartment.c | 8 +- dlls/combase/combase.spec | 15 +- dlls/combase/combase_private.h | 22 +- dlls/combase/marshal.c | 1494 +++++++++++++++++++++++++++- dlls/combase/stubmanager.c | 18 +- dlls/ole32/compobj_private.h | 23 +- dlls/ole32/marshal.c | 1661 +------------------------------- dlls/ole32/ole32.spec | 9 +- dlls/ole32/rpc.c | 8 +- 9 files changed, 1534 insertions(+), 1724 deletions(-)
diff --git a/dlls/combase/apartment.c b/dlls/combase/apartment.c index 6aa053bdd52..bb9e9f44032 100644 --- a/dlls/combase/apartment.c +++ b/dlls/combase/apartment.c @@ -443,8 +443,6 @@ void apartment_freeunusedlibraries(struct apartment *apt, DWORD delay) LeaveCriticalSection(&apt->cs); }
-extern HRESULT WINAPI Internal_apartment_disconnectproxies(struct apartment *apt); - void WINAPI apartment_release(struct apartment *apt) { DWORD refcount; @@ -499,7 +497,7 @@ void WINAPI apartment_release(struct apartment *apt) /* no locking is needed for this apartment, because no other thread * can access it at this point */
- Internal_apartment_disconnectproxies(apt); + apartment_disconnectproxies(apt);
if (apt->win) DestroyWindow(apt->win); if (apt->host_apt_tid) PostThreadMessageW(apt->host_apt_tid, WM_QUIT, 0, 0); @@ -629,7 +627,7 @@ struct apartment * WINAPI apartment_get_current_or_mta(void) }
/* The given OXID must be local to this process */ -struct apartment * WINAPI apartment_findfromoxid(OXID oxid) +struct apartment * apartment_findfromoxid(OXID oxid) { struct apartment *result = NULL; struct list *cursor; @@ -1236,7 +1234,7 @@ static BOOL WINAPI register_class( INIT_ONCE *once, void *param, void **context
/* create a window for the apartment or return the current one if one has * already been created */ -HRESULT WINAPI apartment_createwindowifneeded(struct apartment *apt) +HRESULT apartment_createwindowifneeded(struct apartment *apt) { static INIT_ONCE class_init_once = INIT_ONCE_STATIC_INIT;
diff --git a/dlls/combase/combase.spec b/dlls/combase/combase.spec index 7b165b131ce..f0429343f66 100644 --- a/dlls/combase/combase.spec +++ b/dlls/combase/combase.spec @@ -117,7 +117,7 @@ @ stdcall CoGetObjectContext(ptr ptr) @ stdcall CoGetPSClsid(ptr ptr) @ stub CoGetProcessIdentifier -@ stdcall CoGetStandardMarshal(ptr ptr long ptr long ptr) ole32.CoGetStandardMarshal +@ stdcall CoGetStandardMarshal(ptr ptr long ptr long ptr) @ stub CoGetStdMarshalEx @ stub CoGetSystemSecurityPermissions @ stdcall CoGetTreatAsClass(ptr ptr) @@ -246,7 +246,7 @@ @ stub InternalCoIsSurrogateProcess @ stub InternalCoRegisterDisconnectCallback @ stub InternalCoRegisterSurrogatedObject -@ stub InternalCoStdMarshalObject +@ stdcall InternalCoStdMarshalObject(ptr long ptr ptr) @ stub InternalCoUnregisterDisconnectCallback @ stub InternalCompleteObjRef @ stub InternalCreateCAggId @@ -355,18 +355,7 @@ @ stdcall apartment_release(ptr) @ stdcall enter_apartment(ptr long) @ stdcall leave_apartment(ptr) -@ stdcall apartment_findfromoxid(int64) @ stdcall apartment_getwindow(ptr) -@ stdcall apartment_createwindowifneeded(ptr) @ stdcall stub_manager_int_release(ptr) -@ stdcall get_stub_manager(ptr int64) -@ stdcall stub_manager_is_table_marshaled(ptr ptr) -@ stdcall stub_manager_notify_unmarshal(ptr ptr) -@ stdcall stub_manager_ext_release(ptr long long long) -@ stdcall stub_manager_release_marshal_data(ptr long ptr long) -@ stdcall get_stub_manager_from_object(ptr ptr long) -@ stdcall stub_manager_find_ifstub(ptr ptr long) -@ stdcall stub_manager_ext_addref(ptr long long) -@ stdcall stub_manager_new_ifstub(ptr ptr ptr long ptr long) @ stdcall ipid_get_dispatch_params(ptr ptr ptr ptr ptr ptr ptr) @ stdcall start_apartment_remote_unknown(ptr) diff --git a/dlls/combase/combase_private.h b/dlls/combase/combase_private.h index 6ad3a14b087..0a735c9d473 100644 --- a/dlls/combase/combase_private.h +++ b/dlls/combase/combase_private.h @@ -103,10 +103,11 @@ static inline struct apartment* com_get_current_apt(void) }
HWND WINAPI apartment_getwindow(const struct apartment *apt) DECLSPEC_HIDDEN; -HRESULT WINAPI apartment_createwindowifneeded(struct apartment *apt) DECLSPEC_HIDDEN; +HRESULT apartment_createwindowifneeded(struct apartment *apt) DECLSPEC_HIDDEN; void apartment_freeunusedlibraries(struct apartment *apt, DWORD unload_delay) DECLSPEC_HIDDEN; void apartment_global_cleanup(void) DECLSPEC_HIDDEN; OXID apartment_getoxid(const struct apartment *apt) DECLSPEC_HIDDEN; +HRESULT apartment_disconnectproxies(struct apartment *apt) DECLSPEC_HIDDEN;
/* RpcSs interface */ HRESULT rpcss_get_next_seqid(DWORD *id) DECLSPEC_HIDDEN; @@ -148,7 +149,7 @@ HRESULT apartment_get_local_server_stream(struct apartment *apt, IStream **ret) IUnknown *com_get_registered_class_object(const struct apartment *apartment, REFCLSID rclsid, DWORD clscontext) DECLSPEC_HIDDEN; void apartment_revoke_all_classes(const struct apartment *apt) DECLSPEC_HIDDEN; -struct apartment * WINAPI apartment_findfromoxid(OXID oxid); +struct apartment * apartment_findfromoxid(OXID oxid) DECLSPEC_HIDDEN; struct apartment * apartment_findfromtid(DWORD tid) DECLSPEC_HIDDEN;
/* Stub Manager */ @@ -219,11 +220,14 @@ struct stub_manager };
ULONG WINAPI stub_manager_int_release(struct stub_manager *stub_manager) DECLSPEC_HIDDEN; -struct stub_manager * WINAPI get_stub_manager_from_object(struct apartment *apt, IUnknown *object, BOOL alloc); +struct stub_manager * get_stub_manager_from_object(struct apartment *apt, IUnknown *object, BOOL alloc) DECLSPEC_HIDDEN; void stub_manager_disconnect(struct stub_manager *m) DECLSPEC_HIDDEN; -ULONG WINAPI stub_manager_ext_addref(struct stub_manager *m, ULONG refs, BOOL tableweak) DECLSPEC_HIDDEN; -ULONG WINAPI stub_manager_ext_release(struct stub_manager *m, ULONG refs, BOOL tableweak, BOOL last_unlock_releases) DECLSPEC_HIDDEN; -struct stub_manager * WINAPI get_stub_manager(struct apartment *apt, OID oid); -void WINAPI stub_manager_release_marshal_data(struct stub_manager *m, ULONG refs, const IPID *ipid, BOOL tableweak); -BOOL WINAPI stub_manager_is_table_marshaled(struct stub_manager *m, const IPID *ipid); -BOOL WINAPI stub_manager_notify_unmarshal(struct stub_manager *m, const IPID *ipid); +ULONG stub_manager_ext_addref(struct stub_manager *m, ULONG refs, BOOL tableweak) DECLSPEC_HIDDEN; +ULONG stub_manager_ext_release(struct stub_manager *m, ULONG refs, BOOL tableweak, BOOL last_unlock_releases) DECLSPEC_HIDDEN; +struct stub_manager * get_stub_manager(struct apartment *apt, OID oid) DECLSPEC_HIDDEN; +void stub_manager_release_marshal_data(struct stub_manager *m, ULONG refs, const IPID *ipid, BOOL tableweak) DECLSPEC_HIDDEN; +BOOL stub_manager_is_table_marshaled(struct stub_manager *m, const IPID *ipid) DECLSPEC_HIDDEN; +BOOL stub_manager_notify_unmarshal(struct stub_manager *m, const IPID *ipid) DECLSPEC_HIDDEN; +struct ifstub * stub_manager_find_ifstub(struct stub_manager *m, REFIID iid, MSHLFLAGS flags) DECLSPEC_HIDDEN; +struct ifstub * stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *sb, REFIID iid, DWORD dest_context, + void *dest_context_data, MSHLFLAGS flags) DECLSPEC_HIDDEN; diff --git a/dlls/combase/marshal.c b/dlls/combase/marshal.c index 168ff2f11e7..89e28be4c60 100644 --- a/dlls/combase/marshal.c +++ b/dlls/combase/marshal.c @@ -19,9 +19,12 @@ * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA */
+#include <assert.h> + #define COBJMACROS #include "objbase.h"
+#include "dcom.h" #include "combase_private.h"
#include "wine/debug.h" @@ -29,13 +32,76 @@
WINE_DEFAULT_DEBUG_CHANNEL(ole);
-HRESULT WINAPI unmarshal_object(const STDOBJREF *stdobjref, struct apartment *apt, +HRESULT WINAPI RPC_CreateClientChannel(const OXID *oxid, const IPID *ipid, + const OXID_INFO *oxid_info, const IID *iid, + DWORD dest_context, void *dest_context_data, + IRpcChannelBuffer **chan, struct apartment *apt); +HRESULT WINAPI RPC_RegisterInterface(REFIID riid); +HRESULT WINAPI RPC_ResolveOxid(OXID oxid, OXID_INFO *oxid_info); +void WINAPI RPC_StartRemoting(struct apartment *apt); + +static HRESULT unmarshal_object(const STDOBJREF *stdobjref, struct apartment *apt, MSHCTX dest_context, void *dest_context_data, REFIID riid, const OXID_INFO *oxid_info, void **object);
+/* 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 interface proxy */ +struct ifproxy +{ + struct list entry; /* entry in proxy_manager list (CS parent->cs) */ + struct proxy_manager *parent; /* owning proxy_manager (RO) */ + void *iface; /* interface pointer (RO) */ + STDOBJREF stdobjref; /* marshal data that represents this object (RO) */ + IID iid; /* interface ID (RO) */ + IRpcProxyBuffer *proxy; /* interface proxy (RO) */ + ULONG refs; /* imported (public) references (LOCK) */ + IRpcChannelBuffer *chan; /* channel to object (CS parent->cs) */ +}; + +/* 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); +}
struct ftmarshaler { @@ -794,3 +860,1429 @@ HRESULT WINAPI CoUnmarshalInterface(IStream *stream, REFIID riid, void **ppv)
return hr; } + +/* 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 WINAPI marshal_object(struct 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; + + stdobjref->oxid = apartment_getoxid(apt); + + 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 { + WARN("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)) + WARN("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)) + { + struct apartment *apt = apartment_get_current_or_mta(); + + /* 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, apt, + 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; + } + + apartment_release(apt); + } + + /* 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 +}; + +static HRESULT StdMarshalImpl_Construct(REFIID, DWORD, void*, void**); + +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_GetUnmarshalClass( + IMarshal *iface, REFIID riid, void* pv, DWORD dwDestContext, + void* pvDestContext, DWORD mshlflags, CLSID* pCid) +{ + *pCid = CLSID_StdMarshal; + return S_OK; +} + +static HRESULT WINAPI Proxy_GetMarshalSizeMax( + IMarshal *iface, REFIID riid, void* pv, DWORD dwDestContext, + void* pvDestContext, DWORD mshlflags, DWORD* pSize) +{ + *pSize = FIELD_OFFSET(OBJREF, u_objref.u_standard.saResAddr.aStringArray); + return S_OK; +} + +static void fill_std_objref(OBJREF *objref, const GUID *iid, STDOBJREF *std) +{ + objref->signature = OBJREF_SIGNATURE; + objref->flags = OBJREF_STANDARD; + objref->iid = *iid; + if(std) + objref->u_objref.u_standard.std = *std; + memset(&objref->u_objref.u_standard.saResAddr, 0, + sizeof(objref->u_objref.u_standard.saResAddr)); +} + +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)) + { + OBJREF objref; + + 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)); + fill_std_objref(&objref, riid, &stdobjref); + hr = IStream_Write(pStm, &objref, FIELD_OFFSET(OBJREF, + u_objref.u_standard.saResAddr.aStringArray), 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)) + { + OBJREF objref; + + fill_std_objref(&objref, riid, &qiresults->std); + hr = IStream_Write(pStm, &objref, FIELD_OFFSET(OBJREF, + u_objref.u_standard.saResAddr.aStringArray), 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 HRESULT WINAPI Proxy_UnmarshalInterface( + IMarshal *iface, IStream *pStm, REFIID riid, void **ppv) +{ + struct proxy_manager *This = impl_from_IMarshal( iface ); + IMarshal *marshal; + HRESULT hr; + + TRACE("(%p, %p, %s, %p)\n", This, pStm, wine_dbgstr_guid(riid), ppv); + + hr = StdMarshalImpl_Construct(&IID_IMarshal, This->dest_context, + This->dest_context_data, (void**)&marshal); + if(FAILED(hr)) + return hr; + + hr = IMarshal_UnmarshalInterface(marshal, pStm, riid, ppv); + IMarshal_Release(marshal); + return hr; +} + +static HRESULT WINAPI Proxy_ReleaseMarshalData(IMarshal *iface, IStream *pStm) +{ + struct proxy_manager *This = impl_from_IMarshal( iface ); + IMarshal *marshal; + HRESULT hr; + + TRACE("(%p, %p)\n", This, pStm); + + hr = StdMarshalImpl_Construct(&IID_IMarshal, This->dest_context, + This->dest_context_data, (void**)&marshal); + if(FAILED(hr)) + return hr; + + hr = IMarshal_ReleaseMarshalData(marshal, pStm); + IMarshal_Release(marshal); + return hr; +} + +static HRESULT WINAPI Proxy_DisconnectObject(IMarshal *iface, DWORD dwReserved) +{ + struct proxy_manager *This = impl_from_IMarshal( iface ); + IMarshal *marshal; + HRESULT hr; + + TRACE("(%p, %x)\n", This, dwReserved); + + hr = StdMarshalImpl_Construct(&IID_IMarshal, This->dest_context, + This->dest_context_data, (void**)&marshal); + if(FAILED(hr)) + return hr; + + hr = IMarshal_DisconnectObject(marshal, dwReserved); + IMarshal_Release(marshal); + return hr; +} + +static const IMarshalVtbl ProxyMarshal_Vtbl = +{ + Proxy_QueryInterface, + Proxy_AddRef, + Proxy_Release, + Proxy_GetUnmarshalClass, + Proxy_GetMarshalSizeMax, + Proxy_MarshalInterface, + Proxy_UnmarshalInterface, + Proxy_ReleaseMarshalData, + Proxy_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( + struct 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); + This->cs.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": 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; + + if (!(apt = apartment_get_current_or_mta())) + 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, apt, 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); + apartment_release(apt); + + 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); + + This->cs.DebugInfo->Spare[0] = 0; + 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(struct 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 ****************************/ + +struct stdmarshal +{ + IMarshal IMarshal_iface; + LONG refcount; + DWORD dest_context; + void *dest_context_data; +}; + +static inline struct stdmarshal *impl_from_StdMarshal(IMarshal *iface) +{ + return CONTAINING_RECORD(iface, struct stdmarshal, 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) +{ + struct stdmarshal *marshal = impl_from_StdMarshal(iface); + return InterlockedIncrement(&marshal->refcount); +} + +static ULONG WINAPI StdMarshalImpl_Release(IMarshal *iface) +{ + struct stdmarshal *marshal = impl_from_StdMarshal(iface); + ULONG refcount = InterlockedDecrement(&marshal->refcount); + + if (!refcount) + heap_free(marshal); + + return refcount; +} + +static HRESULT WINAPI StdMarshalImpl_GetUnmarshalClass(IMarshal *iface, REFIID riid, void *pv, + DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, CLSID *pCid) +{ + *pCid = CLSID_StdMarshal; + return S_OK; +} + +static HRESULT WINAPI StdMarshalImpl_GetMarshalSizeMax(IMarshal *iface, REFIID riid, void *pv, + DWORD dwDestContext, void *pvDestContext, DWORD mshlflags, DWORD *pSize) +{ + *pSize = FIELD_OFFSET(OBJREF, u_objref.u_standard.saResAddr.aStringArray); + return S_OK; +} + +static HRESULT WINAPI StdMarshalImpl_MarshalInterface(IMarshal *iface, IStream *stream, REFIID riid, void *pv, + DWORD dest_context, void *dest_context_data, DWORD mshlflags) +{ + ULONG res; + HRESULT hr; + OBJREF objref; + struct apartment *apt; + + TRACE("(...,%s,...)\n", debugstr_guid(riid)); + + if (!(apt = apartment_get_current_or_mta())) + { + ERR("Apartment not initialized\n"); + return CO_E_NOTINITIALIZED; + } + + /* make sure this apartment can be reached from other threads / processes */ + RPC_StartRemoting(apt); + + fill_std_objref(&objref, riid, NULL); + hr = marshal_object(apt, &objref.u_objref.u_standard.std, riid, pv, dest_context, + dest_context_data, mshlflags); + apartment_release(apt); + if (hr != S_OK) + { + ERR("Failed to create ifstub, hr %#x\n", hr); + return hr; + } + + return IStream_Write(stream, &objref, FIELD_OFFSET(OBJREF, u_objref.u_standard.saResAddr.aStringArray), &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, struct 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, riid, + proxy_manager->dest_context, + proxy_manager->dest_context_data, + &chanbuf, apt); + 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 *stream, REFIID riid, void **ppv) +{ + struct stdmarshal *marshal = impl_from_StdMarshal(iface); + OBJREF objref; + HRESULT hr; + ULONG res; + + 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; + } + + if (objref.signature != OBJREF_SIGNATURE) + { + ERR("Bad OBJREF signature 0x%08x\n", objref.signature); + return RPC_E_INVALID_OBJREF; + } + + if (!(objref.flags & OBJREF_STANDARD)) + { + FIXME("unsupported objref.flags = %x\n", objref.flags); + return E_NOTIMPL; + } + + return std_unmarshal_interface(marshal->dest_context, marshal->dest_context_data, stream, riid, ppv); +} + +static HRESULT WINAPI StdMarshalImpl_ReleaseMarshalData(IMarshal *iface, IStream *stream) +{ + OBJREF objref; + HRESULT hr; + ULONG res; + + TRACE("%p, %p\n", iface, stream); + + 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; + } + + if (objref.signature != OBJREF_SIGNATURE) + { + ERR("Bad OBJREF signature 0x%08x\n", objref.signature); + return RPC_E_INVALID_OBJREF; + } + + if (!(objref.flags & OBJREF_STANDARD)) + { + FIXME("unsupported objref.flags = %x\n", objref.flags); + return E_NOTIMPL; + } + + return std_release_marshal_data(stream); +} + +static HRESULT WINAPI StdMarshalImpl_DisconnectObject(IMarshal *iface, DWORD reserved) +{ + 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) +{ + struct stdmarshal *object; + HRESULT hr; + + object = heap_alloc(sizeof(*object)); + if (!object) + return E_OUTOFMEMORY; + + object->IMarshal_iface.lpVtbl = &StdMarshalVtbl; + object->refcount = 1; + object->dest_context = dest_context; + object->dest_context_data = dest_context_data; + + hr = IMarshal_QueryInterface(&object->IMarshal_iface, riid, ppvObject); + IMarshal_Release(&object->IMarshal_iface); + + return hr; +} + +HRESULT WINAPI InternalCoStdMarshalObject(REFIID riid, DWORD dest_context, void *dest_context_data, void **ppvObject) +{ + return StdMarshalImpl_Construct(riid, dest_context, dest_context_data, ppvObject); +} + +/*********************************************************************** + * CoGetStandardMarshal (combase.@) + */ +HRESULT WINAPI CoGetStandardMarshal(REFIID riid, IUnknown *pUnk, DWORD dwDestContext, + void *dest_context, DWORD flags, IMarshal **marshal) +{ + if (pUnk == NULL) + { + FIXME("(%s,NULL,%x,%p,%x,%p), unimplemented yet.\n", debugstr_guid(riid), dwDestContext, + dest_context, flags, marshal); + return E_NOTIMPL; + } + TRACE("%s, %p, %x, %p, %x, %p\n", debugstr_guid(riid), pUnk, dwDestContext, dest_context, flags, marshal); + + return StdMarshalImpl_Construct(&IID_IMarshal, dwDestContext, dest_context, (void **)marshal); +} + diff --git a/dlls/combase/stubmanager.c b/dlls/combase/stubmanager.c index b1070c5683e..a800c0b4844 100644 --- a/dlls/combase/stubmanager.c +++ b/dlls/combase/stubmanager.c @@ -73,7 +73,7 @@ static inline HRESULT generate_ipid(struct stub_manager *m, IPID *ipid) }
/* registers a new interface stub COM object with the stub manager and returns registration record */ -struct ifstub * WINAPI stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *sb, REFIID iid, DWORD dest_context, +struct ifstub * stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *sb, REFIID iid, DWORD dest_context, void *dest_context_data, MSHLFLAGS flags) { struct ifstub *stub; @@ -161,7 +161,7 @@ static struct ifstub *stub_manager_ipid_to_ifstub(struct stub_manager *m, const return result; }
-struct ifstub * WINAPI stub_manager_find_ifstub(struct stub_manager *m, REFIID iid, MSHLFLAGS flags) +struct ifstub * stub_manager_find_ifstub(struct stub_manager *m, REFIID iid, MSHLFLAGS flags) { struct ifstub *result = NULL; struct ifstub *ifstub; @@ -340,7 +340,7 @@ ULONG WINAPI stub_manager_int_release(struct stub_manager *m) /* 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 */ -struct stub_manager * WINAPI get_stub_manager_from_object(struct apartment *apt, IUnknown *obj, BOOL alloc) +struct stub_manager * get_stub_manager_from_object(struct apartment *apt, IUnknown *obj, BOOL alloc) { struct stub_manager *result = NULL; struct list *cursor; @@ -389,7 +389,7 @@ struct stub_manager * WINAPI get_stub_manager_from_object(struct apartment *apt, /* 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 * WINAPI get_stub_manager(struct apartment *apt, OID oid) +struct stub_manager * get_stub_manager(struct apartment *apt, OID oid) { struct stub_manager *result = NULL; struct list *cursor; @@ -417,7 +417,7 @@ struct stub_manager * WINAPI get_stub_manager(struct apartment *apt, OID oid) }
/* add some external references (ie from a client that unmarshaled an ifptr) */ -ULONG WINAPI stub_manager_ext_addref(struct stub_manager *m, ULONG refs, BOOL tableweak) +ULONG stub_manager_ext_addref(struct stub_manager *m, ULONG refs, BOOL tableweak) { BOOL first_extern_ref; ULONG rc; @@ -448,7 +448,7 @@ ULONG WINAPI stub_manager_ext_addref(struct stub_manager *m, ULONG refs, BOOL ta }
/* remove some external references */ -ULONG WINAPI stub_manager_ext_release(struct stub_manager *m, ULONG refs, BOOL tableweak, BOOL last_unlock_releases) +ULONG stub_manager_ext_release(struct stub_manager *m, ULONG refs, BOOL tableweak, BOOL last_unlock_releases) { BOOL last_extern_ref; ULONG rc; @@ -571,7 +571,7 @@ HRESULT WINAPI ipid_get_dispatch_params(const IPID *ipid, struct apartment **stu }
/* returns TRUE if it is possible to unmarshal, FALSE otherwise. */ -BOOL WINAPI stub_manager_notify_unmarshal(struct stub_manager *m, const IPID *ipid) +BOOL stub_manager_notify_unmarshal(struct stub_manager *m, const IPID *ipid) { BOOL ret = TRUE; struct ifstub *ifstub; @@ -602,7 +602,7 @@ BOOL WINAPI stub_manager_notify_unmarshal(struct stub_manager *m, const IPID *ip }
/* handles refcounting for CoReleaseMarshalData */ -void WINAPI stub_manager_release_marshal_data(struct stub_manager *m, ULONG refs, const IPID *ipid, BOOL tableweak) +void stub_manager_release_marshal_data(struct stub_manager *m, ULONG refs, const IPID *ipid, BOOL tableweak) { struct ifstub *ifstub;
@@ -618,7 +618,7 @@ void WINAPI stub_manager_release_marshal_data(struct stub_manager *m, ULONG refs }
/* is an ifstub table marshaled? */ -BOOL WINAPI stub_manager_is_table_marshaled(struct stub_manager *m, const IPID *ipid) +BOOL stub_manager_is_table_marshaled(struct stub_manager *m, const IPID *ipid) { struct ifstub *ifstub = stub_manager_ipid_to_ifstub(m, ipid);
diff --git a/dlls/ole32/compobj_private.h b/dlls/ole32/compobj_private.h index 6f1cf7243e3..12acfc0a9e5 100644 --- a/dlls/ole32/compobj_private.h +++ b/dlls/ole32/compobj_private.h @@ -109,19 +109,6 @@ struct stub_manager BOOL disconnected; /* CoDisconnectObject has been called (CS lock) */ };
-/* imported interface proxy */ -struct ifproxy -{ - struct list entry; /* entry in proxy_manager list (CS parent->cs) */ - struct proxy_manager *parent; /* owning proxy_manager (RO) */ - LPVOID iface; /* interface pointer (RO) */ - STDOBJREF stdobjref; /* marshal data that represents this object (RO) */ - IID iid; /* interface ID (RO) */ - LPRPCPROXYBUFFER proxy; /* interface proxy (RO) */ - ULONG refs; /* imported (public) references (LOCK) */ - IRpcChannelBuffer *chan; /* channel to object (CS parent->cs) */ -}; - struct apartment { struct list entry; @@ -195,35 +182,31 @@ HRESULT FTMarshalCF_Create(REFIID riid, LPVOID *ppv) DECLSPEC_HIDDEN; /* Stub Manager */
extern ULONG WINAPI stub_manager_int_release(struct stub_manager *This) DECLSPEC_HIDDEN; -extern struct stub_manager * WINAPI get_stub_manager(struct apartment *apt, OID oid) DECLSPEC_HIDDEN;
/* RPC Backend */
struct dispatch_params;
-void RPC_StartRemoting(struct apartment *apt) DECLSPEC_HIDDEN; -HRESULT RPC_CreateClientChannel(const OXID *oxid, const IPID *ipid, +HRESULT WINAPI RPC_CreateClientChannel(const OXID *oxid, const IPID *ipid, const OXID_INFO *oxid_info, const IID *iid, DWORD dest_context, void *dest_context_data, IRpcChannelBuffer **chan, struct apartment *apt) DECLSPEC_HIDDEN; -HRESULT RPC_RegisterInterface(REFIID riid) DECLSPEC_HIDDEN; +HRESULT WINAPI RPC_RegisterInterface(REFIID riid) DECLSPEC_HIDDEN; HRESULT RPC_RegisterChannelHook(REFGUID rguid, IChannelHook *hook) DECLSPEC_HIDDEN; void RPC_UnregisterAllChannelHooks(void) DECLSPEC_HIDDEN; -HRESULT RPC_ResolveOxid(OXID oxid, OXID_INFO *oxid_info) DECLSPEC_HIDDEN; +HRESULT WINAPI RPC_ResolveOxid(OXID oxid, OXID_INFO *oxid_info) DECLSPEC_HIDDEN;
/* Drag and drop */ void OLEDD_UnInitialize(void) DECLSPEC_HIDDEN;
/* Apartment Functions */
-extern struct apartment * WINAPI apartment_findfromoxid(OXID oxid) DECLSPEC_HIDDEN; extern void WINAPI apartment_release(struct apartment *apt) DECLSPEC_HIDDEN; static inline HRESULT apartment_getoxid(const struct apartment *apt, OXID *oxid) { *oxid = apt->oxid; return S_OK; } -extern HRESULT WINAPI apartment_createwindowifneeded(struct apartment *apt) DECLSPEC_HIDDEN; extern HWND WINAPI apartment_getwindow(const struct apartment *apt) DECLSPEC_HIDDEN; extern HRESULT WINAPI enter_apartment(struct oletls *info, DWORD model) DECLSPEC_HIDDEN; void WINAPI leave_apartment(struct oletls *info) DECLSPEC_HIDDEN; diff --git a/dlls/ole32/marshal.c b/dlls/ole32/marshal.c index f8b4aecc544..823aea6f721 100644 --- a/dlls/ole32/marshal.c +++ b/dlls/ole32/marshal.c @@ -39,1664 +39,7 @@
WINE_DEFAULT_DEBUG_CHANNEL(ole);
-extern BOOL WINAPI stub_manager_is_table_marshaled(struct stub_manager *m, const IPID *ipid); -extern BOOL WINAPI stub_manager_notify_unmarshal(struct stub_manager *m, const IPID *ipid); -extern ULONG WINAPI stub_manager_ext_release(struct stub_manager *m, ULONG refs, BOOL tableweak, BOOL last_unlock_releases); -extern void WINAPI stub_manager_release_marshal_data(struct stub_manager *m, ULONG refs, const IPID *ipid, BOOL tableweak); -extern struct stub_manager * WINAPI get_stub_manager_from_object(struct apartment *apt, IUnknown *object, BOOL alloc); -extern struct ifstub * WINAPI stub_manager_find_ifstub(struct stub_manager *m, REFIID iid, MSHLFLAGS flags); -extern ULONG WINAPI stub_manager_ext_addref(struct stub_manager *m, ULONG refs, BOOL tableweak); -extern struct ifstub * WINAPI stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *sb, REFIID iid, - DWORD dest_context, void *dest_context_data, MSHLFLAGS flags); - -/* 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); -} - -HRESULT WINAPI unmarshal_object(const STDOBJREF *stdobjref, struct 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 WINAPI marshal_object(struct 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 { - WARN("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)) - WARN("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)) - { - struct apartment *apt = apartment_get_current_or_mta(); - - /* 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, apt, - 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; - } - - apartment_release(apt); - } - - /* 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 -}; - -static HRESULT StdMarshalImpl_Construct(REFIID, DWORD, void*, void**); - -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_GetUnmarshalClass( - IMarshal *iface, REFIID riid, void* pv, DWORD dwDestContext, - void* pvDestContext, DWORD mshlflags, CLSID* pCid) -{ - *pCid = CLSID_StdMarshal; - return S_OK; -} - -static HRESULT WINAPI Proxy_GetMarshalSizeMax( - IMarshal *iface, REFIID riid, void* pv, DWORD dwDestContext, - void* pvDestContext, DWORD mshlflags, DWORD* pSize) -{ - *pSize = FIELD_OFFSET(OBJREF, u_objref.u_standard.saResAddr.aStringArray); - return S_OK; -} - -static void fill_std_objref(OBJREF *objref, const GUID *iid, STDOBJREF *std) -{ - objref->signature = OBJREF_SIGNATURE; - objref->flags = OBJREF_STANDARD; - objref->iid = *iid; - if(std) - objref->u_objref.u_standard.std = *std; - memset(&objref->u_objref.u_standard.saResAddr, 0, - sizeof(objref->u_objref.u_standard.saResAddr)); -} - -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)) - { - OBJREF objref; - - 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)); - fill_std_objref(&objref, riid, &stdobjref); - hr = IStream_Write(pStm, &objref, FIELD_OFFSET(OBJREF, - u_objref.u_standard.saResAddr.aStringArray), 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)) - { - OBJREF objref; - - fill_std_objref(&objref, riid, &qiresults->std); - hr = IStream_Write(pStm, &objref, FIELD_OFFSET(OBJREF, - u_objref.u_standard.saResAddr.aStringArray), 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 HRESULT WINAPI Proxy_UnmarshalInterface( - IMarshal *iface, IStream *pStm, REFIID riid, void **ppv) -{ - struct proxy_manager *This = impl_from_IMarshal( iface ); - IMarshal *marshal; - HRESULT hr; - - TRACE("(%p, %p, %s, %p)\n", This, pStm, wine_dbgstr_guid(riid), ppv); - - hr = StdMarshalImpl_Construct(&IID_IMarshal, This->dest_context, - This->dest_context_data, (void**)&marshal); - if(FAILED(hr)) - return hr; - - hr = IMarshal_UnmarshalInterface(marshal, pStm, riid, ppv); - IMarshal_Release(marshal); - return hr; -} - -static HRESULT WINAPI Proxy_ReleaseMarshalData(IMarshal *iface, IStream *pStm) -{ - struct proxy_manager *This = impl_from_IMarshal( iface ); - IMarshal *marshal; - HRESULT hr; - - TRACE("(%p, %p)\n", This, pStm); - - hr = StdMarshalImpl_Construct(&IID_IMarshal, This->dest_context, - This->dest_context_data, (void**)&marshal); - if(FAILED(hr)) - return hr; - - hr = IMarshal_ReleaseMarshalData(marshal, pStm); - IMarshal_Release(marshal); - return hr; -} - -static HRESULT WINAPI Proxy_DisconnectObject(IMarshal *iface, DWORD dwReserved) -{ - struct proxy_manager *This = impl_from_IMarshal( iface ); - IMarshal *marshal; - HRESULT hr; - - TRACE("(%p, %x)\n", This, dwReserved); - - hr = StdMarshalImpl_Construct(&IID_IMarshal, This->dest_context, - This->dest_context_data, (void**)&marshal); - if(FAILED(hr)) - return hr; - - hr = IMarshal_DisconnectObject(marshal, dwReserved); - IMarshal_Release(marshal); - return hr; -} - -static const IMarshalVtbl ProxyMarshal_Vtbl = -{ - Proxy_QueryInterface, - Proxy_AddRef, - Proxy_Release, - Proxy_GetUnmarshalClass, - Proxy_GetMarshalSizeMax, - Proxy_MarshalInterface, - Proxy_UnmarshalInterface, - Proxy_ReleaseMarshalData, - Proxy_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( - struct 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; - - if (!(apt = apartment_get_current_or_mta())) - 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, apt, 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); - apartment_release(apt); - - 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(struct 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 WINAPI Internal_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_StdMarshal; - return S_OK; -} - -static HRESULT WINAPI -StdMarshalImpl_GetMarshalSizeMax( - IMarshal *iface, REFIID riid, void* pv, DWORD dwDestContext, - void* pvDestContext, DWORD mshlflags, DWORD* pSize) -{ - *pSize = FIELD_OFFSET(OBJREF, u_objref.u_standard.saResAddr.aStringArray); - 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) -{ - ULONG res; - HRESULT hres; - struct apartment *apt; - OBJREF objref; - - TRACE("(...,%s,...)\n", debugstr_guid(riid)); - - if (!(apt = apartment_get_current_or_mta())) - { - ERR("Apartment not initialized\n"); - return CO_E_NOTINITIALIZED; - } - - /* make sure this apartment can be reached from other threads / processes */ - RPC_StartRemoting(apt); - - fill_std_objref(&objref, riid, NULL); - hres = marshal_object(apt, &objref.u_objref.u_standard.std, riid, - pv, dest_context, dest_context_data, mshlflags); - apartment_release(apt); - if (hres != S_OK) - { - ERR("Failed to create ifstub, hres=0x%x\n", hres); - return hres; - } - - return IStream_Write(pStm, &objref, - FIELD_OFFSET(OBJREF, u_objref.u_standard.saResAddr.aStringArray), &res); -} - -/* helper for StdMarshalImpl_UnmarshalInterface - does the unmarshaling with - * no questions asked about the rules surrounding same-apartment unmarshals - * and table marshaling */ -HRESULT WINAPI unmarshal_object(const STDOBJREF *stdobjref, struct 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, riid, - proxy_manager->dest_context, - proxy_manager->dest_context_data, - &chanbuf, apt); - 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 std_unmarshal_interface(MSHCTX dest_context, void *dest_context_data, - IStream *pStm, REFIID riid, void **ppv) -{ - struct stub_manager *stubmgr = NULL; - struct OR_STANDARD obj; - ULONG res; - HRESULT hres; - struct apartment *apt, *stub_apt; - OXID oxid; - - TRACE("(...,%s,....)\n", debugstr_guid(riid)); - - /* we need an apartment to unmarshal into */ - if (!(apt = apartment_get_current_or_mta())) - { - ERR("Apartment not initialized\n"); - return CO_E_NOTINITIALIZED; - } - - /* read STDOBJREF from wire */ - hres = IStream_Read(pStm, &obj, FIELD_OFFSET(struct OR_STANDARD, saResAddr.aStringArray), &res); - if (hres != S_OK) - { - apartment_release(apt); - return STG_E_READFAULT; - } - - hres = apartment_getoxid(apt, &oxid); - if (hres != S_OK) - { - apartment_release(apt); - return hres; - } - - if (obj.saResAddr.wNumEntries) - { - ERR("unsupported size of DUALSTRINGARRAY\n"); - return E_NOTIMPL; - } - - /* check if we're marshalling back to ourselves */ - if ((oxid == obj.std.oxid) && (stubmgr = get_stub_manager(apt, obj.std.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, &obj.std.ipid)) - stub_manager_ext_release(stubmgr, obj.std.cPublicRefs, obj.std.flags & SORFP_TABLEWEAK, FALSE); - - stub_manager_int_release(stubmgr); - apartment_release(apt); - 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(obj.std.oxid))) - { - if ((stubmgr = get_stub_manager(stub_apt, obj.std.oid))) - { - if (!stub_manager_notify_unmarshal(stubmgr, &obj.std.ipid)) - hres = CO_E_OBJNOTCONNECTED; - } - else - { - WARN("Couldn't find object for OXID %s, OID %s, assuming disconnected\n", - wine_dbgstr_longlong(obj.std.oxid), - wine_dbgstr_longlong(obj.std.oid)); - hres = CO_E_OBJNOTCONNECTED; - } - } - else - TRACE("Treating unmarshal from OXID %s as inter-process\n", - wine_dbgstr_longlong(obj.std.oxid)); - - if (hres == S_OK) - hres = unmarshal_object(&obj.std, apt, dest_context, - 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); - - apartment_release(apt); - return hres; -} - -static HRESULT WINAPI -StdMarshalImpl_UnmarshalInterface(IMarshal *iface, IStream *pStm, REFIID riid, void **ppv) -{ - StdMarshalImpl *This = impl_from_StdMarshal(iface); - OBJREF objref; - HRESULT hr; - ULONG res; - - hr = IStream_Read(pStm, &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; - } - - if (objref.signature != OBJREF_SIGNATURE) - { - ERR("Bad OBJREF signature 0x%08x\n", objref.signature); - return RPC_E_INVALID_OBJREF; - } - - if (!(objref.flags & OBJREF_STANDARD)) - { - FIXME("unsupported objref.flags = %x\n", objref.flags); - return E_NOTIMPL; - } - - return std_unmarshal_interface(This->dest_context, - This->dest_context_data, pStm, riid, ppv); -} - -static HRESULT std_release_marshal_data(IStream *pStm) -{ - struct OR_STANDARD obj; - ULONG res; - HRESULT hres; - struct stub_manager *stubmgr; - struct apartment *apt; - - hres = IStream_Read(pStm, &obj, FIELD_OFFSET(struct OR_STANDARD, saResAddr.aStringArray), &res); - if (hres != S_OK) return STG_E_READFAULT; - - if (obj.saResAddr.wNumEntries) - { - ERR("unsupported size of DUALSTRINGARRAY\n"); - return E_NOTIMPL; - } - - TRACE("oxid = %s, oid = %s, ipid = %s\n", - wine_dbgstr_longlong(obj.std.oxid), - wine_dbgstr_longlong(obj.std.oid), - wine_dbgstr_guid(&obj.std.ipid)); - - if (!(apt = apartment_findfromoxid(obj.std.oxid))) - { - WARN("Could not map OXID %s to apartment object\n", - wine_dbgstr_longlong(obj.std.oxid)); - return RPC_E_INVALID_OBJREF; - } - - if (!(stubmgr = get_stub_manager(apt, obj.std.oid))) - { - apartment_release(apt); - ERR("could not map object ID to stub manager, oxid=%s, oid=%s\n", - wine_dbgstr_longlong(obj.std.oxid), wine_dbgstr_longlong(obj.std.oid)); - return RPC_E_INVALID_OBJREF; - } - - stub_manager_release_marshal_data(stubmgr, obj.std.cPublicRefs, &obj.std.ipid, obj.std.flags & SORFP_TABLEWEAK); - - stub_manager_int_release(stubmgr); - apartment_release(apt); - - return S_OK; -} - -static HRESULT WINAPI -StdMarshalImpl_ReleaseMarshalData(IMarshal *iface, IStream *pStm) -{ - OBJREF objref; - HRESULT hr; - ULONG res; - - TRACE("iface=%p, pStm=%p\n", iface, pStm); - - hr = IStream_Read(pStm, &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; - } - - if (objref.signature != OBJREF_SIGNATURE) - { - ERR("Bad OBJREF signature 0x%08x\n", objref.signature); - return RPC_E_INVALID_OBJREF; - } - - if (!(objref.flags & OBJREF_STANDARD)) - { - FIXME("unsupported objref.flags = %x\n", objref.flags); - return E_NOTIMPL; - } - - return std_release_marshal_data(pStm); -} - -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); -} +HRESULT WINAPI InternalCoStdMarshalObject(REFIID riid, DWORD dest_context, void *dest_context_data, void **ppvObject);
static HRESULT WINAPI StdMarshalCF_QueryInterface(LPCLASSFACTORY iface, REFIID riid, LPVOID *ppv) @@ -1724,7 +67,7 @@ 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); + return InternalCoStdMarshalObject(riid, 0, NULL, ppv);
FIXME("(%s), not supported.\n",debugstr_guid(riid)); return E_NOINTERFACE; diff --git a/dlls/ole32/ole32.spec b/dlls/ole32/ole32.spec index ee478c73d2b..1ea57416176 100644 --- a/dlls/ole32/ole32.spec +++ b/dlls/ole32/ole32.spec @@ -43,7 +43,7 @@ @ stdcall CoGetObject(wstr ptr ptr ptr) @ stdcall CoGetObjectContext(ptr ptr) combase.CoGetObjectContext @ stdcall CoGetPSClsid(ptr ptr) combase.CoGetPSClsid -@ stdcall CoGetStandardMarshal(ptr ptr long ptr long ptr) +@ stdcall CoGetStandardMarshal(ptr ptr long ptr long ptr) combase.CoGetStandardMarshal @ stdcall CoGetState(ptr) @ stub CoGetTIDFromIPID @ stdcall CoGetTreatAsClass(ptr ptr) combase.CoGetTreatAsClass @@ -299,9 +299,10 @@ @ stub WriteOleStg @ stub WriteStringStream
-@ stdcall Internal_apartment_disconnectproxies(ptr) @ stdcall Internal_RPC_ExecuteCall(ptr) -@ stdcall marshal_object(ptr ptr ptr ptr long ptr long) -@ stdcall unmarshal_object(ptr ptr long ptr ptr ptr ptr) @ stdcall RPC_CreateServerChannel(long ptr ptr) @ stdcall RPC_UnregisterInterface(ptr long) +@ stdcall RPC_RegisterInterface(ptr) +@ stdcall RPC_ResolveOxid(int64 ptr) +@ stdcall RPC_StartRemoting(ptr) +@ stdcall RPC_CreateClientChannel(ptr ptr ptr ptr long ptr ptr ptr) diff --git a/dlls/ole32/rpc.c b/dlls/ole32/rpc.c index 0aa2ebb41fe..a6783c45eae 100644 --- a/dlls/ole32/rpc.c +++ b/dlls/ole32/rpc.c @@ -1096,7 +1096,7 @@ static const IRpcChannelBufferVtbl ServerRpcChannelBufferVtbl = };
/* returns a channel buffer for proxies */ -HRESULT RPC_CreateClientChannel(const OXID *oxid, const IPID *ipid, +HRESULT WINAPI RPC_CreateClientChannel(const OXID *oxid, const IPID *ipid, const OXID_INFO *oxid_info, const IID *iid, DWORD dest_context, void *dest_context_data, IRpcChannelBuffer **chan, struct apartment *apt) @@ -1531,7 +1531,7 @@ static void __RPC_STUB dispatch_rpc(RPC_MESSAGE *msg) }
/* stub registration */ -HRESULT RPC_RegisterInterface(REFIID riid) +HRESULT WINAPI RPC_RegisterInterface(REFIID riid) { struct registered_if *rif; BOOL found = FALSE; @@ -1610,7 +1610,7 @@ void WINAPI RPC_UnregisterInterface(REFIID riid, BOOL wait)
/* get the info for an OXID, including the IPID for the rem unknown interface * and the string binding */ -HRESULT RPC_ResolveOxid(OXID oxid, OXID_INFO *oxid_info) +HRESULT WINAPI RPC_ResolveOxid(OXID oxid, OXID_INFO *oxid_info) { TRACE("%s\n", wine_dbgstr_longlong(oxid));
@@ -1631,7 +1631,7 @@ HRESULT RPC_ResolveOxid(OXID oxid, OXID_INFO *oxid_info)
/* make the apartment reachable by other threads and processes and create the * IRemUnknown object */ -void RPC_StartRemoting(struct apartment *apt) +void WINAPI RPC_StartRemoting(struct apartment *apt) { if (!InterlockedExchange(&apt->remoting_started, TRUE)) {