Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/combase/Makefile.in | 2 + dlls/combase/apartment.c | 5 +- dlls/combase/combase.c | 84 +++++++++++++++++ dlls/combase/combase.spec | 17 +++- dlls/combase/combase_private.h | 101 ++++++++++++++------ dlls/combase/dcom.idl | 62 +++++++++++++ dlls/{ole32 => combase}/stubmanager.c | 127 ++++++++++++++------------ dlls/ole32/Makefile.in | 1 - dlls/ole32/compobj.c | 121 ------------------------ dlls/ole32/compobj_private.h | 23 +---- dlls/ole32/marshal.c | 12 ++- dlls/ole32/ole32.spec | 9 +- dlls/ole32/rpc.c | 8 +- 13 files changed, 328 insertions(+), 244 deletions(-) create mode 100644 dlls/combase/dcom.idl rename dlls/{ole32 => combase}/stubmanager.c (86%)
diff --git a/dlls/combase/Makefile.in b/dlls/combase/Makefile.in index 45e1ff189cb..ebf86708bbc 100644 --- a/dlls/combase/Makefile.in +++ b/dlls/combase/Makefile.in @@ -14,7 +14,9 @@ C_SRCS = \ roapi.c \ rpc.c \ string.c \ + stubmanager.c \ usrmarshal.c
IDL_SRCS = \ + dcom.idl \ irpcss.idl diff --git a/dlls/combase/apartment.c b/dlls/combase/apartment.c index 12637c8acdb..1637d674222 100644 --- a/dlls/combase/apartment.c +++ b/dlls/combase/apartment.c @@ -444,7 +444,6 @@ void apartment_freeunusedlibraries(struct apartment *apt, DWORD delay) }
extern HRESULT WINAPI Internal_apartment_disconnectproxies(struct apartment *apt); -extern ULONG WINAPI Internal_stub_manager_int_release(struct stub_manager *stubmgr);
void WINAPI apartment_release(struct apartment *apt) { @@ -513,7 +512,7 @@ void WINAPI apartment_release(struct apartment *apt) * stub manager list in the apartment and all non-apartment users * must have a ref on the apartment and so it cannot be destroyed). */ - Internal_stub_manager_int_release(stubmgr); + stub_manager_int_release(stubmgr); }
/* if this assert fires, then another thread took a reference to a @@ -654,7 +653,7 @@ struct apartment * WINAPI apartment_findfromoxid(OXID oxid) /* gets the apartment which has a given creator thread ID. The caller must * release the reference from the apartment as soon as the apartment pointer * is no longer required. */ -struct apartment * WINAPI apartment_findfromtid(DWORD tid) +struct apartment * apartment_findfromtid(DWORD tid) { struct apartment *result = NULL; struct list *cursor; diff --git a/dlls/combase/combase.c b/dlls/combase/combase.c index da91d66cc53..b3e3844a4db 100644 --- a/dlls/combase/combase.c +++ b/dlls/combase/combase.c @@ -2968,6 +2968,90 @@ ULONG WINAPI CoReleaseServerProcess(void) return refs; }
+/****************************************************************************** + * CoDisconnectObject (combase.@) + */ +HRESULT WINAPI CoDisconnectObject(IUnknown *object, DWORD reserved) +{ + struct stub_manager *manager; + struct apartment *apt; + IMarshal *marshal; + HRESULT hr; + + TRACE("%p, %#x\n", object, reserved); + + if (!object) + return E_INVALIDARG; + + hr = IUnknown_QueryInterface(object, &IID_IMarshal, (void **)&marshal); + if (hr == S_OK) + { + hr = IMarshal_DisconnectObject(marshal, reserved); + IMarshal_Release(marshal); + return hr; + } + + if (!(apt = apartment_get_current_or_mta())) + { + ERR("apartment not initialised\n"); + return CO_E_NOTINITIALIZED; + } + + manager = get_stub_manager_from_object(apt, object, FALSE); + if (manager) + { + stub_manager_disconnect(manager); + /* Release stub manager twice, to remove the apartment reference. */ + stub_manager_int_release(manager); + stub_manager_int_release(manager); + } + + /* Note: native is pretty broken here because it just silently + * fails, without returning an appropriate error code if the object was + * not found, making apps think that the object was disconnected, when + * it actually wasn't */ + + apartment_release(apt); + return S_OK; +} + +/****************************************************************************** + * CoLockObjectExternal (combase.@) + */ +HRESULT WINAPI CoLockObjectExternal(IUnknown *object, BOOL lock, BOOL last_unlock_releases) +{ + struct stub_manager *stubmgr; + struct apartment *apt; + + TRACE("%p, %d, %d\n", object, lock, last_unlock_releases); + + if (!(apt = apartment_get_current_or_mta())) + { + ERR("apartment not initialised\n"); + return CO_E_NOTINITIALIZED; + } + + stubmgr = get_stub_manager_from_object(apt, object, lock); + if (!stubmgr) + { + WARN("stub object not found %p\n", object); + /* Note: native is pretty broken here because it just silently + * fails, without returning an appropriate error code, making apps + * think that the object was disconnected, when it actually wasn't */ + apartment_release(apt); + return S_OK; + } + + if (lock) + stub_manager_ext_addref(stubmgr, 1, FALSE); + else + stub_manager_ext_release(stubmgr, 1, FALSE, last_unlock_releases); + + stub_manager_int_release(stubmgr); + apartment_release(apt); + return S_OK; +} + /*********************************************************************** * DllMain (combase.@) */ diff --git a/dlls/combase/combase.spec b/dlls/combase/combase.spec index a888e6deeea..4ac93ff545d 100644 --- a/dlls/combase/combase.spec +++ b/dlls/combase/combase.spec @@ -89,7 +89,7 @@ @ stdcall CoDecrementMTAUsage(ptr) @ stdcall CoDisableCallCancellation(ptr) ole32.CoDisableCallCancellation @ stub CoDisconnectContext -@ stdcall CoDisconnectObject(ptr long) ole32.CoDisconnectObject +@ stdcall CoDisconnectObject(ptr long) @ stdcall CoEnableCallCancellation(ptr) ole32.CoEnableCallCancellation @ stdcall CoFileTimeNow(ptr) @ stdcall CoFreeUnusedLibraries() @@ -128,7 +128,7 @@ @ stdcall CoInitializeWOW(long long) ole32.CoInitializeWOW @ stub CoInvalidateRemoteMachineBindings @ stdcall CoIsHandlerConnected(ptr) ole32.CoIsHandlerConnected -@ stdcall CoLockObjectExternal(ptr long long) ole32.CoLockObjectExternal +@ stdcall CoLockObjectExternal(ptr long long) @ stdcall CoMarshalHresult(ptr long) @ stdcall CoMarshalInterThreadInterfaceInStream(ptr ptr ptr) @ stdcall CoMarshalInterface(ptr ptr ptr long ptr long) @@ -358,4 +358,15 @@ @ stdcall apartment_findfromoxid(int64) @ stdcall apartment_getwindow(ptr) @ stdcall apartment_createwindowifneeded(ptr) -@ stdcall apartment_findfromtid(long) +@ 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 e62204d289c..c6f7e6f071b 100644 --- a/dlls/combase/combase_private.h +++ b/dlls/combase/combase_private.h @@ -113,34 +113,6 @@ HRESULT rpc_get_local_class_object(REFCLSID rclsid, REFIID riid, void **obj) DEC HRESULT rpc_start_local_server(REFCLSID clsid, IStream *stream, BOOL multi_use, void **registration) DECLSPEC_HIDDEN; void rpc_stop_local_server(void *registration) DECLSPEC_HIDDEN;
-/* stub managers hold refs on the object and each interface stub */ -struct stub_manager -{ - struct list entry; /* entry in apartment stubmgr list (CS apt->cs) */ - struct list ifstubs; /* list of active ifstubs for the object (CS lock) */ - CRITICAL_SECTION lock; - struct apartment *apt; /* owning apt (RO) */ - - ULONG extrefs; /* number of 'external' references (CS lock) */ - ULONG refs; /* internal reference count (CS apt->cs) */ - ULONG weakrefs; /* number of weak references (CS lock) */ - OID oid; /* apartment-scoped unique identifier (RO) */ - IUnknown *object; /* the object we are managing the stub for (RO) */ - ULONG next_ipid; /* currently unused (LOCK) */ - OXID_INFO oxid_info; /* string binding, ipid of rem unknown and other information (RO) */ - - IExternalConnection *extern_conn; - - /* We need to keep a count of the outstanding marshals, so we can enforce the - * marshalling rules (ie, you can only unmarshal normal marshals once). Note - * that these counts do NOT include unmarshalled interfaces, once a stream is - * unmarshalled and a proxy set up, this count is decremented. - */ - - ULONG norm_refs; /* refcount of normal marshals (CS lock) */ - BOOL disconnected; /* CoDisconnectObject has been called (CS lock) */ -}; - enum class_reg_data_origin { CLASS_REG_ACTCTX, @@ -175,7 +147,78 @@ 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_findfromtid(DWORD tid) DECLSPEC_HIDDEN;
/* Stub Manager */
-ULONG stub_manager_int_release(struct stub_manager *This) DECLSPEC_HIDDEN; +/* signal to stub manager that this is a rem unknown object */ +#define MSHLFLAGSP_REMUNKNOWN 0x80000000 + +/* Thread-safety Annotation Legend: + * + * RO - The value is read only. It never changes after creation, so no + * locking is required. + * LOCK - The value is written to only using Interlocked* functions. + * CS - The value is read or written to inside a critical section. + * The identifier following "CS" is the specific critical section that + * must be used. + * MUTEX - The value is read or written to with a mutex held. + * The identifier following "MUTEX" is the specific mutex that + * must be used. + */ + +typedef enum ifstub_state +{ + STUBSTATE_NORMAL_MARSHALED, + STUBSTATE_NORMAL_UNMARSHALED, + STUBSTATE_TABLE_WEAK_MARSHALED, + STUBSTATE_TABLE_WEAK_UNMARSHALED, + STUBSTATE_TABLE_STRONG, +} STUB_STATE; + +/* an interface stub */ +struct ifstub +{ + struct list entry; /* entry in stub_manager->ifstubs list (CS stub_manager->lock) */ + IRpcStubBuffer *stubbuffer; /* RO */ + IID iid; /* RO */ + IPID ipid; /* RO */ + IUnknown *iface; /* RO */ + MSHLFLAGS flags; /* so we can enforce process-local marshalling rules (RO) */ + IRpcChannelBuffer*chan; /* channel passed to IRpcStubBuffer::Invoke (RO) */ +}; + +/* stub managers hold refs on the object and each interface stub */ +struct stub_manager +{ + struct list entry; /* entry in apartment stubmgr list (CS apt->cs) */ + struct list ifstubs; /* list of active ifstubs for the object (CS lock) */ + CRITICAL_SECTION lock; + struct apartment *apt; /* owning apt (RO) */ + + ULONG extrefs; /* number of 'external' references (CS lock) */ + ULONG refs; /* internal reference count (CS apt->cs) */ + ULONG weakrefs; /* number of weak references (CS lock) */ + OID oid; /* apartment-scoped unique identifier (RO) */ + IUnknown *object; /* the object we are managing the stub for (RO) */ + ULONG next_ipid; /* currently unused (LOCK) */ + OXID_INFO oxid_info; /* string binding, ipid of rem unknown and other information (RO) */ + + IExternalConnection *extern_conn; + + /* We need to keep a count of the outstanding marshals, so we can enforce the + * marshalling rules (ie, you can only unmarshal normal marshals once). Note + * that these counts do NOT include unmarshalled interfaces, once a stream is + * unmarshalled and a proxy set up, this count is decremented. + */ + + ULONG norm_refs; /* refcount of normal marshals (CS lock) */ + BOOL disconnected; /* CoDisconnectObject has been called (CS lock) */ +}; + +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); +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; diff --git a/dlls/combase/dcom.idl b/dlls/combase/dcom.idl new file mode 100644 index 00000000000..56e3152456c --- /dev/null +++ b/dlls/combase/dcom.idl @@ -0,0 +1,62 @@ +/* + * Copyright 2003 Ove KÃ¥ven, TransGaming Technologies + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Lesser General Public + * License as published by the Free Software Foundation; either + * version 2.1 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Lesser General Public License for more details. + * + * You should have received a copy of the GNU Lesser General Public + * License along with this library; if not, write to the Free Software + * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA + */ + +/* see the official DCOM specification + * (there's a copy at http://www.grimes.demon.co.uk/DCOM/DCOMSpec.htm) */ + +#pragma makedep header + +#include "wine/orpc.idl" + +[ + object, + uuid(00000131-0000-0000-c000-000000000046) +] +interface IRemUnknown : IUnknown +{ + typedef [unique] IRemUnknown *LPREMUNKNOWN; + + typedef struct tagREMQIRESULT + { + HRESULT hResult; + STDOBJREF std; + } REMQIRESULT; + + typedef struct tagREMINTERFACEREF + { + IPID ipid; + unsigned long cPublicRefs; + unsigned long cPrivateRefs; + } REMINTERFACEREF; + + HRESULT RemQueryInterface( + [in] REFIPID ripid, + [in] ULONG cRefs, + [in] unsigned short cIids, + [in, size_is(cIids)] IID *iids, + [out, size_is(,cIids)] REMQIRESULT **ppQIResults); + + HRESULT RemAddRef( + [in] unsigned short cInterfaceRefs, + [in, size_is(cInterfaceRefs)] REMINTERFACEREF *InterfaceRefs, + [out, size_is(cInterfaceRefs)] HRESULT *pResults); + + HRESULT RemRelease( + [in] unsigned short cInterfaceRefs, + [in, size_is(cInterfaceRefs)] REMINTERFACEREF *InterfaceRefs); +} diff --git a/dlls/ole32/stubmanager.c b/dlls/combase/stubmanager.c similarity index 86% rename from dlls/ole32/stubmanager.c rename to dlls/combase/stubmanager.c index d38e22adb47..f5e6497ec6a 100644 --- a/dlls/ole32/stubmanager.c +++ b/dlls/combase/stubmanager.c @@ -38,10 +38,16 @@ #include "wine/debug.h" #include "wine/exception.h"
-#include "compobj_private.h" +#include "initguid.h" +#include "dcom.h" +#include "combase_private.h"
WINE_DEFAULT_DEBUG_CHANNEL(ole);
+extern HRESULT WINAPI marshal_object(struct apartment *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *object, + DWORD dest_context, void *dest_context_data, MSHLFLAGS mshlflags); +extern HRESULT WINAPI RPC_CreateServerChannel(DWORD dest_context, void *dest_context_data, IRpcChannelBuffer **chan); +extern void WINAPI RPC_UnregisterInterface(REFIID riid, BOOL wait);
/* generates an ipid in the following format (similar to native version): * Data1 = apartment-local ipid counter @@ -67,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 *stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *sb, REFIID iid, DWORD dest_context, +struct ifstub * WINAPI stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *sb, REFIID iid, DWORD dest_context, void *dest_context_data, MSHLFLAGS flags) { struct ifstub *stub; @@ -155,7 +161,7 @@ static struct ifstub *stub_manager_ipid_to_ifstub(struct stub_manager *m, const return result; }
-struct ifstub *stub_manager_find_ifstub(struct stub_manager *m, REFIID iid, MSHLFLAGS flags) +struct ifstub * WINAPI stub_manager_find_ifstub(struct stub_manager *m, REFIID iid, MSHLFLAGS flags) { struct ifstub *result = NULL; struct ifstub *ifstub; @@ -182,15 +188,15 @@ static struct stub_manager *new_stub_manager(struct apartment *apt, IUnknown *ob struct stub_manager *sm; HRESULT hres;
- assert( apt ); - + assert(apt); + sm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct stub_manager)); if (!sm) return NULL;
list_init(&sm->ifstubs);
InitializeCriticalSection(&sm->lock); - DEBUG_SET_CRITSEC_NAME(&sm->lock, "stub_manager"); + sm->lock.DebugInfo->Spare[0] = (DWORD_PTR)(__FILE__ ": stub_manager");
IUnknown_AddRef(object); sm->object = object; @@ -287,51 +293,46 @@ static void stub_manager_delete(struct stub_manager *m) } __ENDTRY
- DEBUG_CLEAR_CRITSEC_NAME(&m->lock); + m->lock.DebugInfo->Spare[0] = 0; DeleteCriticalSection(&m->lock);
HeapFree(GetProcessHeap(), 0, m); }
/* increments the internal refcount */ -static ULONG stub_manager_int_addref(struct stub_manager *This) +static ULONG stub_manager_int_addref(struct stub_manager *m) { ULONG refs;
- EnterCriticalSection(&This->apt->cs); - refs = ++This->refs; - LeaveCriticalSection(&This->apt->cs); + EnterCriticalSection(&m->apt->cs); + refs = ++m->refs; + LeaveCriticalSection(&m->apt->cs);
TRACE("before %d\n", refs - 1);
return refs; }
-ULONG WINAPI Internal_stub_manager_int_release(struct stub_manager *m) -{ - return stub_manager_int_release(m); -} - /* decrements the internal refcount */ -ULONG stub_manager_int_release(struct stub_manager *This) +ULONG WINAPI stub_manager_int_release(struct stub_manager *m) { ULONG refs; - struct apartment *apt = This->apt; + struct apartment *apt = m->apt;
EnterCriticalSection(&apt->cs); - refs = --This->refs; + refs = --m->refs;
TRACE("after %d\n", refs);
/* remove from apartment so no other thread can access it... */ if (!refs) - list_remove(&This->entry); + list_remove(&m->entry);
LeaveCriticalSection(&apt->cs);
/* ... so now we can delete it without being inside the apartment critsec */ if (!refs) - stub_manager_delete(This); + stub_manager_delete(m);
return refs; } @@ -339,7 +340,7 @@ ULONG stub_manager_int_release(struct stub_manager *This) /* 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 *get_stub_manager_from_object(struct apartment *apt, IUnknown *obj, BOOL alloc) +struct stub_manager * WINAPI get_stub_manager_from_object(struct apartment *apt, IUnknown *obj, BOOL alloc) { struct stub_manager *result = NULL; struct list *cursor; @@ -347,13 +348,14 @@ struct stub_manager *get_stub_manager_from_object(struct apartment *apt, IUnknow HRESULT hres;
hres = IUnknown_QueryInterface(obj, &IID_IUnknown, (void**)&object); - if (FAILED(hres)) { + if (FAILED(hres)) + { ERR("QueryInterface(IID_IUnknown failed): %08x\n", hres); return NULL; }
EnterCriticalSection(&apt->cs); - LIST_FOR_EACH( cursor, &apt->stubmgrs ) + LIST_FOR_EACH(cursor, &apt->stubmgrs) { struct stub_manager *m = LIST_ENTRY( cursor, struct stub_manager, entry );
@@ -366,12 +368,17 @@ struct stub_manager *get_stub_manager_from_object(struct apartment *apt, IUnknow } LeaveCriticalSection(&apt->cs);
- if (result) { + if (result) + { TRACE("found %p for object %p\n", result, object); - }else if (alloc) { + } + else if (alloc) + { TRACE("not found, creating new stub manager...\n"); result = new_stub_manager(apt, object); - }else { + } + else + { TRACE("not found for object %p\n", object); }
@@ -382,15 +389,15 @@ struct stub_manager *get_stub_manager_from_object(struct apartment *apt, IUnknow /* gets the stub manager associated with an object id - caller must have * a reference to the apartment while a reference to the stub manager is held. * it must also call release on the stub manager when it is no longer needed */ -struct stub_manager *get_stub_manager(struct apartment *apt, OID oid) +struct stub_manager * WINAPI get_stub_manager(struct apartment *apt, OID oid) { struct stub_manager *result = NULL; struct list *cursor;
EnterCriticalSection(&apt->cs); - LIST_FOR_EACH( cursor, &apt->stubmgrs ) + LIST_FOR_EACH(cursor, &apt->stubmgrs) { - struct stub_manager *m = LIST_ENTRY( cursor, struct stub_manager, entry ); + struct stub_manager *m = LIST_ENTRY(cursor, struct stub_manager, entry);
if (m->oid == oid) { @@ -410,7 +417,7 @@ struct stub_manager *get_stub_manager(struct apartment *apt, OID oid) }
/* add some external references (ie from a client that unmarshaled an ifptr) */ -ULONG stub_manager_ext_addref(struct stub_manager *m, ULONG refs, BOOL tableweak) +ULONG WINAPI stub_manager_ext_addref(struct stub_manager *m, ULONG refs, BOOL tableweak) { BOOL first_extern_ref; ULONG rc; @@ -427,7 +434,7 @@ ULONG stub_manager_ext_addref(struct stub_manager *m, ULONG refs, BOOL tableweak rc += ++m->weakrefs;
LeaveCriticalSection(&m->lock); - + TRACE("added %u refs to %p (oid %s), rc is now %u\n", refs, m, wine_dbgstr_longlong(m->oid), rc);
/* @@ -441,7 +448,7 @@ ULONG stub_manager_ext_addref(struct stub_manager *m, ULONG refs, BOOL tableweak }
/* remove some external references */ -ULONG stub_manager_ext_release(struct stub_manager *m, ULONG refs, BOOL tableweak, BOOL last_unlock_releases) +ULONG WINAPI stub_manager_ext_release(struct stub_manager *m, ULONG refs, BOOL tableweak, BOOL last_unlock_releases) { BOOL last_extern_ref; ULONG rc; @@ -460,7 +467,7 @@ ULONG stub_manager_ext_release(struct stub_manager *m, ULONG refs, BOOL tablewea last_extern_ref = refs && !m->extrefs;
LeaveCriticalSection(&m->lock); - + TRACE("removed %u refs from %p (oid %s), rc is now %u\n", refs, m, wine_dbgstr_longlong(m->oid), rc);
if (last_extern_ref && m->extern_conn) @@ -482,9 +489,9 @@ static struct stub_manager *get_stub_manager_from_ipid(struct apartment *apt, co struct list *cursor;
EnterCriticalSection(&apt->cs); - LIST_FOR_EACH( cursor, &apt->stubmgrs ) + LIST_FOR_EACH(cursor, &apt->stubmgrs) { - struct stub_manager *m = LIST_ENTRY( cursor, struct stub_manager, entry ); + struct stub_manager *m = LIST_ENTRY(cursor, struct stub_manager, entry);
if ((*ifstub = stub_manager_ipid_to_ifstub(m, ipid))) { @@ -535,7 +542,7 @@ static HRESULT ipid_to_stub_manager(const IPID *ipid, struct apartment **stub_ap /* gets the apartment, stub and channel of an object. the caller must * release the references to all objects (except iface) if the function * returned success, otherwise no references are returned. */ -HRESULT ipid_get_dispatch_params(const IPID *ipid, struct apartment **stub_apt, +HRESULT WINAPI ipid_get_dispatch_params(const IPID *ipid, struct apartment **stub_apt, struct stub_manager **manager, IRpcStubBuffer **stub, IRpcChannelBuffer **chan, IID *iid, IUnknown **iface) @@ -564,7 +571,7 @@ HRESULT ipid_get_dispatch_params(const IPID *ipid, struct apartment **stub_apt, }
/* returns TRUE if it is possible to unmarshal, FALSE otherwise. */ -BOOL stub_manager_notify_unmarshal(struct stub_manager *m, const IPID *ipid) +BOOL WINAPI stub_manager_notify_unmarshal(struct stub_manager *m, const IPID *ipid) { BOOL ret = TRUE; struct ifstub *ifstub; @@ -595,7 +602,7 @@ BOOL stub_manager_notify_unmarshal(struct stub_manager *m, const IPID *ipid) }
/* handles refcounting for CoReleaseMarshalData */ -void stub_manager_release_marshal_data(struct stub_manager *m, ULONG refs, const IPID *ipid, BOOL tableweak) +void WINAPI stub_manager_release_marshal_data(struct stub_manager *m, ULONG refs, const IPID *ipid, BOOL tableweak) { struct ifstub *ifstub;
@@ -611,16 +618,15 @@ void stub_manager_release_marshal_data(struct stub_manager *m, ULONG refs, const }
/* is an ifstub table marshaled? */ -BOOL stub_manager_is_table_marshaled(struct stub_manager *m, const IPID *ipid) +BOOL WINAPI stub_manager_is_table_marshaled(struct stub_manager *m, const IPID *ipid) { struct ifstub *ifstub = stub_manager_ipid_to_ifstub(m, ipid);
- assert( ifstub ); - + assert(ifstub); + return ifstub->flags & (MSHLFLAGS_TABLESTRONG | MSHLFLAGS_TABLEWEAK); }
- /***************************************************************************** * * IRemUnknown implementation @@ -643,24 +649,24 @@ static inline RemUnknown *impl_from_IRemUnknown(IRemUnknown *iface) return CONTAINING_RECORD(iface, RemUnknown, IRemUnknown_iface); }
- /* construct an IRemUnknown object with one outstanding reference */ static HRESULT RemUnknown_Construct(IRemUnknown **ppRemUnknown) { - RemUnknown *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); + RemUnknown *object = HeapAlloc(GetProcessHeap(), 0, sizeof(*object));
- if (!This) return E_OUTOFMEMORY; + if (!object) + return E_OUTOFMEMORY;
- This->IRemUnknown_iface.lpVtbl = &RemUnknown_Vtbl; - This->refs = 1; + object->IRemUnknown_iface.lpVtbl = &RemUnknown_Vtbl; + object->refs = 1;
- *ppRemUnknown = &This->IRemUnknown_iface; + *ppRemUnknown = &object->IRemUnknown_iface; return S_OK; }
static HRESULT WINAPI RemUnknown_QueryInterface(IRemUnknown *iface, REFIID riid, void **ppv) { - TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv); + TRACE("%p, %s, %p\n", iface, debugstr_guid(riid), ppv);
if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_IRemUnknown)) @@ -680,9 +686,9 @@ static HRESULT WINAPI RemUnknown_QueryInterface(IRemUnknown *iface, REFIID riid, static ULONG WINAPI RemUnknown_AddRef(IRemUnknown *iface) { ULONG refs; - RemUnknown *This = impl_from_IRemUnknown(iface); + RemUnknown *remunk = impl_from_IRemUnknown(iface);
- refs = InterlockedIncrement(&This->refs); + refs = InterlockedIncrement(&remunk->refs);
TRACE("%p before: %d\n", iface, refs-1); return refs; @@ -691,11 +697,11 @@ static ULONG WINAPI RemUnknown_AddRef(IRemUnknown *iface) static ULONG WINAPI RemUnknown_Release(IRemUnknown *iface) { ULONG refs; - RemUnknown *This = impl_from_IRemUnknown(iface); + RemUnknown *remunk = impl_from_IRemUnknown(iface);
- refs = InterlockedDecrement(&This->refs); + refs = InterlockedDecrement(&remunk->refs); if (!refs) - HeapFree(GetProcessHeap(), 0, This); + HeapFree(GetProcessHeap(), 0, remunk);
TRACE("%p after: %d\n", iface, refs); return refs; @@ -714,7 +720,7 @@ static HRESULT WINAPI RemUnknown_RemQueryInterface(IRemUnknown *iface, DWORD dest_context; void *dest_context_data;
- TRACE("(%p)->(%s, %d, %d, %p, %p)\n", iface, debugstr_guid(ripid), cRefs, cIids, iids, ppQIResults); + TRACE("%p, %s, %d, %d, %p, %p\n", iface, debugstr_guid(ripid), cRefs, cIids, iids, ppQIResults);
hr = ipid_to_ifstub(ripid, &apt, &stubmgr, &ifstub); if (hr != S_OK) return hr; @@ -751,7 +757,7 @@ static HRESULT WINAPI RemUnknown_RemAddRef(IRemUnknown *iface, HRESULT hr = S_OK; USHORT i;
- TRACE("(%p)->(%d, %p, %p)\n", iface, cInterfaceRefs, InterfaceRefs, pResults); + TRACE("%p, %d, %p, %p\n", iface, cInterfaceRefs, InterfaceRefs, pResults);
for (i = 0; i < cInterfaceRefs; i++) { @@ -783,7 +789,7 @@ static HRESULT WINAPI RemUnknown_RemRelease(IRemUnknown *iface, HRESULT hr = S_OK; USHORT i;
- TRACE("(%p)->(%d, %p)\n", iface, cInterfaceRefs, InterfaceRefs); + TRACE("%p, %d, %p\n", iface, cInterfaceRefs, InterfaceRefs);
for (i = 0; i < cInterfaceRefs; i++) { @@ -820,7 +826,7 @@ static const IRemUnknownVtbl RemUnknown_Vtbl = };
/* starts the IRemUnknown listener for the current apartment */ -HRESULT start_apartment_remote_unknown(struct apartment *apt) +HRESULT WINAPI start_apartment_remote_unknown(struct apartment *apt) { IRemUnknown *pRemUnknown; HRESULT hr = S_OK; @@ -834,7 +840,8 @@ HRESULT start_apartment_remote_unknown(struct apartment *apt) { STDOBJREF stdobjref; /* dummy - not used */ /* register it with the stub manager */ - hr = marshal_object(apt, &stdobjref, &IID_IRemUnknown, (IUnknown *)pRemUnknown, MSHCTX_DIFFERENTMACHINE, NULL, MSHLFLAGS_NORMAL|MSHLFLAGSP_REMUNKNOWN); + hr = marshal_object(apt, &stdobjref, &IID_IRemUnknown, (IUnknown *)pRemUnknown, + MSHCTX_DIFFERENTMACHINE, NULL, MSHLFLAGS_NORMAL|MSHLFLAGSP_REMUNKNOWN); /* release our reference to the object as the stub manager will manage the life cycle for us */ IRemUnknown_Release(pRemUnknown); if (hr == S_OK) diff --git a/dlls/ole32/Makefile.in b/dlls/ole32/Makefile.in index 1c1e28fa4c5..ddbdfab30de 100644 --- a/dlls/ole32/Makefile.in +++ b/dlls/ole32/Makefile.in @@ -37,7 +37,6 @@ C_SRCS = \ stg_prop.c \ stg_stream.c \ storage32.c \ - stubmanager.c \ usrmarshal.c
RC_SRCS = ole32res.rc diff --git a/dlls/ole32/compobj.c b/dlls/ole32/compobj.c index 39ccc86b352..b92dfd87267 100644 --- a/dlls/ole32/compobj.c +++ b/dlls/ole32/compobj.c @@ -529,69 +529,6 @@ HRESULT WINAPI CoInitialize(LPVOID lpReserved) return CoInitializeEx(lpReserved, COINIT_APARTMENTTHREADED); }
-/****************************************************************************** - * CoDisconnectObject [OLE32.@] - * - * Disconnects all connections to this object from remote processes. Dispatches - * pending RPCs while blocking new RPCs from occurring, and then calls - * IMarshal::DisconnectObject on the given object. - * - * Typically called when the object server is forced to shut down, for instance by - * the user. - * - * PARAMS - * lpUnk [I] The object whose stub should be disconnected. - * reserved [I] Reserved. Should be set to 0. - * - * RETURNS - * Success: S_OK. - * Failure: HRESULT code. - * - * SEE ALSO - * CoMarshalInterface, CoReleaseMarshalData, CoLockObjectExternal - */ -HRESULT WINAPI CoDisconnectObject( LPUNKNOWN lpUnk, DWORD reserved ) -{ - struct stub_manager *manager; - HRESULT hr; - IMarshal *marshal; - struct apartment *apt; - - TRACE("(%p, 0x%08x)\n", lpUnk, reserved); - - if (!lpUnk) return E_INVALIDARG; - - hr = IUnknown_QueryInterface(lpUnk, &IID_IMarshal, (void **)&marshal); - if (hr == S_OK) - { - hr = IMarshal_DisconnectObject(marshal, reserved); - IMarshal_Release(marshal); - return hr; - } - - if (!(apt = apartment_get_current_or_mta())) - { - ERR("apartment not initialised\n"); - return CO_E_NOTINITIALIZED; - } - - manager = get_stub_manager_from_object(apt, lpUnk, FALSE); - if (manager) { - stub_manager_disconnect(manager); - /* Release stub manager twice, to remove the apartment reference. */ - stub_manager_int_release(manager); - stub_manager_int_release(manager); - } - - /* Note: native is pretty broken here because it just silently - * fails, without returning an appropriate error code if the object was - * not found, making apps think that the object was disconnected, when - * it actually wasn't */ - - apartment_release(apt); - return S_OK; -} - /* open HKCR\CLSID\{string form of clsid}\{keyname} key */ HRESULT COM_OpenKeyForCLSID(REFCLSID clsid, LPCWSTR keyname, REGSAM access, HKEY *subkey) { @@ -698,64 +635,6 @@ void WINAPI CoFreeAllLibraries(void) /* NOP */ }
-/****************************************************************************** - * CoLockObjectExternal [OLE32.@] - * - * Increments or decrements the external reference count of a stub object. - * - * PARAMS - * pUnk [I] Stub object. - * fLock [I] If TRUE then increments the external ref-count, - * otherwise decrements. - * fLastUnlockReleases [I] If TRUE then the last unlock has the effect of - * calling CoDisconnectObject. - * - * RETURNS - * Success: S_OK. - * Failure: HRESULT code. - * - * NOTES - * If fLock is TRUE and an object is passed in that doesn't have a stub - * manager then a new stub manager is created for the object. - */ -HRESULT WINAPI CoLockObjectExternal( - LPUNKNOWN pUnk, - BOOL fLock, - BOOL fLastUnlockReleases) -{ - struct stub_manager *stubmgr; - struct apartment *apt; - - TRACE("pUnk=%p, fLock=%s, fLastUnlockReleases=%s\n", - pUnk, fLock ? "TRUE" : "FALSE", fLastUnlockReleases ? "TRUE" : "FALSE"); - - if (!(apt = apartment_get_current_or_mta())) - { - ERR("apartment not initialised\n"); - return CO_E_NOTINITIALIZED; - } - - stubmgr = get_stub_manager_from_object(apt, pUnk, fLock); - if (!stubmgr) - { - WARN("stub object not found %p\n", pUnk); - /* Note: native is pretty broken here because it just silently - * fails, without returning an appropriate error code, making apps - * think that the object was disconnected, when it actually wasn't */ - apartment_release(apt); - return S_OK; - } - - if (fLock) - stub_manager_ext_addref(stubmgr, 1, FALSE); - else - stub_manager_ext_release(stubmgr, 1, FALSE, fLastUnlockReleases); - - stub_manager_int_release(stubmgr); - apartment_release(apt); - return S_OK; -} - /*********************************************************************** * CoInitializeWOW (OLE32.@) * diff --git a/dlls/ole32/compobj_private.h b/dlls/ole32/compobj_private.h index 953574423aa..6f1cf7243e3 100644 --- a/dlls/ole32/compobj_private.h +++ b/dlls/ole32/compobj_private.h @@ -194,24 +194,8 @@ HRESULT FTMarshalCF_Create(REFIID riid, LPVOID *ppv) DECLSPEC_HIDDEN;
/* Stub Manager */
-ULONG stub_manager_int_release(struct stub_manager *This) DECLSPEC_HIDDEN; -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 ifstub *stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *sb, REFIID iid, - DWORD dest_context, void *dest_context_data, MSHLFLAGS flags) DECLSPEC_HIDDEN; -struct ifstub *stub_manager_find_ifstub(struct stub_manager *m, REFIID iid, MSHLFLAGS flags) DECLSPEC_HIDDEN; -struct stub_manager *get_stub_manager(struct apartment *apt, OID oid) DECLSPEC_HIDDEN; -struct stub_manager *get_stub_manager_from_object(struct apartment *apt, IUnknown *object, BOOL alloc) DECLSPEC_HIDDEN; -BOOL stub_manager_notify_unmarshal(struct stub_manager *m, const IPID *ipid) DECLSPEC_HIDDEN; -BOOL stub_manager_is_table_marshaled(struct stub_manager *m, const IPID *ipid) DECLSPEC_HIDDEN; -void stub_manager_release_marshal_data(struct stub_manager *m, ULONG refs, const IPID *ipid, BOOL tableweak) DECLSPEC_HIDDEN; -void stub_manager_disconnect(struct stub_manager *m) DECLSPEC_HIDDEN; -HRESULT ipid_get_dispatch_params(const IPID *ipid, struct apartment **stub_apt, struct stub_manager **manager, IRpcStubBuffer **stub, - IRpcChannelBuffer **chan, IID *iid, IUnknown **iface) DECLSPEC_HIDDEN; -HRESULT start_apartment_remote_unknown(struct apartment *apt) DECLSPEC_HIDDEN; - -HRESULT marshal_object(struct apartment *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *obj, DWORD dest_context, - void *dest_context_data, MSHLFLAGS mshlflags) DECLSPEC_HIDDEN; +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 */
@@ -222,9 +206,7 @@ HRESULT 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_CreateServerChannel(DWORD dest_context, void *dest_context_data, IRpcChannelBuffer **chan) DECLSPEC_HIDDEN; HRESULT RPC_RegisterInterface(REFIID riid) DECLSPEC_HIDDEN; -void RPC_UnregisterInterface(REFIID riid, BOOL wait) 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; @@ -235,7 +217,6 @@ void OLEDD_UnInitialize(void) DECLSPEC_HIDDEN; /* Apartment Functions */
extern struct apartment * WINAPI apartment_findfromoxid(OXID oxid) DECLSPEC_HIDDEN; -extern struct apartment * WINAPI apartment_findfromtid(DWORD tid) DECLSPEC_HIDDEN; extern void WINAPI apartment_release(struct apartment *apt) DECLSPEC_HIDDEN; static inline HRESULT apartment_getoxid(const struct apartment *apt, OXID *oxid) { diff --git a/dlls/ole32/marshal.c b/dlls/ole32/marshal.c index d08144f619a..87017dbc922 100644 --- a/dlls/ole32/marshal.c +++ b/dlls/ole32/marshal.c @@ -39,6 +39,16 @@
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
@@ -118,7 +128,7 @@ static inline HRESULT get_facbuf_for_iid(REFIID riid, IPSFactoryBuffer **facbuf) }
/* marshals an object into a STDOBJREF structure */ -HRESULT marshal_object(struct apartment *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *object, +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; diff --git a/dlls/ole32/ole32.spec b/dlls/ole32/ole32.spec index ca22e4c8f1a..972629ecb70 100644 --- a/dlls/ole32/ole32.spec +++ b/dlls/ole32/ole32.spec @@ -16,7 +16,7 @@ @ stdcall CoCreateInstanceEx(ptr ptr long ptr long ptr) combase.CoCreateInstanceEx @ stdcall CoDecrementMTAUsage(ptr) combase.CoDecrementMTAUsage @ stdcall CoDisableCallCancellation(ptr) -@ stdcall CoDisconnectObject(ptr long) +@ stdcall CoDisconnectObject(ptr long) combase.CoDisconnectObject @ stdcall CoDosDateTimeToFileTime(long long ptr) kernel32.DosDateTimeToFileTime @ stdcall CoEnableCallCancellation(ptr) @ stdcall CoFileTimeNow(ptr) combase.CoFileTimeNow @@ -56,7 +56,7 @@ @ stdcall CoIsHandlerConnected(ptr) @ stdcall CoIsOle1Class (ptr) @ stdcall CoLoadLibrary(wstr long) -@ stdcall CoLockObjectExternal(ptr long long) +@ stdcall CoLockObjectExternal(ptr long long) combase.CoLockObjectExternal @ stdcall CoMarshalHresult(ptr long) combase.CoMarshalHresult @ stdcall CoMarshalInterThreadInterfaceInStream(ptr ptr ptr) combase.CoMarshalInterThreadInterfaceInStream @ stdcall CoMarshalInterface(ptr ptr ptr long ptr long) combase.CoMarshalInterface @@ -298,6 +298,9 @@ @ stdcall WriteFmtUserTypeStg(ptr long ptr) @ stub WriteOleStg @ stub WriteStringStream + @ stdcall Internal_apartment_disconnectproxies(ptr) @ stdcall Internal_RPC_ExecuteCall(ptr) -@ stdcall Internal_stub_manager_int_release(ptr) +@ stdcall marshal_object(ptr ptr ptr ptr long ptr long) +@ stdcall RPC_CreateServerChannel(long ptr ptr) +@ stdcall RPC_UnregisterInterface(ptr long) diff --git a/dlls/ole32/rpc.c b/dlls/ole32/rpc.c index 84d26c70c6c..0aa2ebb41fe 100644 --- a/dlls/ole32/rpc.c +++ b/dlls/ole32/rpc.c @@ -43,6 +43,10 @@
WINE_DEFAULT_DEBUG_CHANNEL(ole);
+extern HRESULT WINAPI ipid_get_dispatch_params(const IPID *ipid, struct apartment **stub_apt, + struct stub_manager **manager, IRpcStubBuffer **stub, IRpcChannelBuffer **chan, IID *iid, IUnknown **iface); +extern HRESULT WINAPI start_apartment_remote_unknown(struct apartment *apt); + static void __RPC_STUB dispatch_rpc(RPC_MESSAGE *msg);
/* we only use one function to dispatch calls for all methods - we use the @@ -1159,7 +1163,7 @@ HRESULT RPC_CreateClientChannel(const OXID *oxid, const IPID *ipid, return S_OK; }
-HRESULT RPC_CreateServerChannel(DWORD dest_context, void *dest_context_data, IRpcChannelBuffer **chan) +HRESULT WINAPI RPC_CreateServerChannel(DWORD dest_context, void *dest_context_data, IRpcChannelBuffer **chan) { RpcChannelBuffer *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); if (!This) @@ -1584,7 +1588,7 @@ HRESULT RPC_RegisterInterface(REFIID riid) }
/* stub unregistration */ -void RPC_UnregisterInterface(REFIID riid, BOOL wait) +void WINAPI RPC_UnregisterInterface(REFIID riid, BOOL wait) { struct registered_if *rif; EnterCriticalSection(&csRegIf);
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/combase/combase.spec | 2 +- dlls/combase/combase_private.h | 2 + dlls/combase/marshal.c | 141 +++++++++++++++++++++++++++++++++ dlls/combase/stubmanager.c | 4 +- dlls/ole32/marshal.c | 50 ------------ dlls/ole32/ole32.spec | 2 +- 6 files changed, 147 insertions(+), 54 deletions(-)
diff --git a/dlls/combase/combase.spec b/dlls/combase/combase.spec index 4ac93ff545d..e1ffdc82539 100644 --- a/dlls/combase/combase.spec +++ b/dlls/combase/combase.spec @@ -146,7 +146,7 @@ @ stdcall CoRegisterPSClsid(ptr ptr) @ stdcall CoRegisterSurrogate(ptr) ole32.CoRegisterSurrogate @ stdcall CoRegisterSurrogateEx(ptr ptr) ole32.CoRegisterSurrogateEx -@ stdcall CoReleaseMarshalData(ptr) ole32.CoReleaseMarshalData +@ stdcall CoReleaseMarshalData(ptr) @ stdcall CoReleaseServerProcess() @ stdcall CoResumeClassObjects() ole32.CoResumeClassObjects @ stub CoRetireServer diff --git a/dlls/combase/combase_private.h b/dlls/combase/combase_private.h index c6f7e6f071b..39be51a3614 100644 --- a/dlls/combase/combase_private.h +++ b/dlls/combase/combase_private.h @@ -222,3 +222,5 @@ struct stub_manager * WINAPI get_stub_manager_from_object(struct apartment *apt, 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); diff --git a/dlls/combase/marshal.c b/dlls/combase/marshal.c index db5089f013e..612404d9587 100644 --- a/dlls/combase/marshal.c +++ b/dlls/combase/marshal.c @@ -29,6 +29,9 @@
WINE_DEFAULT_DEBUG_CHANNEL(ole);
+/* private flag indicating that the object was marshaled as table-weak */ +#define SORFP_TABLEWEAK SORF_OXRES1 + struct ftmarshaler { IUnknown IUnknown_inner; @@ -507,3 +510,141 @@ cleanup:
return hr; } + +/* Creates an IMarshal* object according to the data marshaled to the stream. + * The function leaves the stream pointer at the start of the data written + * to the stream by the IMarshal* object. + */ +static HRESULT get_unmarshaler_from_stream(IStream *stream, IMarshal **marshal, IID *iid) +{ + OBJREF objref; + HRESULT hr; + ULONG res; + + /* read common OBJREF header */ + hr = IStream_Read(stream, &objref, FIELD_OFFSET(OBJREF, u_objref), &res); + if (hr != S_OK || (res != FIELD_OFFSET(OBJREF, u_objref))) + { + ERR("Failed to read common OBJREF header, 0x%08x\n", hr); + return STG_E_READFAULT; + } + + /* sanity check on header */ + if (objref.signature != OBJREF_SIGNATURE) + { + ERR("Bad OBJREF signature 0x%08x\n", objref.signature); + return RPC_E_INVALID_OBJREF; + } + + if (iid) *iid = objref.iid; + + /* FIXME: handler marshaling */ + if (objref.flags & OBJREF_STANDARD) + { + TRACE("Using standard unmarshaling\n"); + *marshal = NULL; + return S_FALSE; + } + else if (objref.flags & OBJREF_CUSTOM) + { + ULONG custom_header_size = FIELD_OFFSET(OBJREF, u_objref.u_custom.pData) - + FIELD_OFFSET(OBJREF, u_objref.u_custom); + TRACE("Using custom unmarshaling\n"); + /* read constant sized OR_CUSTOM data from stream */ + hr = IStream_Read(stream, &objref.u_objref.u_custom, + custom_header_size, &res); + if (hr != S_OK || (res != custom_header_size)) + { + ERR("Failed to read OR_CUSTOM header, 0x%08x\n", hr); + return STG_E_READFAULT; + } + /* now create the marshaler specified in the stream */ + hr = CoCreateInstance(&objref.u_objref.u_custom.clsid, NULL, + CLSCTX_INPROC_SERVER, &IID_IMarshal, + (LPVOID*)marshal); + } + else + { + FIXME("Invalid or unimplemented marshaling type specified: %x\n", + objref.flags); + return RPC_E_INVALID_OBJREF; + } + + if (hr != S_OK) + ERR("Failed to create marshal, 0x%08x\n", hr); + + return hr; +} + +static HRESULT std_release_marshal_data(IStream *stream) +{ + struct stub_manager *stubmgr; + struct OR_STANDARD obj; + struct apartment *apt; + ULONG res; + HRESULT hr; + + hr = IStream_Read(stream, &obj, FIELD_OFFSET(struct OR_STANDARD, saResAddr.aStringArray), &res); + if (hr != 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; +} + +/*********************************************************************** + * CoReleaseMarshalData (combase.@) + */ +HRESULT WINAPI CoReleaseMarshalData(IStream *stream) +{ + IMarshal *marshal; + HRESULT hr; + + TRACE("%p\n", stream); + + hr = get_unmarshaler_from_stream(stream, &marshal, NULL); + if (hr == S_FALSE) + { + hr = std_release_marshal_data(stream); + if (hr != S_OK) + ERR("StdMarshal ReleaseMarshalData failed with error %#x\n", hr); + return hr; + } + if (hr != S_OK) + return hr; + + /* call the helper object to do the releasing of marshal data */ + hr = IMarshal_ReleaseMarshalData(marshal, stream); + if (hr != S_OK) + ERR("IMarshal::ReleaseMarshalData failed with error %#x\n", hr); + + IMarshal_Release(marshal); + return hr; +} diff --git a/dlls/combase/stubmanager.c b/dlls/combase/stubmanager.c index f5e6497ec6a..b1070c5683e 100644 --- a/dlls/combase/stubmanager.c +++ b/dlls/combase/stubmanager.c @@ -605,10 +605,10 @@ BOOL WINAPI stub_manager_notify_unmarshal(struct stub_manager *m, const IPID *ip void WINAPI stub_manager_release_marshal_data(struct stub_manager *m, ULONG refs, const IPID *ipid, BOOL tableweak) { struct ifstub *ifstub; - + if (!(ifstub = stub_manager_ipid_to_ifstub(m, ipid))) return; - + if (ifstub->flags & MSHLFLAGS_TABLEWEAK) refs = 0; else if (ifstub->flags & MSHLFLAGS_TABLESTRONG) diff --git a/dlls/ole32/marshal.c b/dlls/ole32/marshal.c index 87017dbc922..e462c9ffd48 100644 --- a/dlls/ole32/marshal.c +++ b/dlls/ole32/marshal.c @@ -1837,56 +1837,6 @@ HRESULT WINAPI CoUnmarshalInterface(IStream *pStream, REFIID riid, LPVOID *ppv) return hr; }
-/*********************************************************************** - * CoReleaseMarshalData [OLE32.@] - * - * Releases resources associated with an object that has been marshaled into - * a stream. - * - * PARAMS - * - * pStream [I] The stream that the object has been marshaled into. - * - * RETURNS - * Success: S_OK. - * Failure: HRESULT error code. - * - * NOTES - * - * Call this function to release resources associated with a normal or - * table-weak marshal that will not be unmarshaled, and all table-strong - * marshals when they are no longer needed. - * - * SEE ALSO - * CoMarshalInterface(), CoUnmarshalInterface(). - */ -HRESULT WINAPI CoReleaseMarshalData(IStream *pStream) -{ - HRESULT hr; - LPMARSHAL pMarshal; - - TRACE("(%p)\n", pStream); - - hr = get_unmarshaler_from_stream(pStream, &pMarshal, NULL); - if (hr == S_FALSE) - { - hr = std_release_marshal_data(pStream); - if (hr != S_OK) - ERR("StdMarshal ReleaseMarshalData failed with error 0x%08x\n", hr); - return hr; - } - if (hr != S_OK) - return hr; - - /* call the helper object to do the releasing of marshal data */ - hr = IMarshal_ReleaseMarshalData(pMarshal, pStream); - if (hr != S_OK) - ERR("IMarshal::ReleaseMarshalData failed with error 0x%08x\n", hr); - - IMarshal_Release(pMarshal); - return hr; -} - static HRESULT WINAPI StdMarshalCF_QueryInterface(LPCLASSFACTORY iface, REFIID riid, LPVOID *ppv) { diff --git a/dlls/ole32/ole32.spec b/dlls/ole32/ole32.spec index 972629ecb70..2ecfcf6b510 100644 --- a/dlls/ole32/ole32.spec +++ b/dlls/ole32/ole32.spec @@ -72,7 +72,7 @@ @ stdcall CoRegisterPSClsid(ptr ptr) combase.CoRegisterPSClsid @ stdcall CoRegisterSurrogate(ptr) @ stdcall CoRegisterSurrogateEx(ptr ptr) -@ stdcall CoReleaseMarshalData(ptr) +@ stdcall CoReleaseMarshalData(ptr) combase.CoReleaseMarshalData @ stdcall CoReleaseServerProcess() combase.CoReleaseServerProcess @ stdcall CoResumeClassObjects() @ stdcall CoRevertToSelf() combase.CoRevertToSelf
On Tue, Sep 01, 2020 at 04:27:07PM +0300, Nikolay Sivov wrote:
--- a/dlls/combase/stubmanager.c +++ b/dlls/combase/stubmanager.c @@ -605,10 +605,10 @@ BOOL WINAPI stub_manager_notify_unmarshal(struct stub_manager *m, const IPID *ip void WINAPI stub_manager_release_marshal_data(struct stub_manager *m, ULONG refs, const IPID *ipid, BOOL tableweak) { struct ifstub *ifstub;
- if (!(ifstub = stub_manager_ipid_to_ifstub(m, ipid))) return;
I've fixed-up a few white-space issues and sent in a v2.
Huw.
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/combase/apartment.c | 5 ++ dlls/combase/combase.spec | 2 +- dlls/combase/combase_private.h | 3 + dlls/combase/marshal.c | 146 +++++++++++++++++++++++++++++++++ dlls/ole32/marshal.c | 143 +------------------------------- dlls/ole32/ole32.spec | 3 +- 6 files changed, 159 insertions(+), 143 deletions(-)
diff --git a/dlls/combase/apartment.c b/dlls/combase/apartment.c index 1637d674222..6aa053bdd52 100644 --- a/dlls/combase/apartment.c +++ b/dlls/combase/apartment.c @@ -1270,6 +1270,11 @@ HWND WINAPI apartment_getwindow(const struct apartment *apt) return apt->win; }
+OXID apartment_getoxid(const struct apartment *apt) +{ + return apt->oxid; +} + void apartment_global_cleanup(void) { if (apt_win_class) diff --git a/dlls/combase/combase.spec b/dlls/combase/combase.spec index e1ffdc82539..7b165b131ce 100644 --- a/dlls/combase/combase.spec +++ b/dlls/combase/combase.spec @@ -166,7 +166,7 @@ @ stdcall CoUninitialize() @ stub CoUnloadingWOW @ stdcall CoUnmarshalHresult(ptr ptr) -@ stdcall CoUnmarshalInterface(ptr ptr ptr) ole32.CoUnmarshalInterface +@ stdcall CoUnmarshalInterface(ptr ptr ptr) @ stub CoVrfCheckThreadState @ stub CoVrfGetThreadState @ stub CoVrfReleaseThreadState diff --git a/dlls/combase/combase_private.h b/dlls/combase/combase_private.h index 39be51a3614..6ad3a14b087 100644 --- a/dlls/combase/combase_private.h +++ b/dlls/combase/combase_private.h @@ -106,6 +106,7 @@ HWND WINAPI apartment_getwindow(const struct apartment *apt) DECLSPEC_HIDDEN; HRESULT WINAPI 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;
/* RpcSs interface */ HRESULT rpcss_get_next_seqid(DWORD *id) DECLSPEC_HIDDEN; @@ -224,3 +225,5 @@ ULONG WINAPI stub_manager_ext_addref(struct stub_manager *m, ULONG refs, BOOL ta 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); diff --git a/dlls/combase/marshal.c b/dlls/combase/marshal.c index 612404d9587..168ff2f11e7 100644 --- a/dlls/combase/marshal.c +++ b/dlls/combase/marshal.c @@ -29,6 +29,11 @@
WINE_DEFAULT_DEBUG_CHANNEL(ole);
+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); + /* private flag indicating that the object was marshaled as table-weak */ #define SORFP_TABLEWEAK SORF_OXRES1
@@ -648,3 +653,144 @@ HRESULT WINAPI CoReleaseMarshalData(IStream *stream) IMarshal_Release(marshal); return hr; } + +static HRESULT std_unmarshal_interface(MSHCTX dest_context, void *dest_context_data, + IStream *stream, REFIID riid, void **ppv) +{ + struct stub_manager *stubmgr = NULL; + struct OR_STANDARD obj; + ULONG res; + HRESULT hres; + struct apartment *apt, *stub_apt; + + 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(stream, &obj, FIELD_OFFSET(struct OR_STANDARD, saResAddr.aStringArray), &res); + if (hres != S_OK) + { + apartment_release(apt); + return STG_E_READFAULT; + } + + if (obj.saResAddr.wNumEntries) + { + ERR("unsupported size of DUALSTRINGARRAY\n"); + return E_NOTIMPL; + } + + /* check if we're marshalling back to ourselves */ + if ((apartment_getoxid(apt) == 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; +} + +/*********************************************************************** + * CoUnmarshalInterface (combase.@) + */ +HRESULT WINAPI CoUnmarshalInterface(IStream *stream, REFIID riid, void **ppv) +{ + IMarshal *marshal; + IUnknown *object; + HRESULT hr; + IID iid; + + TRACE("%p, %s, %p\n", stream, debugstr_guid(riid), ppv); + + if (!stream || !ppv) + return E_INVALIDARG; + + hr = get_unmarshaler_from_stream(stream, &marshal, &iid); + if (hr == S_FALSE) + { + hr = std_unmarshal_interface(0, NULL, stream, &iid, (void **)&object); + if (hr != S_OK) + ERR("StdMarshal UnmarshalInterface failed, hr %#x\n", hr); + } + else if (hr == S_OK) + { + /* call the helper object to do the actual unmarshaling */ + hr = IMarshal_UnmarshalInterface(marshal, stream, &iid, (void **)&object); + IMarshal_Release(marshal); + if (hr != S_OK) + ERR("IMarshal::UnmarshalInterface failed, hr %#x\n", hr); + } + + if (hr == S_OK) + { + /* IID_NULL means use the interface ID of the marshaled object */ + if (!IsEqualIID(riid, &IID_NULL) && !IsEqualIID(riid, &iid)) + { + TRACE("requested interface != marshalled interface, additional QI needed\n"); + hr = IUnknown_QueryInterface(object, riid, ppv); + if (hr != S_OK) + ERR("Couldn't query for interface %s, hr %#x\n", debugstr_guid(riid), hr); + IUnknown_Release(object); + } + else + { + *ppv = object; + } + } + + TRACE("completed with hr 0x%x\n", hr); + + return hr; +} diff --git a/dlls/ole32/marshal.c b/dlls/ole32/marshal.c index e462c9ffd48..f8b4aecc544 100644 --- a/dlls/ole32/marshal.c +++ b/dlls/ole32/marshal.c @@ -95,7 +95,7 @@ static inline struct proxy_manager *impl_from_IClientSecurity( IClientSecurity * return CONTAINING_RECORD(iface, struct proxy_manager, IClientSecurity_iface); }
-static HRESULT unmarshal_object(const STDOBJREF *stdobjref, struct apartment *apt, +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); @@ -1347,7 +1347,7 @@ StdMarshalImpl_MarshalInterface( /* 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, +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) @@ -1698,145 +1698,6 @@ HRESULT WINAPI CoGetStandardMarshal(REFIID riid, IUnknown *pUnk, return StdMarshalImpl_Construct(&IID_IMarshal, dwDestContext, pvDestContext, (void**)ppMarshal); }
-/*********************************************************************** - * get_unmarshaler_from_stream [internal] - * - * Creates an IMarshal* object according to the data marshaled to the stream. - * The function leaves the stream pointer at the start of the data written - * to the stream by the IMarshal* object. - */ -static HRESULT get_unmarshaler_from_stream(IStream *stream, IMarshal **marshal, IID *iid) -{ - HRESULT hr; - ULONG res; - OBJREF objref; - - /* read common OBJREF header */ - hr = IStream_Read(stream, &objref, FIELD_OFFSET(OBJREF, u_objref), &res); - if (hr != S_OK || (res != FIELD_OFFSET(OBJREF, u_objref))) - { - ERR("Failed to read common OBJREF header, 0x%08x\n", hr); - return STG_E_READFAULT; - } - - /* sanity check on header */ - if (objref.signature != OBJREF_SIGNATURE) - { - ERR("Bad OBJREF signature 0x%08x\n", objref.signature); - return RPC_E_INVALID_OBJREF; - } - - if (iid) *iid = objref.iid; - - /* FIXME: handler marshaling */ - if (objref.flags & OBJREF_STANDARD) - { - TRACE("Using standard unmarshaling\n"); - *marshal = NULL; - return S_FALSE; - } - else if (objref.flags & OBJREF_CUSTOM) - { - ULONG custom_header_size = FIELD_OFFSET(OBJREF, u_objref.u_custom.pData) - - FIELD_OFFSET(OBJREF, u_objref.u_custom); - TRACE("Using custom unmarshaling\n"); - /* read constant sized OR_CUSTOM data from stream */ - hr = IStream_Read(stream, &objref.u_objref.u_custom, - custom_header_size, &res); - if (hr != S_OK || (res != custom_header_size)) - { - ERR("Failed to read OR_CUSTOM header, 0x%08x\n", hr); - return STG_E_READFAULT; - } - /* now create the marshaler specified in the stream */ - hr = CoCreateInstance(&objref.u_objref.u_custom.clsid, NULL, - CLSCTX_INPROC_SERVER, &IID_IMarshal, - (LPVOID*)marshal); - } - else - { - FIXME("Invalid or unimplemented marshaling type specified: %x\n", - objref.flags); - return RPC_E_INVALID_OBJREF; - } - - if (hr != S_OK) - ERR("Failed to create marshal, 0x%08x\n", hr); - - return hr; -} - -/*********************************************************************** - * CoUnmarshalInterface [OLE32.@] - * - * Unmarshals an object from a stream by creating a proxy to the remote - * object, if necessary. - * - * PARAMS - * - * pStream [I] Stream containing the marshaled object. - * riid [I] Interface identifier of the object to create a proxy to. - * ppv [O] Address where proxy will be stored. - * - * RETURNS - * - * Success: S_OK. - * Failure: HRESULT code. - * - * SEE ALSO - * CoMarshalInterface(). - */ -HRESULT WINAPI CoUnmarshalInterface(IStream *pStream, REFIID riid, LPVOID *ppv) -{ - HRESULT hr; - LPMARSHAL pMarshal; - IID iid; - IUnknown *object; - - TRACE("(%p, %s, %p)\n", pStream, debugstr_guid(riid), ppv); - - if (!pStream || !ppv) - return E_INVALIDARG; - - hr = get_unmarshaler_from_stream(pStream, &pMarshal, &iid); - if (hr == S_FALSE) - { - hr = std_unmarshal_interface(0, NULL, pStream, &iid, (void**)&object); - if (hr != S_OK) - ERR("StdMarshal UnmarshalInterface failed, 0x%08x\n", hr); - } - else if (hr == S_OK) - { - /* call the helper object to do the actual unmarshaling */ - hr = IMarshal_UnmarshalInterface(pMarshal, pStream, &iid, (LPVOID*)&object); - IMarshal_Release(pMarshal); - if (hr != S_OK) - ERR("IMarshal::UnmarshalInterface failed, 0x%08x\n", hr); - } - - if (hr == S_OK) - { - /* IID_NULL means use the interface ID of the marshaled object */ - if (!IsEqualIID(riid, &IID_NULL) && !IsEqualIID(riid, &iid)) - { - TRACE("requested interface != marshalled interface, additional QI needed\n"); - hr = IUnknown_QueryInterface(object, riid, ppv); - if (hr != S_OK) - ERR("Couldn't query for interface %s, hr = 0x%08x\n", - debugstr_guid(riid), hr); - IUnknown_Release(object); - } - else - { - *ppv = object; - } - } - - TRACE("completed with hr 0x%x\n", hr); - - return hr; -} - static HRESULT WINAPI StdMarshalCF_QueryInterface(LPCLASSFACTORY iface, REFIID riid, LPVOID *ppv) { diff --git a/dlls/ole32/ole32.spec b/dlls/ole32/ole32.spec index 2ecfcf6b510..ee478c73d2b 100644 --- a/dlls/ole32/ole32.spec +++ b/dlls/ole32/ole32.spec @@ -90,7 +90,7 @@ @ stdcall CoUninitialize() combase.CoUninitialize @ stub CoUnloadingWOW @ stdcall CoUnmarshalHresult(ptr ptr) combase.CoUnmarshalHresult -@ stdcall CoUnmarshalInterface(ptr ptr ptr) +@ stdcall CoUnmarshalInterface(ptr ptr ptr) combase.CoUnmarshalInterface @ stdcall CoWaitForMultipleHandles(long long long ptr ptr) combase.CoWaitForMultipleHandles @ stdcall CreateAntiMoniker(ptr) @ stdcall CreateBindCtx(long ptr) @@ -302,5 +302,6 @@ @ 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)
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=77847
Your paranoid android.
=== debiant (32 bit Chinese:China report) ===
ole32: clipboard.c:1687: Test failed: got 800401d0 clipboard.c:1703: Test failed: got 800401d0 clipboard.c:1706: Test failed: called 0 clipboard.c:1707: Test failed: called 0 clipboard.c:1715: Test failed: got 80040064 clipboard.c:1718: Test failed: called 0 clipboard.c:1719: Test failed: called 0 clipboard.c:1725: Test failed: got 80040064 clipboard.c:1728: Test failed: called 0 clipboard.c:1729: Test failed: called 0 clipboard.c:1735: Test failed: got 80040064 clipboard.c:1738: Test failed: called 0 clipboard.c:1739: Test failed: called 0 clipboard.c:1745: Test failed: got 80040064 clipboard.c:1748: Test failed: called 0 clipboard.c:1749: Test failed: called 0 clipboard.c:1757: Test failed: got 80040064 clipboard.c:1760: Test failed: called 0 clipboard.c:1761: Test failed: called 0 clipboard.c:1767: Test failed: got 80040064 clipboard.c:1770: Test failed: called 0 clipboard.c:1771: Test failed: called 0 clipboard.c:1777: Test failed: got 80040064 clipboard.c:1780: Test failed: called 0 clipboard.c:1781: Test failed: called 0 clipboard.c:1789: Test failed: got 80040064 clipboard.c:1792: Test failed: called 0 clipboard.c:1793: Test failed: called 0 clipboard.c:1799: Test failed: got 80040064 clipboard.c:1802: Test failed: called 0 clipboard.c:1803: Test failed: called 0 clipboard.c:1809: Test failed: got 80040064 clipboard.c:1812: Test failed: called 0 clipboard.c:1813: Test failed: called 0
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)) {
Signed-off-by: Nikolay Sivov nsivov@codeweavers.com --- dlls/combase/apartment.c | 13 +- dlls/combase/combase.c | 11 + dlls/combase/combase.spec | 10 +- dlls/combase/combase_private.h | 31 +- dlls/combase/marshal.c | 24 +- dlls/combase/rpc.c | 1615 +++++++++++++++++++++++++++++++ dlls/combase/stubmanager.c | 22 +- dlls/ole32/Makefile.in | 1 - dlls/ole32/compobj.c | 21 - dlls/ole32/compobj_private.h | 146 +-- dlls/ole32/ole32.spec | 10 +- dlls/ole32/rpc.c | 1654 -------------------------------- 12 files changed, 1676 insertions(+), 1882 deletions(-) delete mode 100644 dlls/ole32/rpc.c
diff --git a/dlls/combase/apartment.c b/dlls/combase/apartment.c index bb9e9f44032..125eaff1429 100644 --- a/dlls/combase/apartment.c +++ b/dlls/combase/apartment.c @@ -443,7 +443,7 @@ void apartment_freeunusedlibraries(struct apartment *apt, DWORD delay) LeaveCriticalSection(&apt->cs); }
-void WINAPI apartment_release(struct apartment *apt) +void apartment_release(struct apartment *apt) { DWORD refcount;
@@ -615,7 +615,7 @@ struct apartment * apartment_get_mta(void)
/* Return the current apartment if it exists, or, failing that, the MTA. Caller * must free the returned apartment in either case. */ -struct apartment * WINAPI apartment_get_current_or_mta(void) +struct apartment * apartment_get_current_or_mta(void) { struct apartment *apt = com_get_current_apt(); if (apt) @@ -1098,14 +1098,13 @@ static HRESULT apartment_hostobject(struct apartment *apt, const struct host_obj }
struct dispatch_params; -extern void WINAPI Internal_RPC_ExecuteCall(struct dispatch_params *params);
static LRESULT CALLBACK apartment_wndproc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam) { switch (msg) { case DM_EXECUTERPC: - Internal_RPC_ExecuteCall((struct dispatch_params *)lParam); + rpc_execute_call((struct dispatch_params *)lParam); return 0; case DM_HOSTOBJECT: return apartment_hostobject(com_get_current_apt(), (const struct host_object_params *)lParam); @@ -1119,7 +1118,7 @@ static BOOL apartment_is_model(const struct apartment *apt, DWORD model) return (apt->multi_threaded == !(model & COINIT_APARTMENTTHREADED)); }
-HRESULT WINAPI enter_apartment(struct tlsdata *data, DWORD model) +HRESULT enter_apartment(struct tlsdata *data, DWORD model) { HRESULT hr = S_OK;
@@ -1143,7 +1142,7 @@ HRESULT WINAPI enter_apartment(struct tlsdata *data, DWORD model) return hr; }
-void WINAPI leave_apartment(struct tlsdata *data) +void leave_apartment(struct tlsdata *data) { if (!--data->inits) { @@ -1262,7 +1261,7 @@ HRESULT apartment_createwindowifneeded(struct apartment *apt) }
/* retrieves the window for the main- or apartment-threaded apartment */ -HWND WINAPI apartment_getwindow(const struct apartment *apt) +HWND apartment_getwindow(const struct apartment *apt) { assert(!apt->multi_threaded); return apt->win; diff --git a/dlls/combase/combase.c b/dlls/combase/combase.c index b3e3844a4db..d959034b57f 100644 --- a/dlls/combase/combase.c +++ b/dlls/combase/combase.c @@ -3052,6 +3052,16 @@ HRESULT WINAPI CoLockObjectExternal(IUnknown *object, BOOL lock, BOOL last_unloc return S_OK; }
+/*********************************************************************** + * CoRegisterChannelHook (combase.@) + */ +HRESULT WINAPI CoRegisterChannelHook(REFGUID guidExtension, IChannelHook *channel_hook) +{ + TRACE("%s, %p\n", debugstr_guid(guidExtension), channel_hook); + + return rpc_register_channel_hook(guidExtension, channel_hook); +} + /*********************************************************************** * DllMain (combase.@) */ @@ -3068,6 +3078,7 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD reason, LPVOID reserved) if (reserved) break; apartment_global_cleanup(); DeleteCriticalSection(®istered_classes_cs); + rpc_unregister_channel_hooks(); break; case DLL_THREAD_DETACH: com_cleanup_tlsdata(); diff --git a/dlls/combase/combase.spec b/dlls/combase/combase.spec index f0429343f66..3a09ce064b0 100644 --- a/dlls/combase/combase.spec +++ b/dlls/combase/combase.spec @@ -139,6 +139,7 @@ @ stdcall CoQueryProxyBlanket(ptr ptr ptr ptr ptr ptr ptr ptr) @ stub CoReactivateObject @ stub CoRegisterActivationFilter +@ stdcall CoRegisterChannelHook(ptr ptr) @ stdcall CoRegisterClassObject(ptr ptr long long ptr) @ stdcall CoRegisterInitializeSpy(ptr ptr) @ stdcall CoRegisterMallocSpy(ptr) @@ -350,12 +351,3 @@ @ stdcall WindowsSubstringWithSpecifiedLength(ptr long long ptr) @ stdcall WindowsTrimStringEnd(ptr ptr ptr) @ stdcall WindowsTrimStringStart(ptr ptr ptr) - -@ stdcall apartment_get_current_or_mta() -@ stdcall apartment_release(ptr) -@ stdcall enter_apartment(ptr long) -@ stdcall leave_apartment(ptr) -@ stdcall apartment_getwindow(ptr) -@ stdcall stub_manager_int_release(ptr) -@ 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 0a735c9d473..0aa87f79a07 100644 --- a/dlls/combase/combase_private.h +++ b/dlls/combase/combase_private.h @@ -102,7 +102,7 @@ static inline struct apartment* com_get_current_apt(void) return tlsdata->apt; }
-HWND WINAPI apartment_getwindow(const struct apartment *apt) DECLSPEC_HIDDEN; +HWND apartment_getwindow(const 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; @@ -114,6 +114,18 @@ HRESULT rpcss_get_next_seqid(DWORD *id) DECLSPEC_HIDDEN; HRESULT rpc_get_local_class_object(REFCLSID rclsid, REFIID riid, void **obj) DECLSPEC_HIDDEN; HRESULT rpc_start_local_server(REFCLSID clsid, IStream *stream, BOOL multi_use, void **registration) DECLSPEC_HIDDEN; void rpc_stop_local_server(void *registration) DECLSPEC_HIDDEN; +HRESULT rpc_create_clientchannel(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_create_serverchannel(DWORD dest_context, void *dest_context_data, IRpcChannelBuffer **chan) DECLSPEC_HIDDEN; +HRESULT rpc_register_interface(REFIID riid) DECLSPEC_HIDDEN; +void rpc_unregister_interface(REFIID riid, BOOL wait) DECLSPEC_HIDDEN; +HRESULT rpc_resolve_oxid(OXID oxid, OXID_INFO *oxid_info) DECLSPEC_HIDDEN; +void rpc_start_remoting(struct apartment *apt) DECLSPEC_HIDDEN; +HRESULT rpc_register_channel_hook(REFGUID rguid, IChannelHook *hook) DECLSPEC_HIDDEN; +void rpc_unregister_channel_hooks(void) DECLSPEC_HIDDEN; + +struct dispatch_params; +void rpc_execute_call(struct dispatch_params *params);
enum class_reg_data_origin { @@ -136,10 +148,10 @@ struct class_reg_data } u; };
-HRESULT WINAPI enter_apartment(struct tlsdata *data, DWORD model); -void WINAPI leave_apartment(struct tlsdata *data); -void WINAPI apartment_release(struct apartment *apt); -struct apartment * WINAPI apartment_get_current_or_mta(void); +HRESULT enter_apartment(struct tlsdata *data, DWORD model) DECLSPEC_HIDDEN; +void leave_apartment(struct tlsdata *data) DECLSPEC_HIDDEN; +void apartment_release(struct apartment *apt) DECLSPEC_HIDDEN; +struct apartment * apartment_get_current_or_mta(void) DECLSPEC_HIDDEN; HRESULT apartment_increment_mta_usage(CO_MTA_USAGE_COOKIE *cookie) DECLSPEC_HIDDEN; void apartment_decrement_mta_usage(CO_MTA_USAGE_COOKIE cookie) DECLSPEC_HIDDEN; struct apartment * apartment_get_mta(void) DECLSPEC_HIDDEN; @@ -152,6 +164,9 @@ void apartment_revoke_all_classes(const struct apartment *apt) DECLSPEC_HIDDEN; struct apartment * apartment_findfromoxid(OXID oxid) DECLSPEC_HIDDEN; struct apartment * apartment_findfromtid(DWORD tid) DECLSPEC_HIDDEN;
+HRESULT marshal_object(struct apartment *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *object, + DWORD dest_context, void *dest_context_data, MSHLFLAGS mshlflags) DECLSPEC_HIDDEN; + /* Stub Manager */
/* signal to stub manager that this is a rem unknown object */ @@ -219,7 +234,7 @@ struct stub_manager BOOL disconnected; /* CoDisconnectObject has been called (CS lock) */ };
-ULONG WINAPI stub_manager_int_release(struct stub_manager *stub_manager) DECLSPEC_HIDDEN; +ULONG stub_manager_int_release(struct stub_manager *stub_manager) DECLSPEC_HIDDEN; 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 stub_manager_ext_addref(struct stub_manager *m, ULONG refs, BOOL tableweak) DECLSPEC_HIDDEN; @@ -231,3 +246,7 @@ BOOL stub_manager_notify_unmarshal(struct stub_manager *m, const IPID *ipid) DEC 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; +HRESULT ipid_get_dispatch_params(const IPID *ipid, struct apartment **stub_apt, + struct stub_manager **manager, IRpcStubBuffer **stub, IRpcChannelBuffer **chan, + IID *iid, IUnknown **iface) DECLSPEC_HIDDEN; +HRESULT start_apartment_remote_unknown(struct apartment *apt) DECLSPEC_HIDDEN; diff --git a/dlls/combase/marshal.c b/dlls/combase/marshal.c index 89e28be4c60..ae971edbb1e 100644 --- a/dlls/combase/marshal.c +++ b/dlls/combase/marshal.c @@ -36,9 +36,6 @@ 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, @@ -889,7 +886,7 @@ static inline HRESULT get_facbuf_for_iid(REFIID riid, IPSFactoryBuffer **facbuf) }
/* marshals an object into a STDOBJREF structure */ -HRESULT WINAPI marshal_object(struct apartment *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *object, +HRESULT marshal_object(struct apartment *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *object, DWORD dest_context, void *dest_context_data, MSHLFLAGS mshlflags) { struct stub_manager *manager; @@ -971,7 +968,7 @@ HRESULT WINAPI marshal_object(struct apartment *apt, STDOBJREF *stdobjref, REFII }
/* FIXME: check return value */ - RPC_RegisterInterface(riid); + rpc_register_interface(riid);
stdobjref->ipid = ifstub->ipid;
@@ -979,8 +976,6 @@ HRESULT WINAPI marshal_object(struct apartment *apt, STDOBJREF *stdobjref, REFII return S_OK; }
- - /* Client-side identity of the server object */
static HRESULT proxy_manager_get_remunknown(struct proxy_manager * This, IRemUnknown **remunk); @@ -1583,7 +1578,7 @@ static HRESULT proxy_manager_construct( } else { - HRESULT hr = RPC_ResolveOxid(oxid, &This->oxid_info); + HRESULT hr = rpc_resolve_oxid(oxid, &This->oxid_info); if (FAILED(hr)) { CloseHandle(This->remoting_mutex); @@ -2081,7 +2076,7 @@ static HRESULT WINAPI StdMarshalImpl_MarshalInterface(IMarshal *iface, IStream * }
/* make sure this apartment can be reached from other threads / processes */ - RPC_StartRemoting(apt); + rpc_start_remoting(apt);
fill_std_objref(&objref, riid, NULL); hr = marshal_object(apt, &objref.u_objref.u_standard.std, riid, pv, dest_context, @@ -2134,14 +2129,11 @@ static HRESULT unmarshal_object(const STDOBJREF *stdobjref, struct apartment *ap 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); + hr = rpc_create_clientchannel(&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); + hr = proxy_manager_create_ifproxy(proxy_manager, stdobjref, riid, chanbuf, &ifproxy); } else IUnknown_AddRef((IUnknown *)ifproxy->iface); diff --git a/dlls/combase/rpc.c b/dlls/combase/rpc.c index cb503e9ae7c..7cdb87a5a20 100644 --- a/dlls/combase/rpc.c +++ b/dlls/combase/rpc.c @@ -1,4 +1,8 @@ /* + * Copyright 2001 Ove KÃ¥ven, TransGaming Technologies + * Copyright 2002 Marcus Meissner + * Copyright 2005 Mike Hearn, Rob Shearman for CodeWeavers + * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public * License as published by the Free Software Foundation; either @@ -33,6 +37,138 @@
WINE_DEFAULT_DEBUG_CHANNEL(ole);
+static void __RPC_STUB dispatch_rpc(RPC_MESSAGE *msg); + +/* we only use one function to dispatch calls for all methods - we use the + * RPC_IF_OLE flag to tell the RPC runtime that this is the case */ +static RPC_DISPATCH_FUNCTION rpc_dispatch_table[1] = { dispatch_rpc }; /* (RO) */ +static RPC_DISPATCH_TABLE rpc_dispatch = { 1, rpc_dispatch_table }; /* (RO) */ + +static struct list registered_interfaces = LIST_INIT(registered_interfaces); /* (CS csRegIf) */ +static CRITICAL_SECTION csRegIf; +static CRITICAL_SECTION_DEBUG csRegIf_debug = +{ + 0, 0, &csRegIf, + { &csRegIf_debug.ProcessLocksList, &csRegIf_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": dcom registered server interfaces") } +}; +static CRITICAL_SECTION csRegIf = { &csRegIf_debug, -1, 0, 0, 0, 0 }; + +static struct list channel_hooks = LIST_INIT(channel_hooks); /* (CS csChannelHook) */ +static CRITICAL_SECTION csChannelHook; +static CRITICAL_SECTION_DEBUG csChannelHook_debug = +{ + 0, 0, &csChannelHook, + { &csChannelHook_debug.ProcessLocksList, &csChannelHook_debug.ProcessLocksList }, + 0, 0, { (DWORD_PTR)(__FILE__ ": channel hooks") } +}; +static CRITICAL_SECTION csChannelHook = { &csChannelHook_debug, -1, 0, 0, 0, 0 }; + +static WCHAR rpctransportW[] = L"ncalrpc"; + +struct registered_if +{ + struct list entry; + DWORD refs; /* ref count */ + RPC_SERVER_INTERFACE If; /* interface registered with the RPC runtime */ +}; + +/* get the pipe endpoint specified of the specified apartment */ +static inline void get_rpc_endpoint(LPWSTR endpoint, const OXID *oxid) +{ + /* FIXME: should get endpoint from rpcss */ + static const WCHAR wszEndpointFormat[] = {'\','p','i','p','e','\','O','L','E','_','%','0','8','l','x','%','0','8','l','x',0}; + wsprintfW(endpoint, wszEndpointFormat, (DWORD)(*oxid >> 32),(DWORD)*oxid); +} + +typedef struct +{ + IRpcChannelBuffer IRpcChannelBuffer_iface; + LONG refs; + + DWORD dest_context; /* returned from GetDestCtx */ + void *dest_context_data; /* returned from GetDestCtx */ +} RpcChannelBuffer; + +typedef struct +{ + RpcChannelBuffer super; /* superclass */ + + RPC_BINDING_HANDLE bind; /* handle to the remote server */ + OXID oxid; /* apartment in which the channel is valid */ + DWORD server_pid; /* id of server process */ + HANDLE event; /* cached event handle */ + IID iid; /* IID of the proxy this belongs to */ +} ClientRpcChannelBuffer; + +struct dispatch_params +{ + RPCOLEMESSAGE *msg; /* message */ + IRpcStubBuffer *stub; /* stub buffer, if applicable */ + IRpcChannelBuffer *chan; /* server channel buffer, if applicable */ + IID iid; /* ID of interface being called */ + IUnknown *iface; /* interface being called */ + HANDLE handle; /* handle that will become signaled when call finishes */ + BOOL bypass_rpcrt; /* bypass RPC runtime? */ + RPC_STATUS status; /* status (out) */ + HRESULT hr; /* hresult (out) */ +}; + +struct message_state +{ + RPC_BINDING_HANDLE binding_handle; + ULONG prefix_data_len; + SChannelHookCallInfo channel_hook_info; + BOOL bypass_rpcrt; + + /* client only */ + HWND target_hwnd; + DWORD target_tid; + struct dispatch_params params; +}; + +typedef struct +{ + ULONG conformance; /* NDR */ + GUID id; + ULONG size; + /* [size_is((size+7)&~7)] */ unsigned char data[1]; +} WIRE_ORPC_EXTENT; + +typedef struct +{ + ULONG size; + ULONG reserved; + unsigned char extent[1]; +} WIRE_ORPC_EXTENT_ARRAY; + +typedef struct +{ + ULONG version; + ULONG flags; + ULONG reserved1; + GUID cid; + unsigned char extensions[1]; +} WIRE_ORPCTHIS; + +typedef struct +{ + ULONG flags; + unsigned char extensions[1]; +} WIRE_ORPCTHAT; + +struct channel_hook_entry +{ + struct list entry; + GUID id; + IChannelHook *hook; +}; + +struct channel_hook_buffer_data +{ + GUID id; + ULONG extension_size; +}; void * __RPC_USER MIDL_user_allocate(SIZE_T size) { return heap_alloc(size); @@ -629,3 +765,1482 @@ void rpc_stop_local_server(void *registration) CloseHandle(lsp->thread); HeapFree(GetProcessHeap(), 0, lsp); } + +static HRESULT unmarshal_ORPCTHAT(RPC_MESSAGE *msg, ORPCTHAT *orpcthat, ORPC_EXTENT_ARRAY *orpc_ext_array, + WIRE_ORPC_EXTENT **first_wire_orpc_extent); + +/* Channel Hook Functions */ + +static ULONG ChannelHooks_ClientGetSize(SChannelHookCallInfo *info, struct channel_hook_buffer_data **data, + unsigned int *hook_count, ULONG *extension_count) +{ + struct channel_hook_entry *entry; + ULONG total_size = 0; + unsigned int hook_index = 0; + + *hook_count = 0; + *extension_count = 0; + + EnterCriticalSection(&csChannelHook); + + LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) + (*hook_count)++; + + if (*hook_count) + *data = HeapAlloc(GetProcessHeap(), 0, *hook_count * sizeof(struct channel_hook_buffer_data)); + else + *data = NULL; + + LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) + { + ULONG extension_size = 0; + + IChannelHook_ClientGetSize(entry->hook, &entry->id, &info->iid, &extension_size); + + TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size); + + extension_size = (extension_size+7)&~7; + (*data)[hook_index].id = entry->id; + (*data)[hook_index].extension_size = extension_size; + + /* an extension is only put onto the wire if it has data to write */ + if (extension_size) + { + total_size += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[extension_size]); + (*extension_count)++; + } + + hook_index++; + } + + LeaveCriticalSection(&csChannelHook); + + return total_size; +} + +static unsigned char * ChannelHooks_ClientFillBuffer(SChannelHookCallInfo *info, + unsigned char *buffer, struct channel_hook_buffer_data *data, + unsigned int hook_count) +{ + struct channel_hook_entry *entry; + + EnterCriticalSection(&csChannelHook); + + LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) + { + unsigned int i; + ULONG extension_size = 0; + WIRE_ORPC_EXTENT *wire_orpc_extent = (WIRE_ORPC_EXTENT *)buffer; + + for (i = 0; i < hook_count; i++) + if (IsEqualGUID(&entry->id, &data[i].id)) + extension_size = data[i].extension_size; + + /* an extension is only put onto the wire if it has data to write */ + if (!extension_size) + continue; + + IChannelHook_ClientFillBuffer(entry->hook, &entry->id, &info->iid, + &extension_size, buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0])); + + TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size); + + /* FIXME: set unused portion of wire_orpc_extent->data to 0? */ + + wire_orpc_extent->conformance = (extension_size+7)&~7; + wire_orpc_extent->size = extension_size; + wire_orpc_extent->id = entry->id; + buffer += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[wire_orpc_extent->conformance]); + } + + LeaveCriticalSection(&csChannelHook); + + return buffer; +} + +static void ChannelHooks_ServerNotify(SChannelHookCallInfo *info, + DWORD lDataRep, WIRE_ORPC_EXTENT *first_wire_orpc_extent, + ULONG extension_count) +{ + struct channel_hook_entry *entry; + ULONG i; + + EnterCriticalSection(&csChannelHook); + + LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) + { + WIRE_ORPC_EXTENT *wire_orpc_extent; + for (i = 0, wire_orpc_extent = first_wire_orpc_extent; + i < extension_count; + i++, wire_orpc_extent = (WIRE_ORPC_EXTENT *)&wire_orpc_extent->data[wire_orpc_extent->conformance]) + { + if (IsEqualGUID(&entry->id, &wire_orpc_extent->id)) + break; + } + if (i == extension_count) wire_orpc_extent = NULL; + + IChannelHook_ServerNotify(entry->hook, &entry->id, &info->iid, + wire_orpc_extent ? wire_orpc_extent->size : 0, + wire_orpc_extent ? wire_orpc_extent->data : NULL, + lDataRep); + } + + LeaveCriticalSection(&csChannelHook); +} + +static ULONG ChannelHooks_ServerGetSize(SChannelHookCallInfo *info, + struct channel_hook_buffer_data **data, unsigned int *hook_count, + ULONG *extension_count) +{ + struct channel_hook_entry *entry; + ULONG total_size = 0; + unsigned int hook_index = 0; + + *hook_count = 0; + *extension_count = 0; + + EnterCriticalSection(&csChannelHook); + + LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) + (*hook_count)++; + + if (*hook_count) + *data = HeapAlloc(GetProcessHeap(), 0, *hook_count * sizeof(struct channel_hook_buffer_data)); + else + *data = NULL; + + LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) + { + ULONG extension_size = 0; + + IChannelHook_ServerGetSize(entry->hook, &entry->id, &info->iid, S_OK, + &extension_size); + + TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size); + + extension_size = (extension_size+7)&~7; + (*data)[hook_index].id = entry->id; + (*data)[hook_index].extension_size = extension_size; + + /* an extension is only put onto the wire if it has data to write */ + if (extension_size) + { + total_size += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[extension_size]); + (*extension_count)++; + } + + hook_index++; + } + + LeaveCriticalSection(&csChannelHook); + + return total_size; +} + +static unsigned char * ChannelHooks_ServerFillBuffer(SChannelHookCallInfo *info, + unsigned char *buffer, struct channel_hook_buffer_data *data, + unsigned int hook_count) +{ + struct channel_hook_entry *entry; + + EnterCriticalSection(&csChannelHook); + + LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) + { + unsigned int i; + ULONG extension_size = 0; + WIRE_ORPC_EXTENT *wire_orpc_extent = (WIRE_ORPC_EXTENT *)buffer; + + for (i = 0; i < hook_count; i++) + if (IsEqualGUID(&entry->id, &data[i].id)) + extension_size = data[i].extension_size; + + /* an extension is only put onto the wire if it has data to write */ + if (!extension_size) + continue; + + IChannelHook_ServerFillBuffer(entry->hook, &entry->id, &info->iid, + &extension_size, buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]), + S_OK); + + TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size); + + /* FIXME: set unused portion of wire_orpc_extent->data to 0? */ + + wire_orpc_extent->conformance = (extension_size+7)&~7; + wire_orpc_extent->size = extension_size; + wire_orpc_extent->id = entry->id; + buffer += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[wire_orpc_extent->conformance]); + } + + LeaveCriticalSection(&csChannelHook); + + return buffer; +} + +static void ChannelHooks_ClientNotify(SChannelHookCallInfo *info, + DWORD lDataRep, WIRE_ORPC_EXTENT *first_wire_orpc_extent, + ULONG extension_count, HRESULT hrFault) +{ + struct channel_hook_entry *entry; + ULONG i; + + EnterCriticalSection(&csChannelHook); + + LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) + { + WIRE_ORPC_EXTENT *wire_orpc_extent; + for (i = 0, wire_orpc_extent = first_wire_orpc_extent; + i < extension_count; + i++, wire_orpc_extent = (WIRE_ORPC_EXTENT *)&wire_orpc_extent->data[wire_orpc_extent->conformance]) + { + if (IsEqualGUID(&entry->id, &wire_orpc_extent->id)) + break; + } + if (i == extension_count) wire_orpc_extent = NULL; + + IChannelHook_ClientNotify(entry->hook, &entry->id, &info->iid, + wire_orpc_extent ? wire_orpc_extent->size : 0, + wire_orpc_extent ? wire_orpc_extent->data : NULL, + lDataRep, hrFault); + } + + LeaveCriticalSection(&csChannelHook); +} + +HRESULT rpc_register_channel_hook(REFGUID rguid, IChannelHook *hook) +{ + struct channel_hook_entry *entry; + + entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry)); + if (!entry) + return E_OUTOFMEMORY; + + entry->id = *rguid; + entry->hook = hook; + IChannelHook_AddRef(hook); + + EnterCriticalSection(&csChannelHook); + list_add_tail(&channel_hooks, &entry->entry); + LeaveCriticalSection(&csChannelHook); + + return S_OK; +} + +void rpc_unregister_channel_hooks(void) +{ + struct channel_hook_entry *cursor; + struct channel_hook_entry *cursor2; + + EnterCriticalSection(&csChannelHook); + LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &channel_hooks, struct channel_hook_entry, entry) + HeapFree(GetProcessHeap(), 0, cursor); + LeaveCriticalSection(&csChannelHook); + DeleteCriticalSection(&csChannelHook); + DeleteCriticalSection(&csRegIf); +} + +/* RPC Channel Buffer Functions */ + +static HRESULT WINAPI RpcChannelBuffer_QueryInterface(IRpcChannelBuffer *iface, REFIID riid, LPVOID *ppv) +{ + *ppv = NULL; + if (IsEqualIID(riid,&IID_IRpcChannelBuffer) || IsEqualIID(riid,&IID_IUnknown)) + { + *ppv = iface; + IRpcChannelBuffer_AddRef(iface); + return S_OK; + } + return E_NOINTERFACE; +} + +static ULONG WINAPI RpcChannelBuffer_AddRef(LPRPCCHANNELBUFFER iface) +{ + RpcChannelBuffer *This = (RpcChannelBuffer *)iface; + return InterlockedIncrement(&This->refs); +} + +static ULONG WINAPI ServerRpcChannelBuffer_Release(LPRPCCHANNELBUFFER iface) +{ + RpcChannelBuffer *This = (RpcChannelBuffer *)iface; + ULONG ref; + + ref = InterlockedDecrement(&This->refs); + if (ref) + return ref; + + HeapFree(GetProcessHeap(), 0, This); + return 0; +} + +static ULONG WINAPI ClientRpcChannelBuffer_Release(LPRPCCHANNELBUFFER iface) +{ + ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface; + ULONG ref; + + ref = InterlockedDecrement(&This->super.refs); + if (ref) + return ref; + + if (This->event) CloseHandle(This->event); + RpcBindingFree(&This->bind); + HeapFree(GetProcessHeap(), 0, This); + return 0; +} + +static HRESULT WINAPI ServerRpcChannelBuffer_GetBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg, REFIID riid) +{ + RpcChannelBuffer *This = (RpcChannelBuffer *)iface; + RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg; + RPC_STATUS status; + ORPCTHAT *orpcthat; + struct message_state *message_state; + ULONG extensions_size; + struct channel_hook_buffer_data *channel_hook_data; + unsigned int channel_hook_count; + ULONG extension_count; + + TRACE("(%p)->(%p,%s)\n", This, olemsg, debugstr_guid(riid)); + + message_state = msg->Handle; + /* restore the binding handle and the real start of data */ + msg->Handle = message_state->binding_handle; + msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len; + + extensions_size = ChannelHooks_ServerGetSize(&message_state->channel_hook_info, + &channel_hook_data, &channel_hook_count, &extension_count); + + msg->BufferLength += FIELD_OFFSET(WIRE_ORPCTHAT, extensions) + sizeof(DWORD); + if (extensions_size) + { + msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent[2*sizeof(DWORD) + extensions_size]); + if (extension_count & 1) + msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]); + } + + if (message_state->bypass_rpcrt) + { + msg->Buffer = HeapAlloc(GetProcessHeap(), 0, msg->BufferLength); + if (msg->Buffer) + status = RPC_S_OK; + else + { + HeapFree(GetProcessHeap(), 0, channel_hook_data); + return E_OUTOFMEMORY; + } + } + else + status = I_RpcGetBuffer(msg); + + orpcthat = msg->Buffer; + msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPCTHAT, extensions); + + orpcthat->flags = ORPCF_NULL /* FIXME? */; + + /* NDR representation of orpcthat->extensions */ + *(DWORD *)msg->Buffer = extensions_size ? 1 : 0; + msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); + + if (extensions_size) + { + WIRE_ORPC_EXTENT_ARRAY *orpc_extent_array = msg->Buffer; + orpc_extent_array->size = extension_count; + orpc_extent_array->reserved = 0; + msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent); + /* NDR representation of orpc_extent_array->extent */ + *(DWORD *)msg->Buffer = 1; + msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); + /* NDR representation of [size_is] attribute of orpc_extent_array->extent */ + *(DWORD *)msg->Buffer = (extension_count + 1) & ~1; + msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); + + msg->Buffer = ChannelHooks_ServerFillBuffer(&message_state->channel_hook_info, + msg->Buffer, channel_hook_data, channel_hook_count); + + /* we must add a dummy extension if there is an odd extension + * count to meet the contract specified by the size_is attribute */ + if (extension_count & 1) + { + WIRE_ORPC_EXTENT *wire_orpc_extent = msg->Buffer; + wire_orpc_extent->conformance = 0; + wire_orpc_extent->id = GUID_NULL; + wire_orpc_extent->size = 0; + msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]); + } + } + + HeapFree(GetProcessHeap(), 0, channel_hook_data); + + /* store the prefixed data length so that we can restore the real buffer + * later */ + message_state->prefix_data_len = (char *)msg->Buffer - (char *)orpcthat; + msg->BufferLength -= message_state->prefix_data_len; + /* save away the message state again */ + msg->Handle = message_state; + + TRACE("-- %d\n", status); + + return HRESULT_FROM_WIN32(status); +} + +static HANDLE ClientRpcChannelBuffer_GetEventHandle(ClientRpcChannelBuffer *This) +{ + HANDLE event = InterlockedExchangePointer(&This->event, NULL); + + /* Note: must be auto-reset event so we can reuse it without a call + * to ResetEvent */ + if (!event) event = CreateEventW(NULL, FALSE, FALSE, NULL); + + return event; +} + +static void ClientRpcChannelBuffer_ReleaseEventHandle(ClientRpcChannelBuffer *This, HANDLE event) +{ + if (InterlockedCompareExchangePointer(&This->event, event, NULL)) + /* already a handle cached in This */ + CloseHandle(event); +} + +static HRESULT WINAPI ClientRpcChannelBuffer_GetBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg, REFIID riid) +{ + ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface; + RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg; + RPC_CLIENT_INTERFACE *cif; + RPC_STATUS status; + ORPCTHIS *orpcthis; + struct message_state *message_state; + ULONG extensions_size; + struct channel_hook_buffer_data *channel_hook_data; + unsigned int channel_hook_count; + ULONG extension_count; + IPID ipid; + HRESULT hr; + struct apartment *apt = NULL; + + TRACE("(%p)->(%p,%s)\n", This, olemsg, debugstr_guid(riid)); + + cif = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RPC_CLIENT_INTERFACE)); + if (!cif) + return E_OUTOFMEMORY; + + message_state = HeapAlloc(GetProcessHeap(), 0, sizeof(*message_state)); + if (!message_state) + { + HeapFree(GetProcessHeap(), 0, cif); + return E_OUTOFMEMORY; + } + + cif->Length = sizeof(RPC_CLIENT_INTERFACE); + /* RPC interface ID = COM interface ID */ + cif->InterfaceId.SyntaxGUID = This->iid; + /* COM objects always have a version of 0.0 */ + cif->InterfaceId.SyntaxVersion.MajorVersion = 0; + cif->InterfaceId.SyntaxVersion.MinorVersion = 0; + msg->Handle = This->bind; + msg->RpcInterfaceInformation = cif; + + message_state->prefix_data_len = 0; + message_state->binding_handle = This->bind; + + message_state->channel_hook_info.iid = *riid; + message_state->channel_hook_info.cbSize = sizeof(message_state->channel_hook_info); + CoGetCurrentLogicalThreadId(&message_state->channel_hook_info.uCausality); + message_state->channel_hook_info.dwServerPid = This->server_pid; + message_state->channel_hook_info.iMethod = msg->ProcNum & ~RPC_FLAGS_VALID_BIT; + message_state->channel_hook_info.pObject = NULL; /* only present on server-side */ + message_state->target_hwnd = NULL; + message_state->target_tid = 0; + memset(&message_state->params, 0, sizeof(message_state->params)); + + extensions_size = ChannelHooks_ClientGetSize(&message_state->channel_hook_info, + &channel_hook_data, &channel_hook_count, &extension_count); + + msg->BufferLength += FIELD_OFFSET(WIRE_ORPCTHIS, extensions) + sizeof(DWORD); + if (extensions_size) + { + msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent[2*sizeof(DWORD) + extensions_size]); + if (extension_count & 1) + msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]); + } + + RpcBindingInqObject(message_state->binding_handle, &ipid); + hr = ipid_get_dispatch_params(&ipid, &apt, NULL, &message_state->params.stub, + &message_state->params.chan, + &message_state->params.iid, + &message_state->params.iface); + if (hr == S_OK) + { + /* stub, chan, iface and iid are unneeded in multi-threaded case as we go + * via the RPC runtime */ + if (apt->multi_threaded) + { + IRpcStubBuffer_Release(message_state->params.stub); + message_state->params.stub = NULL; + IRpcChannelBuffer_Release(message_state->params.chan); + message_state->params.chan = NULL; + message_state->params.iface = NULL; + } + else + { + message_state->params.bypass_rpcrt = TRUE; + message_state->target_hwnd = apartment_getwindow(apt); + message_state->target_tid = apt->tid; + /* we assume later on that this being non-NULL is the indicator that + * means call directly instead of going through RPC runtime */ + if (!message_state->target_hwnd) + ERR("window for apartment %s is NULL\n", wine_dbgstr_longlong(apt->oxid)); + } + } + if (apt) apartment_release(apt); + message_state->params.handle = ClientRpcChannelBuffer_GetEventHandle(This); + /* Note: message_state->params.msg is initialised in + * ClientRpcChannelBuffer_SendReceive */ + + /* shortcut the RPC runtime */ + if (message_state->target_hwnd) + { + msg->Buffer = HeapAlloc(GetProcessHeap(), 0, msg->BufferLength); + if (msg->Buffer) + status = RPC_S_OK; + else + status = ERROR_OUTOFMEMORY; + } + else + status = I_RpcGetBuffer(msg); + + msg->Handle = message_state; + + if (status == RPC_S_OK) + { + orpcthis = msg->Buffer; + msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPCTHIS, extensions); + + orpcthis->version.MajorVersion = COM_MAJOR_VERSION; + orpcthis->version.MinorVersion = COM_MINOR_VERSION; + orpcthis->flags = message_state->channel_hook_info.dwServerPid ? ORPCF_LOCAL : ORPCF_NULL; + orpcthis->reserved1 = 0; + orpcthis->cid = message_state->channel_hook_info.uCausality; + + /* NDR representation of orpcthis->extensions */ + *(DWORD *)msg->Buffer = extensions_size ? 1 : 0; + msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); + + if (extensions_size) + { + ORPC_EXTENT_ARRAY *orpc_extent_array = msg->Buffer; + orpc_extent_array->size = extension_count; + orpc_extent_array->reserved = 0; + msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent); + /* NDR representation of orpc_extent_array->extent */ + *(DWORD *)msg->Buffer = 1; + msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); + /* NDR representation of [size_is] attribute of orpc_extent_array->extent */ + *(DWORD *)msg->Buffer = (extension_count + 1) & ~1; + msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); + + msg->Buffer = ChannelHooks_ClientFillBuffer(&message_state->channel_hook_info, + msg->Buffer, channel_hook_data, channel_hook_count); + + /* we must add a dummy extension if there is an odd extension + * count to meet the contract specified by the size_is attribute */ + if (extension_count & 1) + { + WIRE_ORPC_EXTENT *wire_orpc_extent = msg->Buffer; + wire_orpc_extent->conformance = 0; + wire_orpc_extent->id = GUID_NULL; + wire_orpc_extent->size = 0; + msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]); + } + } + + /* store the prefixed data length so that we can restore the real buffer + * pointer in ClientRpcChannelBuffer_SendReceive. */ + message_state->prefix_data_len = (char *)msg->Buffer - (char *)orpcthis; + msg->BufferLength -= message_state->prefix_data_len; + } + + HeapFree(GetProcessHeap(), 0, channel_hook_data); + + TRACE("-- %d\n", status); + + return HRESULT_FROM_WIN32(status); +} + +static HRESULT WINAPI ServerRpcChannelBuffer_SendReceive(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE *olemsg, ULONG *pstatus) +{ + FIXME("stub\n"); + return E_NOTIMPL; +} + +/* this thread runs an outgoing RPC */ +static DWORD WINAPI rpc_sendreceive_thread(LPVOID param) +{ + struct dispatch_params *data = param; + + /* Note: I_RpcSendReceive doesn't raise exceptions like the higher-level + * RPC functions do */ + data->status = I_RpcSendReceive((RPC_MESSAGE *)data->msg); + + TRACE("completed with status 0x%x\n", data->status); + + SetEvent(data->handle); + + return 0; +} + +static inline HRESULT ClientRpcChannelBuffer_IsCorrectApartment(ClientRpcChannelBuffer *This, const struct apartment *apt) +{ + if (!apt) + return S_FALSE; + if (This->oxid != apartment_getoxid(apt)) + return S_FALSE; + return S_OK; +} + +static HRESULT WINAPI ClientRpcChannelBuffer_SendReceive(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE *olemsg, ULONG *pstatus) +{ + ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface; + HRESULT hr; + RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg; + RPC_STATUS status; + DWORD index; + struct message_state *message_state; + ORPCTHAT orpcthat; + ORPC_EXTENT_ARRAY orpc_ext_array; + WIRE_ORPC_EXTENT *first_wire_orpc_extent = NULL; + HRESULT hrFault = S_OK; + struct apartment *apt = apartment_get_current_or_mta(); + struct tlsdata *tlsdata; + + TRACE("(%p) iMethod=%d\n", olemsg, olemsg->iMethod); + + hr = ClientRpcChannelBuffer_IsCorrectApartment(This, apt); + if (hr != S_OK) + { + ERR("called from wrong apartment, should have been 0x%s\n", + wine_dbgstr_longlong(This->oxid)); + if (apt) apartment_release(apt); + return RPC_E_WRONG_THREAD; + } + + if (FAILED(hr = com_get_tlsdata(&tlsdata))) + return hr; + + /* This situation should be impossible in multi-threaded apartments, + * because the calling thread isn't re-enterable. + * Note: doing a COM call during the processing of a sent message is + * only disallowed if a client call is already being waited for + * completion */ + if (!apt->multi_threaded && + tlsdata->pending_call_count_client && + InSendMessage()) + { + ERR("can't make an outgoing COM call in response to a sent message\n"); + apartment_release(apt); + return RPC_E_CANTCALLOUT_ININPUTSYNCCALL; + } + + message_state = msg->Handle; + /* restore the binding handle and the real start of data */ + msg->Handle = message_state->binding_handle; + msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len; + msg->BufferLength += message_state->prefix_data_len; + + /* Note: this is an optimization in the Microsoft OLE runtime that we need + * to copy, as shown by the test_no_couninitialize_client test. without + * short-circuiting the RPC runtime in the case below, the test will + * deadlock on the loader lock due to the RPC runtime needing to create + * a thread to process the RPC when this function is called indirectly + * from DllMain */ + + message_state->params.msg = olemsg; + if (message_state->params.bypass_rpcrt) + { + TRACE("Calling apartment thread 0x%08x...\n", message_state->target_tid); + + msg->ProcNum &= ~RPC_FLAGS_VALID_BIT; + + if (!PostMessageW(message_state->target_hwnd, DM_EXECUTERPC, 0, + (LPARAM)&message_state->params)) + { + ERR("PostMessage failed with error %u\n", GetLastError()); + + /* Note: message_state->params.iface doesn't have a reference and + * so doesn't need to be released */ + + hr = HRESULT_FROM_WIN32(GetLastError()); + } + } + else + { + /* we use a separate thread here because we need to be able to + * pump the message loop in the application thread: if we do not, + * any windows created by this thread will hang and RPCs that try + * and re-enter this STA from an incoming server thread will + * deadlock. InstallShield is an example of that. + */ + if (!QueueUserWorkItem(rpc_sendreceive_thread, &message_state->params, WT_EXECUTEDEFAULT)) + { + ERR("QueueUserWorkItem failed with error %u\n", GetLastError()); + hr = E_UNEXPECTED; + } + else + hr = S_OK; + } + + if (hr == S_OK) + { + if (WaitForSingleObject(message_state->params.handle, 0)) + { + tlsdata->pending_call_count_client++; + hr = CoWaitForMultipleHandles(0, INFINITE, 1, &message_state->params.handle, &index); + tlsdata->pending_call_count_client--; + } + } + ClientRpcChannelBuffer_ReleaseEventHandle(This, message_state->params.handle); + + /* for WM shortcut, faults are returned in params->hr */ + if (hr == S_OK) + hrFault = message_state->params.hr; + + status = message_state->params.status; + + orpcthat.flags = ORPCF_NULL; + orpcthat.extensions = NULL; + + TRACE("RPC call status: 0x%x\n", status); + if (status != RPC_S_OK) + hr = HRESULT_FROM_WIN32(status); + + TRACE("hrFault = 0x%08x\n", hrFault); + + /* FIXME: this condition should be + * "hr == S_OK && (!hrFault || msg->BufferLength > FIELD_OFFSET(ORPCTHAT, extensions) + 4)" + * but we don't currently reset the message length for PostMessage + * dispatched calls */ + if (hr == S_OK && hrFault == S_OK) + { + HRESULT hr2; + char *original_buffer = msg->Buffer; + + /* handle ORPCTHAT and client extensions */ + + hr2 = unmarshal_ORPCTHAT(msg, &orpcthat, &orpc_ext_array, &first_wire_orpc_extent); + if (FAILED(hr2)) + hr = hr2; + + message_state->prefix_data_len = (char *)msg->Buffer - original_buffer; + msg->BufferLength -= message_state->prefix_data_len; + } + else + message_state->prefix_data_len = 0; + + if (hr == S_OK) + { + ChannelHooks_ClientNotify(&message_state->channel_hook_info, + msg->DataRepresentation, + first_wire_orpc_extent, + orpcthat.extensions && first_wire_orpc_extent ? orpcthat.extensions->size : 0, + hrFault); + } + + /* save away the message state again */ + msg->Handle = message_state; + + if (pstatus) *pstatus = status; + + if (hr == S_OK) + hr = hrFault; + + TRACE("-- 0x%08x\n", hr); + + apartment_release(apt); + return hr; +} + +static HRESULT WINAPI ServerRpcChannelBuffer_FreeBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg) +{ + RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg; + RPC_STATUS status; + struct message_state *message_state; + + TRACE("(%p)\n", msg); + + message_state = msg->Handle; + /* restore the binding handle and the real start of data */ + msg->Handle = message_state->binding_handle; + msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len; + msg->BufferLength += message_state->prefix_data_len; + message_state->prefix_data_len = 0; + + if (message_state->bypass_rpcrt) + { + HeapFree(GetProcessHeap(), 0, msg->Buffer); + status = RPC_S_OK; + } + else + status = I_RpcFreeBuffer(msg); + + msg->Handle = message_state; + + TRACE("-- %d\n", status); + + return HRESULT_FROM_WIN32(status); +} + +static HRESULT WINAPI ClientRpcChannelBuffer_FreeBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg) +{ + RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg; + RPC_STATUS status; + struct message_state *message_state; + + TRACE("(%p)\n", msg); + + message_state = msg->Handle; + /* restore the binding handle and the real start of data */ + msg->Handle = message_state->binding_handle; + msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len; + msg->BufferLength += message_state->prefix_data_len; + + if (message_state->params.bypass_rpcrt) + { + HeapFree(GetProcessHeap(), 0, msg->Buffer); + status = RPC_S_OK; + } + else + status = I_RpcFreeBuffer(msg); + + HeapFree(GetProcessHeap(), 0, msg->RpcInterfaceInformation); + msg->RpcInterfaceInformation = NULL; + + if (message_state->params.stub) + IRpcStubBuffer_Release(message_state->params.stub); + if (message_state->params.chan) + IRpcChannelBuffer_Release(message_state->params.chan); + HeapFree(GetProcessHeap(), 0, message_state); + + TRACE("-- %d\n", status); + + return HRESULT_FROM_WIN32(status); +} + +static HRESULT WINAPI ClientRpcChannelBuffer_GetDestCtx(LPRPCCHANNELBUFFER iface, DWORD* pdwDestContext, void** ppvDestContext) +{ + ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface; + + TRACE("(%p,%p)\n", pdwDestContext, ppvDestContext); + + *pdwDestContext = This->super.dest_context; + *ppvDestContext = This->super.dest_context_data; + + return S_OK; +} + +static HRESULT WINAPI ServerRpcChannelBuffer_GetDestCtx(LPRPCCHANNELBUFFER iface, DWORD* dest_context, void** dest_context_data) +{ + RpcChannelBuffer *This = (RpcChannelBuffer *)iface; + + TRACE("(%p,%p)\n", dest_context, dest_context_data); + + *dest_context = This->dest_context; + *dest_context_data = This->dest_context_data; + return S_OK; +} + +static HRESULT WINAPI RpcChannelBuffer_IsConnected(LPRPCCHANNELBUFFER iface) +{ + TRACE("()\n"); + /* native does nothing too */ + return S_OK; +} + +static const IRpcChannelBufferVtbl ClientRpcChannelBufferVtbl = +{ + RpcChannelBuffer_QueryInterface, + RpcChannelBuffer_AddRef, + ClientRpcChannelBuffer_Release, + ClientRpcChannelBuffer_GetBuffer, + ClientRpcChannelBuffer_SendReceive, + ClientRpcChannelBuffer_FreeBuffer, + ClientRpcChannelBuffer_GetDestCtx, + RpcChannelBuffer_IsConnected +}; + +static const IRpcChannelBufferVtbl ServerRpcChannelBufferVtbl = +{ + RpcChannelBuffer_QueryInterface, + RpcChannelBuffer_AddRef, + ServerRpcChannelBuffer_Release, + ServerRpcChannelBuffer_GetBuffer, + ServerRpcChannelBuffer_SendReceive, + ServerRpcChannelBuffer_FreeBuffer, + ServerRpcChannelBuffer_GetDestCtx, + RpcChannelBuffer_IsConnected +}; + +/* returns a channel buffer for proxies */ +HRESULT rpc_create_clientchannel(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) +{ + ClientRpcChannelBuffer *This; + WCHAR endpoint[200]; + RPC_BINDING_HANDLE bind; + RPC_STATUS status; + LPWSTR string_binding; + + /* FIXME: get the endpoint from oxid_info->psa instead */ + get_rpc_endpoint(endpoint, oxid); + + TRACE("proxy pipe: connecting to endpoint: %s\n", debugstr_w(endpoint)); + + status = RpcStringBindingComposeW( + NULL, + rpctransportW, + NULL, + endpoint, + NULL, + &string_binding); + + if (status == RPC_S_OK) + { + status = RpcBindingFromStringBindingW(string_binding, &bind); + + if (status == RPC_S_OK) + { + IPID ipid2 = *ipid; /* why can't RpcBindingSetObject take a const? */ + status = RpcBindingSetObject(bind, &ipid2); + if (status != RPC_S_OK) + RpcBindingFree(&bind); + } + + RpcStringFreeW(&string_binding); + } + + if (status != RPC_S_OK) + { + ERR("Couldn't get binding for endpoint %s, status = %d\n", debugstr_w(endpoint), status); + return HRESULT_FROM_WIN32(status); + } + + This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); + if (!This) + { + RpcBindingFree(&bind); + return E_OUTOFMEMORY; + } + + This->super.IRpcChannelBuffer_iface.lpVtbl = &ClientRpcChannelBufferVtbl; + This->super.refs = 1; + This->super.dest_context = dest_context; + This->super.dest_context_data = dest_context_data; + This->bind = bind; + This->oxid = apartment_getoxid(apt); + This->server_pid = oxid_info->dwPid; + This->event = NULL; + This->iid = *iid; + + *chan = &This->super.IRpcChannelBuffer_iface; + + return S_OK; +} + +HRESULT rpc_create_serverchannel(DWORD dest_context, void *dest_context_data, IRpcChannelBuffer **chan) +{ + RpcChannelBuffer *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); + if (!This) + return E_OUTOFMEMORY; + + This->IRpcChannelBuffer_iface.lpVtbl = &ServerRpcChannelBufferVtbl; + This->refs = 1; + This->dest_context = dest_context; + This->dest_context_data = dest_context_data; + + *chan = &This->IRpcChannelBuffer_iface; + + return S_OK; +} + +/* unmarshals ORPC_EXTENT_ARRAY according to NDR rules, but doesn't allocate + * any memory */ +static HRESULT unmarshal_ORPC_EXTENT_ARRAY(RPC_MESSAGE *msg, const char *end, + ORPC_EXTENT_ARRAY *extensions, + WIRE_ORPC_EXTENT **first_wire_orpc_extent) +{ + DWORD pointer_id; + DWORD i; + + memcpy(extensions, msg->Buffer, FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent)); + msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent); + + if ((const char *)msg->Buffer + 2 * sizeof(DWORD) > end) + return RPC_E_INVALID_HEADER; + + pointer_id = *(DWORD *)msg->Buffer; + msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); + extensions->extent = NULL; + + if (pointer_id) + { + WIRE_ORPC_EXTENT *wire_orpc_extent; + + /* conformance */ + if (*(DWORD *)msg->Buffer != ((extensions->size+1)&~1)) + return RPC_S_INVALID_BOUND; + + msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); + + /* arbitrary limit for security (don't know what native does) */ + if (extensions->size > 256) + { + ERR("too many extensions: %d\n", extensions->size); + return RPC_S_INVALID_BOUND; + } + + *first_wire_orpc_extent = wire_orpc_extent = msg->Buffer; + for (i = 0; i < ((extensions->size+1)&~1); i++) + { + if ((const char *)&wire_orpc_extent->data[0] > end) + return RPC_S_INVALID_BOUND; + if (wire_orpc_extent->conformance != ((wire_orpc_extent->size+7)&~7)) + return RPC_S_INVALID_BOUND; + if ((const char *)&wire_orpc_extent->data[wire_orpc_extent->conformance] > end) + return RPC_S_INVALID_BOUND; + TRACE("size %u, guid %s\n", wire_orpc_extent->size, debugstr_guid(&wire_orpc_extent->id)); + wire_orpc_extent = (WIRE_ORPC_EXTENT *)&wire_orpc_extent->data[wire_orpc_extent->conformance]; + } + msg->Buffer = wire_orpc_extent; + } + + return S_OK; +} + +/* unmarshals ORPCTHIS according to NDR rules, but doesn't allocate any memory */ +static HRESULT unmarshal_ORPCTHIS(RPC_MESSAGE *msg, ORPCTHIS *orpcthis, + ORPC_EXTENT_ARRAY *orpc_ext_array, WIRE_ORPC_EXTENT **first_wire_orpc_extent) +{ + const char *end = (char *)msg->Buffer + msg->BufferLength; + + *first_wire_orpc_extent = NULL; + + if (msg->BufferLength < FIELD_OFFSET(WIRE_ORPCTHIS, extensions) + sizeof(DWORD)) + { + ERR("invalid buffer length\n"); + return RPC_E_INVALID_HEADER; + } + + memcpy(orpcthis, msg->Buffer, FIELD_OFFSET(WIRE_ORPCTHIS, extensions)); + msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPCTHIS, extensions); + + if ((const char *)msg->Buffer + sizeof(DWORD) > end) + return RPC_E_INVALID_HEADER; + + if (*(DWORD *)msg->Buffer) + orpcthis->extensions = orpc_ext_array; + else + orpcthis->extensions = NULL; + + msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); + + if (orpcthis->extensions) + { + HRESULT hr = unmarshal_ORPC_EXTENT_ARRAY(msg, end, orpc_ext_array, + first_wire_orpc_extent); + if (FAILED(hr)) + return hr; + } + + if ((orpcthis->version.MajorVersion != COM_MAJOR_VERSION) || + (orpcthis->version.MinorVersion > COM_MINOR_VERSION)) + { + ERR("COM version {%d, %d} not supported\n", + orpcthis->version.MajorVersion, orpcthis->version.MinorVersion); + return RPC_E_VERSION_MISMATCH; + } + + if (orpcthis->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4)) + { + ERR("invalid flags 0x%x\n", orpcthis->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4)); + return RPC_E_INVALID_HEADER; + } + + return S_OK; +} + +static HRESULT unmarshal_ORPCTHAT(RPC_MESSAGE *msg, ORPCTHAT *orpcthat, + ORPC_EXTENT_ARRAY *orpc_ext_array, WIRE_ORPC_EXTENT **first_wire_orpc_extent) +{ + const char *end = (char *)msg->Buffer + msg->BufferLength; + + *first_wire_orpc_extent = NULL; + + if (msg->BufferLength < FIELD_OFFSET(WIRE_ORPCTHAT, extensions) + sizeof(DWORD)) + { + ERR("invalid buffer length\n"); + return RPC_E_INVALID_HEADER; + } + + memcpy(orpcthat, msg->Buffer, FIELD_OFFSET(WIRE_ORPCTHAT, extensions)); + msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPCTHAT, extensions); + + if ((const char *)msg->Buffer + sizeof(DWORD) > end) + return RPC_E_INVALID_HEADER; + + if (*(DWORD *)msg->Buffer) + orpcthat->extensions = orpc_ext_array; + else + orpcthat->extensions = NULL; + + msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); + + if (orpcthat->extensions) + { + HRESULT hr = unmarshal_ORPC_EXTENT_ARRAY(msg, end, orpc_ext_array, + first_wire_orpc_extent); + if (FAILED(hr)) + return hr; + } + + if (orpcthat->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4)) + { + ERR("invalid flags 0x%x\n", orpcthat->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4)); + return RPC_E_INVALID_HEADER; + } + + return S_OK; +} + +void rpc_execute_call(struct dispatch_params *params) +{ + struct message_state *message_state = NULL; + RPC_MESSAGE *msg = (RPC_MESSAGE *)params->msg; + char *original_buffer = msg->Buffer; + ORPCTHIS orpcthis; + ORPC_EXTENT_ARRAY orpc_ext_array; + WIRE_ORPC_EXTENT *first_wire_orpc_extent; + GUID old_causality_id; + struct tlsdata *tlsdata; + struct apartment *apt; + + if (FAILED(com_get_tlsdata(&tlsdata))) + return; + + apt = com_get_current_apt(); + + /* handle ORPCTHIS and server extensions */ + + params->hr = unmarshal_ORPCTHIS(msg, &orpcthis, &orpc_ext_array, &first_wire_orpc_extent); + if (params->hr != S_OK) + { + msg->Buffer = original_buffer; + goto exit; + } + + message_state = HeapAlloc(GetProcessHeap(), 0, sizeof(*message_state)); + if (!message_state) + { + params->hr = E_OUTOFMEMORY; + msg->Buffer = original_buffer; + goto exit; + } + + message_state->prefix_data_len = (char *)msg->Buffer - original_buffer; + message_state->binding_handle = msg->Handle; + message_state->bypass_rpcrt = params->bypass_rpcrt; + + message_state->channel_hook_info.iid = params->iid; + message_state->channel_hook_info.cbSize = sizeof(message_state->channel_hook_info); + message_state->channel_hook_info.uCausality = orpcthis.cid; + message_state->channel_hook_info.dwServerPid = GetCurrentProcessId(); + message_state->channel_hook_info.iMethod = msg->ProcNum; + message_state->channel_hook_info.pObject = params->iface; + + if (orpcthis.extensions && first_wire_orpc_extent && + orpcthis.extensions->size) + ChannelHooks_ServerNotify(&message_state->channel_hook_info, msg->DataRepresentation, first_wire_orpc_extent, orpcthis.extensions->size); + + msg->Handle = message_state; + msg->BufferLength -= message_state->prefix_data_len; + + /* call message filter */ + + if (apt->filter) + { + DWORD handlecall; + INTERFACEINFO interface_info; + CALLTYPE calltype; + + interface_info.pUnk = params->iface; + interface_info.iid = params->iid; + interface_info.wMethod = msg->ProcNum; + + if (IsEqualGUID(&orpcthis.cid, &tlsdata->causality_id)) + calltype = CALLTYPE_NESTED; + else if (tlsdata->pending_call_count_server == 0) + calltype = CALLTYPE_TOPLEVEL; + else + calltype = CALLTYPE_TOPLEVEL_CALLPENDING; + + handlecall = IMessageFilter_HandleInComingCall(apt->filter, + calltype, + UlongToHandle(GetCurrentProcessId()), + 0 /* FIXME */, + &interface_info); + TRACE("IMessageFilter_HandleInComingCall returned %d\n", handlecall); + switch (handlecall) + { + case SERVERCALL_REJECTED: + params->hr = RPC_E_CALL_REJECTED; + goto exit_reset_state; + case SERVERCALL_RETRYLATER: +#if 0 /* FIXME: handle retries on the client side before enabling this code */ + params->hr = RPC_E_RETRY; + goto exit_reset_state; +#else + FIXME("retry call later not implemented\n"); + break; +#endif + case SERVERCALL_ISHANDLED: + default: + break; + } + } + + /* invoke the method */ + + /* save the old causality ID - note: any calls executed while processing + * messages received during the SendReceive will appear to originate from + * this call - this should be checked with what Windows does */ + old_causality_id = tlsdata->causality_id; + tlsdata->causality_id = orpcthis.cid; + tlsdata->pending_call_count_server++; + params->hr = IRpcStubBuffer_Invoke(params->stub, params->msg, params->chan); + tlsdata->pending_call_count_server--; + tlsdata->causality_id = old_causality_id; + + /* the invoke allocated a new buffer, so free the old one */ + if (message_state->bypass_rpcrt && original_buffer != msg->Buffer) + HeapFree(GetProcessHeap(), 0, original_buffer); + +exit_reset_state: + message_state = msg->Handle; + msg->Handle = message_state->binding_handle; + msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len; + msg->BufferLength += message_state->prefix_data_len; + +exit: + HeapFree(GetProcessHeap(), 0, message_state); + if (params->handle) SetEvent(params->handle); +} + +static void __RPC_STUB dispatch_rpc(RPC_MESSAGE *msg) +{ + struct dispatch_params *params; + struct stub_manager *stub_manager; + struct apartment *apt; + IPID ipid; + HRESULT hr; + + RpcBindingInqObject(msg->Handle, &ipid); + + TRACE("ipid = %s, iMethod = %d\n", debugstr_guid(&ipid), msg->ProcNum); + + params = HeapAlloc(GetProcessHeap(), 0, sizeof(*params)); + if (!params) + { + RpcRaiseException(E_OUTOFMEMORY); + return; + } + + hr = ipid_get_dispatch_params(&ipid, &apt, &stub_manager, ¶ms->stub, ¶ms->chan, + ¶ms->iid, ¶ms->iface); + if (hr != S_OK) + { + ERR("no apartment found for ipid %s\n", debugstr_guid(&ipid)); + HeapFree(GetProcessHeap(), 0, params); + RpcRaiseException(hr); + return; + } + + params->msg = (RPCOLEMESSAGE *)msg; + params->status = RPC_S_OK; + params->hr = S_OK; + params->handle = NULL; + params->bypass_rpcrt = FALSE; + + /* Note: this is the important difference between STAs and MTAs - we + * always execute RPCs to STAs in the thread that originally created the + * apartment (i.e. the one that pumps messages to the window) */ + if (!apt->multi_threaded) + { + params->handle = CreateEventW(NULL, FALSE, FALSE, NULL); + + TRACE("Calling apartment thread 0x%08x...\n", apt->tid); + + if (PostMessageW(apartment_getwindow(apt), DM_EXECUTERPC, 0, (LPARAM)params)) + WaitForSingleObject(params->handle, INFINITE); + else + { + ERR("PostMessage failed with error %u\n", GetLastError()); + IRpcChannelBuffer_Release(params->chan); + IRpcStubBuffer_Release(params->stub); + } + CloseHandle(params->handle); + } + else + { + BOOL joined = FALSE; + struct tlsdata *tlsdata; + + com_get_tlsdata(&tlsdata); + + if (!tlsdata->apt) + { + enter_apartment(tlsdata, COINIT_MULTITHREADED); + joined = TRUE; + } + rpc_execute_call(params); + if (joined) + { + leave_apartment(tlsdata); + } + } + + hr = params->hr; + if (params->chan) + IRpcChannelBuffer_Release(params->chan); + if (params->stub) + IRpcStubBuffer_Release(params->stub); + HeapFree(GetProcessHeap(), 0, params); + + stub_manager_int_release(stub_manager); + apartment_release(apt); + + /* if IRpcStubBuffer_Invoke fails, we should raise an exception to tell + * the RPC runtime that the call failed */ + if (hr != S_OK) RpcRaiseException(hr); +} + +/* stub registration */ +HRESULT rpc_register_interface(REFIID riid) +{ + struct registered_if *rif; + BOOL found = FALSE; + HRESULT hr = S_OK; + + TRACE("(%s)\n", debugstr_guid(riid)); + + EnterCriticalSection(&csRegIf); + LIST_FOR_EACH_ENTRY(rif, ®istered_interfaces, struct registered_if, entry) + { + if (IsEqualGUID(&rif->If.InterfaceId.SyntaxGUID, riid)) + { + rif->refs++; + found = TRUE; + break; + } + } + if (!found) + { + TRACE("Creating new interface\n"); + + rif = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*rif)); + if (rif) + { + RPC_STATUS status; + + rif->refs = 1; + rif->If.Length = sizeof(RPC_SERVER_INTERFACE); + /* RPC interface ID = COM interface ID */ + rif->If.InterfaceId.SyntaxGUID = *riid; + rif->If.DispatchTable = &rpc_dispatch; + /* all other fields are 0, including the version asCOM objects + * always have a version of 0.0 */ + status = RpcServerRegisterIfEx( + (RPC_IF_HANDLE)&rif->If, + NULL, NULL, + RPC_IF_OLE | RPC_IF_AUTOLISTEN, + RPC_C_LISTEN_MAX_CALLS_DEFAULT, + NULL); + if (status == RPC_S_OK) + list_add_tail(®istered_interfaces, &rif->entry); + else + { + ERR("RpcServerRegisterIfEx failed with error %d\n", status); + HeapFree(GetProcessHeap(), 0, rif); + hr = HRESULT_FROM_WIN32(status); + } + } + else + hr = E_OUTOFMEMORY; + } + LeaveCriticalSection(&csRegIf); + return hr; +} + +/* stub unregistration */ +void rpc_unregister_interface(REFIID riid, BOOL wait) +{ + struct registered_if *rif; + EnterCriticalSection(&csRegIf); + LIST_FOR_EACH_ENTRY(rif, ®istered_interfaces, struct registered_if, entry) + { + if (IsEqualGUID(&rif->If.InterfaceId.SyntaxGUID, riid)) + { + if (!--rif->refs) + { + RpcServerUnregisterIf((RPC_IF_HANDLE)&rif->If, NULL, wait); + list_remove(&rif->entry); + HeapFree(GetProcessHeap(), 0, rif); + } + break; + } + } + LeaveCriticalSection(&csRegIf); +} + +/* get the info for an OXID, including the IPID for the rem unknown interface + * and the string binding */ +HRESULT rpc_resolve_oxid(OXID oxid, OXID_INFO *oxid_info) +{ + TRACE("%s\n", wine_dbgstr_longlong(oxid)); + + oxid_info->dwTid = 0; + oxid_info->dwPid = 0; + oxid_info->dwAuthnHint = RPC_C_AUTHN_LEVEL_NONE; + /* FIXME: this is a hack around not having an OXID resolver yet - + * this function should contact the machine's OXID resolver and then it + * should give us the IPID of the IRemUnknown interface */ + oxid_info->ipidRemUnknown.Data1 = 0xffffffff; + oxid_info->ipidRemUnknown.Data2 = 0xffff; + oxid_info->ipidRemUnknown.Data3 = 0xffff; + memcpy(oxid_info->ipidRemUnknown.Data4, &oxid, sizeof(OXID)); + oxid_info->psa = NULL /* FIXME */; + + return S_OK; +} + +/* make the apartment reachable by other threads and processes and create the + * IRemUnknown object */ +void rpc_start_remoting(struct apartment *apt) +{ + if (!InterlockedExchange(&apt->remoting_started, TRUE)) + { + WCHAR endpoint[200]; + RPC_STATUS status; + + get_rpc_endpoint(endpoint, &apt->oxid); + + status = RpcServerUseProtseqEpW( + rpctransportW, + RPC_C_PROTSEQ_MAX_REQS_DEFAULT, + endpoint, + NULL); + if (status != RPC_S_OK) + ERR("Couldn't register endpoint %s\n", debugstr_w(endpoint)); + + /* FIXME: move remote unknown exporting into this function */ + } + start_apartment_remote_unknown(apt); +} diff --git a/dlls/combase/stubmanager.c b/dlls/combase/stubmanager.c index a800c0b4844..16b8bb35d00 100644 --- a/dlls/combase/stubmanager.c +++ b/dlls/combase/stubmanager.c @@ -44,11 +44,6 @@
WINE_DEFAULT_DEBUG_CHANNEL(ole);
-extern HRESULT WINAPI marshal_object(struct apartment *apt, STDOBJREF *stdobjref, REFIID riid, IUnknown *object, - DWORD dest_context, void *dest_context_data, MSHLFLAGS mshlflags); -extern HRESULT WINAPI RPC_CreateServerChannel(DWORD dest_context, void *dest_context_data, IRpcChannelBuffer **chan); -extern void WINAPI RPC_UnregisterInterface(REFIID riid, BOOL wait); - /* generates an ipid in the following format (similar to native version): * Data1 = apartment-local ipid counter * Data2 = apartment creator thread ID @@ -92,7 +87,7 @@ struct ifstub * stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer * return NULL; }
- hr = RPC_CreateServerChannel(dest_context, dest_context_data, &stub->chan); + hr = rpc_create_serverchannel(dest_context, dest_context_data, &stub->chan); if (hr != S_OK) { IUnknown_Release(stub->iface); @@ -131,7 +126,7 @@ static void stub_manager_delete_ifstub(struct stub_manager *m, struct ifstub *if list_remove(&ifstub->entry);
if (!m->disconnected) - RPC_UnregisterInterface(&ifstub->iid, TRUE); + rpc_unregister_interface(&ifstub->iid, TRUE);
if (ifstub->stubbuffer) IRpcStubBuffer_Release(ifstub->stubbuffer); IUnknown_Release(ifstub->iface); @@ -255,7 +250,7 @@ void stub_manager_disconnect(struct stub_manager *m) if (!m->disconnected) { LIST_FOR_EACH_ENTRY(ifstub, &m->ifstubs, struct ifstub, entry) - RPC_UnregisterInterface(&ifstub->iid, FALSE); + rpc_unregister_interface(&ifstub->iid, FALSE);
m->disconnected = TRUE; } @@ -314,7 +309,7 @@ static ULONG stub_manager_int_addref(struct stub_manager *m) }
/* decrements the internal refcount */ -ULONG WINAPI stub_manager_int_release(struct stub_manager *m) +ULONG stub_manager_int_release(struct stub_manager *m) { ULONG refs; struct apartment *apt = m->apt; @@ -542,10 +537,9 @@ static HRESULT ipid_to_stub_manager(const IPID *ipid, struct apartment **stub_ap /* gets the apartment, stub and channel of an object. the caller must * release the references to all objects (except iface) if the function * returned success, otherwise no references are returned. */ -HRESULT WINAPI ipid_get_dispatch_params(const IPID *ipid, struct apartment **stub_apt, - struct stub_manager **manager, - IRpcStubBuffer **stub, IRpcChannelBuffer **chan, - IID *iid, IUnknown **iface) +HRESULT ipid_get_dispatch_params(const IPID *ipid, struct apartment **stub_apt, + struct stub_manager **manager, IRpcStubBuffer **stub, IRpcChannelBuffer **chan, + IID *iid, IUnknown **iface) { struct stub_manager *stubmgr; struct ifstub *ifstub; @@ -826,7 +820,7 @@ static const IRemUnknownVtbl RemUnknown_Vtbl = };
/* starts the IRemUnknown listener for the current apartment */ -HRESULT WINAPI start_apartment_remote_unknown(struct apartment *apt) +HRESULT start_apartment_remote_unknown(struct apartment *apt) { IRemUnknown *pRemUnknown; HRESULT hr = S_OK; diff --git a/dlls/ole32/Makefile.in b/dlls/ole32/Makefile.in index ddbdfab30de..89807b94ef0 100644 --- a/dlls/ole32/Makefile.in +++ b/dlls/ole32/Makefile.in @@ -33,7 +33,6 @@ C_SRCS = \ oleobj.c \ oleproxy.c \ pointermoniker.c \ - rpc.c \ stg_prop.c \ stg_stream.c \ storage32.c \ diff --git a/dlls/ole32/compobj.c b/dlls/ole32/compobj.c index b92dfd87267..a9a9af53108 100644 --- a/dlls/ole32/compobj.c +++ b/dlls/ole32/compobj.c @@ -930,26 +930,6 @@ HRESULT WINAPI CoGetObject(LPCWSTR pszName, BIND_OPTS *pBindOptions, return hr; }
-/*********************************************************************** - * CoRegisterChannelHook [OLE32.@] - * - * Registers a process-wide hook that is called during ORPC calls. - * - * PARAMS - * guidExtension [I] GUID of the channel hook to register. - * pChannelHook [I] Channel hook object to register. - * - * RETURNS - * Success: S_OK. - * Failure: HRESULT code. - */ -HRESULT WINAPI CoRegisterChannelHook(REFGUID guidExtension, IChannelHook *pChannelHook) -{ - TRACE("(%s, %p)\n", debugstr_guid(guidExtension), pChannelHook); - - return RPC_RegisterChannelHook(guidExtension, pChannelHook); -} - /* Returns expanded dll path from the registry or activation context. */ static BOOL get_object_dll_path(const struct class_reg_data *regdata, WCHAR *dst, DWORD dstlen) { @@ -1176,7 +1156,6 @@ BOOL WINAPI DllMain(HINSTANCE hinstDLL, DWORD fdwReason, LPVOID reserved) case DLL_PROCESS_DETACH: if (reserved) break; release_std_git(); - RPC_UnregisterAllChannelHooks(); break; } return TRUE; diff --git a/dlls/ole32/compobj_private.h b/dlls/ole32/compobj_private.h index 12acfc0a9e5..18b39d655f0 100644 --- a/dlls/ole32/compobj_private.h +++ b/dlls/ole32/compobj_private.h @@ -39,113 +39,9 @@ #include "winreg.h" #include "winternl.h"
-struct apartment; - DEFINE_OLEGUID( CLSID_DfMarshal, 0x0000030b, 0, 0 );
-/* signal to stub manager that this is a rem unknown object */ -#define MSHLFLAGSP_REMUNKNOWN 0x80000000 - -/* Thread-safety Annotation Legend: - * - * RO - The value is read only. It never changes after creation, so no - * locking is required. - * LOCK - The value is written to only using Interlocked* functions. - * CS - The value is read or written to inside a critical section. - * The identifier following "CS" is the specific critical section that - * must be used. - * MUTEX - The value is read or written to with a mutex held. - * The identifier following "MUTEX" is the specific mutex that - * must be used. - */ - -typedef enum ifstub_state -{ - STUBSTATE_NORMAL_MARSHALED, - STUBSTATE_NORMAL_UNMARSHALED, - STUBSTATE_TABLE_WEAK_MARSHALED, - STUBSTATE_TABLE_WEAK_UNMARSHALED, - STUBSTATE_TABLE_STRONG, -} STUB_STATE; - -/* an interface stub */ -struct ifstub -{ - struct list entry; /* entry in stub_manager->ifstubs list (CS stub_manager->lock) */ - IRpcStubBuffer *stubbuffer; /* RO */ - IID iid; /* RO */ - IPID ipid; /* RO */ - IUnknown *iface; /* RO */ - MSHLFLAGS flags; /* so we can enforce process-local marshalling rules (RO) */ - IRpcChannelBuffer*chan; /* channel passed to IRpcStubBuffer::Invoke (RO) */ -}; - - -/* stub managers hold refs on the object and each interface stub */ -struct stub_manager -{ - struct list entry; /* entry in apartment stubmgr list (CS apt->cs) */ - struct list ifstubs; /* list of active ifstubs for the object (CS lock) */ - CRITICAL_SECTION lock; - struct apartment *apt; /* owning apt (RO) */ - - ULONG extrefs; /* number of 'external' references (CS lock) */ - ULONG refs; /* internal reference count (CS apt->cs) */ - ULONG weakrefs; /* number of weak references (CS lock) */ - OID oid; /* apartment-scoped unique identifier (RO) */ - IUnknown *object; /* the object we are managing the stub for (RO) */ - ULONG next_ipid; /* currently unused (LOCK) */ - OXID_INFO oxid_info; /* string binding, ipid of rem unknown and other information (RO) */ - - IExternalConnection *extern_conn; - - /* We need to keep a count of the outstanding marshals, so we can enforce the - * marshalling rules (ie, you can only unmarshal normal marshals once). Note - * that these counts do NOT include unmarshalled interfaces, once a stream is - * unmarshalled and a proxy set up, this count is decremented. - */ - - ULONG norm_refs; /* refcount of normal marshals (CS lock) */ - BOOL disconnected; /* CoDisconnectObject has been called (CS lock) */ -}; - -struct apartment -{ - struct list entry; - - LONG refs; /* refcount of the apartment (LOCK) */ - BOOL multi_threaded; /* multi-threaded or single-threaded apartment? (RO) */ - DWORD tid; /* thread id (RO) */ - OXID oxid; /* object exporter ID (RO) */ - LONG ipidc; /* interface pointer ID counter, starts at 1 (LOCK) */ - CRITICAL_SECTION cs; /* thread safety */ - struct list proxies; /* imported objects (CS cs) */ - struct list stubmgrs; /* stub managers for exported objects (CS cs) */ - BOOL remunk_exported; /* has the IRemUnknown interface for this apartment been created yet? (CS cs) */ - LONG remoting_started; /* has the RPC system been started for this apartment? (LOCK) */ - struct list loaded_dlls; /* list of dlls loaded by this apartment (CS cs) */ - DWORD host_apt_tid; /* thread ID of apartment hosting objects of differing threading model (CS cs) */ - HWND host_apt_hwnd; /* handle to apartment window of host apartment (CS cs) */ - struct local_server *local_server; /* A marshallable object exposing local servers (CS cs) */ - BOOL being_destroyed; /* is currently being destroyed */ - - /* FIXME: OIDs should be given out by RPCSS */ - OID oidc; /* object ID counter, starts at 1, zero is invalid OID (CS cs) */ - - /* STA-only fields */ - HWND win; /* message window (LOCK) */ - LPMESSAGEFILTER filter; /* message filter (CS cs) */ - BOOL main; /* is this a main-threaded-apartment? (RO) */ - /* MTA-only */ - struct list usage_cookies; /* Used for refcount control with CoIncrementMTAUsage()/CoDecrementMTAUsage(). */ -}; - -struct init_spy -{ - struct list entry; - IInitializeSpy *spy; - unsigned int id; -}; +struct apartment;
/* this is what is stored in TEB->ReservedForOle */ struct oletls @@ -170,7 +66,6 @@ struct oletls DWORD spies_lock; };
- /* Global Interface Table Functions */ extern void release_std_git(void) DECLSPEC_HIDDEN; extern HRESULT StdGlobalInterfaceTable_GetFactory(LPVOID *ppv) DECLSPEC_HIDDEN; @@ -179,48 +74,9 @@ HRESULT COM_OpenKeyForCLSID(REFCLSID clsid, LPCWSTR keyname, REGSAM access, HKEY HRESULT MARSHAL_GetStandardMarshalCF(LPVOID *ppv) DECLSPEC_HIDDEN; HRESULT FTMarshalCF_Create(REFIID riid, LPVOID *ppv) DECLSPEC_HIDDEN;
-/* Stub Manager */ - -extern ULONG WINAPI stub_manager_int_release(struct stub_manager *This) DECLSPEC_HIDDEN; - -/* RPC Backend */ - -struct dispatch_params; - -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 WINAPI RPC_RegisterInterface(REFIID riid) DECLSPEC_HIDDEN; -HRESULT RPC_RegisterChannelHook(REFGUID rguid, IChannelHook *hook) DECLSPEC_HIDDEN; -void RPC_UnregisterAllChannelHooks(void) 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 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 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; -extern struct apartment * WINAPI apartment_get_current_or_mta(void) DECLSPEC_HIDDEN; -extern HRESULT WINAPI apartment_get_local_server_stream(struct apartment *apt, IStream **ret) DECLSPEC_HIDDEN; - -/* DCOM messages used by the apartment window (not compatible with native) */ -#define DM_EXECUTERPC (WM_USER + 0) /* WPARAM = 0, LPARAM = (struct dispatch_params *) */ -#define DM_HOSTOBJECT (WM_USER + 1) /* WPARAM = 0, LPARAM = (struct host_object_params *) */ - -/* - * Per-thread values are stored in the TEB on offset 0xF80 - */ - extern HRESULT WINAPI InternalTlsAllocData(struct oletls **tlsdata);
/* will create if necessary */ diff --git a/dlls/ole32/ole32.spec b/dlls/ole32/ole32.spec index 1ea57416176..cf48d043b75 100644 --- a/dlls/ole32/ole32.spec +++ b/dlls/ole32/ole32.spec @@ -64,7 +64,7 @@ @ stdcall CoQueryClientBlanket(ptr ptr ptr ptr ptr ptr ptr) combase.CoQueryClientBlanket @ stdcall CoQueryProxyBlanket(ptr ptr ptr ptr ptr ptr ptr ptr) combase.CoQueryProxyBlanket @ stub CoQueryReleaseObject -@ stdcall CoRegisterChannelHook(ptr ptr) +@ stdcall CoRegisterChannelHook(ptr ptr) combase.CoRegisterChannelHook @ stdcall CoRegisterClassObject(ptr ptr long long ptr) combase.CoRegisterClassObject @ stdcall CoRegisterInitializeSpy(ptr ptr) combase.CoRegisterInitializeSpy @ stdcall CoRegisterMallocSpy(ptr) combase.CoRegisterMallocSpy @@ -298,11 +298,3 @@ @ stdcall WriteFmtUserTypeStg(ptr long ptr) @ stub WriteOleStg @ stub WriteStringStream - -@ stdcall Internal_RPC_ExecuteCall(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 deleted file mode 100644 index a6783c45eae..00000000000 --- a/dlls/ole32/rpc.c +++ /dev/null @@ -1,1654 +0,0 @@ -/* - * RPC Manager - * - * Copyright 2001 Ove KÃ¥ven, TransGaming Technologies - * Copyright 2002 Marcus Meissner - * Copyright 2005 Mike Hearn, Rob Shearman for CodeWeavers - * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Lesser General Public - * License as published by the Free Software Foundation; either - * version 2.1 of the License, or (at your option) any later version. - * - * This library is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Lesser General Public License for more details. - * - * You should have received a copy of the GNU Lesser General Public - * License along with this library; if not, write to the Free Software - * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA - */ - -#include <stdarg.h> -#include <string.h> - -#define COBJMACROS -#define NONAMELESSUNION - -#include "windef.h" -#include "winbase.h" -#include "winuser.h" -#include "winsvc.h" -#include "objbase.h" -#include "ole2.h" -#include "rpc.h" -#include "winerror.h" -#include "winreg.h" -#include "servprov.h" - -#include "compobj_private.h" - -#include "wine/debug.h" - -WINE_DEFAULT_DEBUG_CHANNEL(ole); - -extern HRESULT WINAPI ipid_get_dispatch_params(const IPID *ipid, struct apartment **stub_apt, - struct stub_manager **manager, IRpcStubBuffer **stub, IRpcChannelBuffer **chan, IID *iid, IUnknown **iface); -extern HRESULT WINAPI start_apartment_remote_unknown(struct apartment *apt); - -static void __RPC_STUB dispatch_rpc(RPC_MESSAGE *msg); - -/* we only use one function to dispatch calls for all methods - we use the - * RPC_IF_OLE flag to tell the RPC runtime that this is the case */ -static RPC_DISPATCH_FUNCTION rpc_dispatch_table[1] = { dispatch_rpc }; /* (RO) */ -static RPC_DISPATCH_TABLE rpc_dispatch = { 1, rpc_dispatch_table }; /* (RO) */ - -static struct list registered_interfaces = LIST_INIT(registered_interfaces); /* (CS csRegIf) */ -static CRITICAL_SECTION csRegIf; -static CRITICAL_SECTION_DEBUG csRegIf_debug = -{ - 0, 0, &csRegIf, - { &csRegIf_debug.ProcessLocksList, &csRegIf_debug.ProcessLocksList }, - 0, 0, { (DWORD_PTR)(__FILE__ ": dcom registered server interfaces") } -}; -static CRITICAL_SECTION csRegIf = { &csRegIf_debug, -1, 0, 0, 0, 0 }; - -static struct list channel_hooks = LIST_INIT(channel_hooks); /* (CS csChannelHook) */ -static CRITICAL_SECTION csChannelHook; -static CRITICAL_SECTION_DEBUG csChannelHook_debug = -{ - 0, 0, &csChannelHook, - { &csChannelHook_debug.ProcessLocksList, &csChannelHook_debug.ProcessLocksList }, - 0, 0, { (DWORD_PTR)(__FILE__ ": channel hooks") } -}; -static CRITICAL_SECTION csChannelHook = { &csChannelHook_debug, -1, 0, 0, 0, 0 }; - -static WCHAR wszRpcTransport[] = {'n','c','a','l','r','p','c',0}; - - -struct registered_if -{ - struct list entry; - DWORD refs; /* ref count */ - RPC_SERVER_INTERFACE If; /* interface registered with the RPC runtime */ -}; - -/* get the pipe endpoint specified of the specified apartment */ -static inline void get_rpc_endpoint(LPWSTR endpoint, const OXID *oxid) -{ - /* FIXME: should get endpoint from rpcss */ - static const WCHAR wszEndpointFormat[] = {'\','p','i','p','e','\','O','L','E','_','%','0','8','l','x','%','0','8','l','x',0}; - wsprintfW(endpoint, wszEndpointFormat, (DWORD)(*oxid >> 32),(DWORD)*oxid); -} - -typedef struct -{ - IRpcChannelBuffer IRpcChannelBuffer_iface; - LONG refs; - - DWORD dest_context; /* returned from GetDestCtx */ - void *dest_context_data; /* returned from GetDestCtx */ -} RpcChannelBuffer; - -typedef struct -{ - RpcChannelBuffer super; /* superclass */ - - RPC_BINDING_HANDLE bind; /* handle to the remote server */ - OXID oxid; /* apartment in which the channel is valid */ - DWORD server_pid; /* id of server process */ - HANDLE event; /* cached event handle */ - IID iid; /* IID of the proxy this belongs to */ -} ClientRpcChannelBuffer; - -struct dispatch_params -{ - RPCOLEMESSAGE *msg; /* message */ - IRpcStubBuffer *stub; /* stub buffer, if applicable */ - IRpcChannelBuffer *chan; /* server channel buffer, if applicable */ - IID iid; /* ID of interface being called */ - IUnknown *iface; /* interface being called */ - HANDLE handle; /* handle that will become signaled when call finishes */ - BOOL bypass_rpcrt; /* bypass RPC runtime? */ - RPC_STATUS status; /* status (out) */ - HRESULT hr; /* hresult (out) */ -}; - -struct message_state -{ - RPC_BINDING_HANDLE binding_handle; - ULONG prefix_data_len; - SChannelHookCallInfo channel_hook_info; - BOOL bypass_rpcrt; - - /* client only */ - HWND target_hwnd; - DWORD target_tid; - struct dispatch_params params; -}; - -typedef struct -{ - ULONG conformance; /* NDR */ - GUID id; - ULONG size; - /* [size_is((size+7)&~7)] */ unsigned char data[1]; -} WIRE_ORPC_EXTENT; - -typedef struct -{ - ULONG size; - ULONG reserved; - unsigned char extent[1]; -} WIRE_ORPC_EXTENT_ARRAY; - -typedef struct -{ - ULONG version; - ULONG flags; - ULONG reserved1; - GUID cid; - unsigned char extensions[1]; -} WIRE_ORPCTHIS; - -typedef struct -{ - ULONG flags; - unsigned char extensions[1]; -} WIRE_ORPCTHAT; - -struct channel_hook_entry -{ - struct list entry; - GUID id; - IChannelHook *hook; -}; - -struct channel_hook_buffer_data -{ - GUID id; - ULONG extension_size; -}; - - -static HRESULT unmarshal_ORPCTHAT(RPC_MESSAGE *msg, ORPCTHAT *orpcthat, - ORPC_EXTENT_ARRAY *orpc_ext_array, WIRE_ORPC_EXTENT **first_wire_orpc_extent); - -/* Channel Hook Functions */ - -static ULONG ChannelHooks_ClientGetSize(SChannelHookCallInfo *info, - struct channel_hook_buffer_data **data, unsigned int *hook_count, - ULONG *extension_count) -{ - struct channel_hook_entry *entry; - ULONG total_size = 0; - unsigned int hook_index = 0; - - *hook_count = 0; - *extension_count = 0; - - EnterCriticalSection(&csChannelHook); - - LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) - (*hook_count)++; - - if (*hook_count) - *data = HeapAlloc(GetProcessHeap(), 0, *hook_count * sizeof(struct channel_hook_buffer_data)); - else - *data = NULL; - - LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) - { - ULONG extension_size = 0; - - IChannelHook_ClientGetSize(entry->hook, &entry->id, &info->iid, &extension_size); - - TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size); - - extension_size = (extension_size+7)&~7; - (*data)[hook_index].id = entry->id; - (*data)[hook_index].extension_size = extension_size; - - /* an extension is only put onto the wire if it has data to write */ - if (extension_size) - { - total_size += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[extension_size]); - (*extension_count)++; - } - - hook_index++; - } - - LeaveCriticalSection(&csChannelHook); - - return total_size; -} - -static unsigned char * ChannelHooks_ClientFillBuffer(SChannelHookCallInfo *info, - unsigned char *buffer, struct channel_hook_buffer_data *data, - unsigned int hook_count) -{ - struct channel_hook_entry *entry; - - EnterCriticalSection(&csChannelHook); - - LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) - { - unsigned int i; - ULONG extension_size = 0; - WIRE_ORPC_EXTENT *wire_orpc_extent = (WIRE_ORPC_EXTENT *)buffer; - - for (i = 0; i < hook_count; i++) - if (IsEqualGUID(&entry->id, &data[i].id)) - extension_size = data[i].extension_size; - - /* an extension is only put onto the wire if it has data to write */ - if (!extension_size) - continue; - - IChannelHook_ClientFillBuffer(entry->hook, &entry->id, &info->iid, - &extension_size, buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0])); - - TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size); - - /* FIXME: set unused portion of wire_orpc_extent->data to 0? */ - - wire_orpc_extent->conformance = (extension_size+7)&~7; - wire_orpc_extent->size = extension_size; - wire_orpc_extent->id = entry->id; - buffer += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[wire_orpc_extent->conformance]); - } - - LeaveCriticalSection(&csChannelHook); - - return buffer; -} - -static void ChannelHooks_ServerNotify(SChannelHookCallInfo *info, - DWORD lDataRep, WIRE_ORPC_EXTENT *first_wire_orpc_extent, - ULONG extension_count) -{ - struct channel_hook_entry *entry; - ULONG i; - - EnterCriticalSection(&csChannelHook); - - LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) - { - WIRE_ORPC_EXTENT *wire_orpc_extent; - for (i = 0, wire_orpc_extent = first_wire_orpc_extent; - i < extension_count; - i++, wire_orpc_extent = (WIRE_ORPC_EXTENT *)&wire_orpc_extent->data[wire_orpc_extent->conformance]) - { - if (IsEqualGUID(&entry->id, &wire_orpc_extent->id)) - break; - } - if (i == extension_count) wire_orpc_extent = NULL; - - IChannelHook_ServerNotify(entry->hook, &entry->id, &info->iid, - wire_orpc_extent ? wire_orpc_extent->size : 0, - wire_orpc_extent ? wire_orpc_extent->data : NULL, - lDataRep); - } - - LeaveCriticalSection(&csChannelHook); -} - -static ULONG ChannelHooks_ServerGetSize(SChannelHookCallInfo *info, - struct channel_hook_buffer_data **data, unsigned int *hook_count, - ULONG *extension_count) -{ - struct channel_hook_entry *entry; - ULONG total_size = 0; - unsigned int hook_index = 0; - - *hook_count = 0; - *extension_count = 0; - - EnterCriticalSection(&csChannelHook); - - LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) - (*hook_count)++; - - if (*hook_count) - *data = HeapAlloc(GetProcessHeap(), 0, *hook_count * sizeof(struct channel_hook_buffer_data)); - else - *data = NULL; - - LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) - { - ULONG extension_size = 0; - - IChannelHook_ServerGetSize(entry->hook, &entry->id, &info->iid, S_OK, - &extension_size); - - TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size); - - extension_size = (extension_size+7)&~7; - (*data)[hook_index].id = entry->id; - (*data)[hook_index].extension_size = extension_size; - - /* an extension is only put onto the wire if it has data to write */ - if (extension_size) - { - total_size += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[extension_size]); - (*extension_count)++; - } - - hook_index++; - } - - LeaveCriticalSection(&csChannelHook); - - return total_size; -} - -static unsigned char * ChannelHooks_ServerFillBuffer(SChannelHookCallInfo *info, - unsigned char *buffer, struct channel_hook_buffer_data *data, - unsigned int hook_count) -{ - struct channel_hook_entry *entry; - - EnterCriticalSection(&csChannelHook); - - LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) - { - unsigned int i; - ULONG extension_size = 0; - WIRE_ORPC_EXTENT *wire_orpc_extent = (WIRE_ORPC_EXTENT *)buffer; - - for (i = 0; i < hook_count; i++) - if (IsEqualGUID(&entry->id, &data[i].id)) - extension_size = data[i].extension_size; - - /* an extension is only put onto the wire if it has data to write */ - if (!extension_size) - continue; - - IChannelHook_ServerFillBuffer(entry->hook, &entry->id, &info->iid, - &extension_size, buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]), - S_OK); - - TRACE("%s: extension_size = %u\n", debugstr_guid(&entry->id), extension_size); - - /* FIXME: set unused portion of wire_orpc_extent->data to 0? */ - - wire_orpc_extent->conformance = (extension_size+7)&~7; - wire_orpc_extent->size = extension_size; - wire_orpc_extent->id = entry->id; - buffer += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[wire_orpc_extent->conformance]); - } - - LeaveCriticalSection(&csChannelHook); - - return buffer; -} - -static void ChannelHooks_ClientNotify(SChannelHookCallInfo *info, - DWORD lDataRep, WIRE_ORPC_EXTENT *first_wire_orpc_extent, - ULONG extension_count, HRESULT hrFault) -{ - struct channel_hook_entry *entry; - ULONG i; - - EnterCriticalSection(&csChannelHook); - - LIST_FOR_EACH_ENTRY(entry, &channel_hooks, struct channel_hook_entry, entry) - { - WIRE_ORPC_EXTENT *wire_orpc_extent; - for (i = 0, wire_orpc_extent = first_wire_orpc_extent; - i < extension_count; - i++, wire_orpc_extent = (WIRE_ORPC_EXTENT *)&wire_orpc_extent->data[wire_orpc_extent->conformance]) - { - if (IsEqualGUID(&entry->id, &wire_orpc_extent->id)) - break; - } - if (i == extension_count) wire_orpc_extent = NULL; - - IChannelHook_ClientNotify(entry->hook, &entry->id, &info->iid, - wire_orpc_extent ? wire_orpc_extent->size : 0, - wire_orpc_extent ? wire_orpc_extent->data : NULL, - lDataRep, hrFault); - } - - LeaveCriticalSection(&csChannelHook); -} - -HRESULT RPC_RegisterChannelHook(REFGUID rguid, IChannelHook *hook) -{ - struct channel_hook_entry *entry; - - TRACE("(%s, %p)\n", debugstr_guid(rguid), hook); - - entry = HeapAlloc(GetProcessHeap(), 0, sizeof(*entry)); - if (!entry) - return E_OUTOFMEMORY; - - entry->id = *rguid; - entry->hook = hook; - IChannelHook_AddRef(hook); - - EnterCriticalSection(&csChannelHook); - list_add_tail(&channel_hooks, &entry->entry); - LeaveCriticalSection(&csChannelHook); - - return S_OK; -} - -void RPC_UnregisterAllChannelHooks(void) -{ - struct channel_hook_entry *cursor; - struct channel_hook_entry *cursor2; - - EnterCriticalSection(&csChannelHook); - LIST_FOR_EACH_ENTRY_SAFE(cursor, cursor2, &channel_hooks, struct channel_hook_entry, entry) - HeapFree(GetProcessHeap(), 0, cursor); - LeaveCriticalSection(&csChannelHook); - DeleteCriticalSection(&csChannelHook); - DeleteCriticalSection(&csRegIf); -} - -/* RPC Channel Buffer Functions */ - -static HRESULT WINAPI RpcChannelBuffer_QueryInterface(IRpcChannelBuffer *iface, REFIID riid, LPVOID *ppv) -{ - *ppv = NULL; - if (IsEqualIID(riid,&IID_IRpcChannelBuffer) || IsEqualIID(riid,&IID_IUnknown)) - { - *ppv = iface; - IRpcChannelBuffer_AddRef(iface); - return S_OK; - } - return E_NOINTERFACE; -} - -static ULONG WINAPI RpcChannelBuffer_AddRef(LPRPCCHANNELBUFFER iface) -{ - RpcChannelBuffer *This = (RpcChannelBuffer *)iface; - return InterlockedIncrement(&This->refs); -} - -static ULONG WINAPI ServerRpcChannelBuffer_Release(LPRPCCHANNELBUFFER iface) -{ - RpcChannelBuffer *This = (RpcChannelBuffer *)iface; - ULONG ref; - - ref = InterlockedDecrement(&This->refs); - if (ref) - return ref; - - HeapFree(GetProcessHeap(), 0, This); - return 0; -} - -static ULONG WINAPI ClientRpcChannelBuffer_Release(LPRPCCHANNELBUFFER iface) -{ - ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface; - ULONG ref; - - ref = InterlockedDecrement(&This->super.refs); - if (ref) - return ref; - - if (This->event) CloseHandle(This->event); - RpcBindingFree(&This->bind); - HeapFree(GetProcessHeap(), 0, This); - return 0; -} - -static HRESULT WINAPI ServerRpcChannelBuffer_GetBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg, REFIID riid) -{ - RpcChannelBuffer *This = (RpcChannelBuffer *)iface; - RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg; - RPC_STATUS status; - ORPCTHAT *orpcthat; - struct message_state *message_state; - ULONG extensions_size; - struct channel_hook_buffer_data *channel_hook_data; - unsigned int channel_hook_count; - ULONG extension_count; - - TRACE("(%p)->(%p,%s)\n", This, olemsg, debugstr_guid(riid)); - - message_state = msg->Handle; - /* restore the binding handle and the real start of data */ - msg->Handle = message_state->binding_handle; - msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len; - - extensions_size = ChannelHooks_ServerGetSize(&message_state->channel_hook_info, - &channel_hook_data, &channel_hook_count, &extension_count); - - msg->BufferLength += FIELD_OFFSET(WIRE_ORPCTHAT, extensions) + sizeof(DWORD); - if (extensions_size) - { - msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent[2*sizeof(DWORD) + extensions_size]); - if (extension_count & 1) - msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]); - } - - if (message_state->bypass_rpcrt) - { - msg->Buffer = HeapAlloc(GetProcessHeap(), 0, msg->BufferLength); - if (msg->Buffer) - status = RPC_S_OK; - else - { - HeapFree(GetProcessHeap(), 0, channel_hook_data); - return E_OUTOFMEMORY; - } - } - else - status = I_RpcGetBuffer(msg); - - orpcthat = msg->Buffer; - msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPCTHAT, extensions); - - orpcthat->flags = ORPCF_NULL /* FIXME? */; - - /* NDR representation of orpcthat->extensions */ - *(DWORD *)msg->Buffer = extensions_size ? 1 : 0; - msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); - - if (extensions_size) - { - WIRE_ORPC_EXTENT_ARRAY *orpc_extent_array = msg->Buffer; - orpc_extent_array->size = extension_count; - orpc_extent_array->reserved = 0; - msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent); - /* NDR representation of orpc_extent_array->extent */ - *(DWORD *)msg->Buffer = 1; - msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); - /* NDR representation of [size_is] attribute of orpc_extent_array->extent */ - *(DWORD *)msg->Buffer = (extension_count + 1) & ~1; - msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); - - msg->Buffer = ChannelHooks_ServerFillBuffer(&message_state->channel_hook_info, - msg->Buffer, channel_hook_data, channel_hook_count); - - /* we must add a dummy extension if there is an odd extension - * count to meet the contract specified by the size_is attribute */ - if (extension_count & 1) - { - WIRE_ORPC_EXTENT *wire_orpc_extent = msg->Buffer; - wire_orpc_extent->conformance = 0; - wire_orpc_extent->id = GUID_NULL; - wire_orpc_extent->size = 0; - msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]); - } - } - - HeapFree(GetProcessHeap(), 0, channel_hook_data); - - /* store the prefixed data length so that we can restore the real buffer - * later */ - message_state->prefix_data_len = (char *)msg->Buffer - (char *)orpcthat; - msg->BufferLength -= message_state->prefix_data_len; - /* save away the message state again */ - msg->Handle = message_state; - - TRACE("-- %d\n", status); - - return HRESULT_FROM_WIN32(status); -} - -static HANDLE ClientRpcChannelBuffer_GetEventHandle(ClientRpcChannelBuffer *This) -{ - HANDLE event = InterlockedExchangePointer(&This->event, NULL); - - /* Note: must be auto-reset event so we can reuse it without a call - * to ResetEvent */ - if (!event) event = CreateEventW(NULL, FALSE, FALSE, NULL); - - return event; -} - -static void ClientRpcChannelBuffer_ReleaseEventHandle(ClientRpcChannelBuffer *This, HANDLE event) -{ - if (InterlockedCompareExchangePointer(&This->event, event, NULL)) - /* already a handle cached in This */ - CloseHandle(event); -} - -static HRESULT WINAPI ClientRpcChannelBuffer_GetBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg, REFIID riid) -{ - ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface; - RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg; - RPC_CLIENT_INTERFACE *cif; - RPC_STATUS status; - ORPCTHIS *orpcthis; - struct message_state *message_state; - ULONG extensions_size; - struct channel_hook_buffer_data *channel_hook_data; - unsigned int channel_hook_count; - ULONG extension_count; - IPID ipid; - HRESULT hr; - struct apartment *apt = NULL; - - TRACE("(%p)->(%p,%s)\n", This, olemsg, debugstr_guid(riid)); - - cif = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(RPC_CLIENT_INTERFACE)); - if (!cif) - return E_OUTOFMEMORY; - - message_state = HeapAlloc(GetProcessHeap(), 0, sizeof(*message_state)); - if (!message_state) - { - HeapFree(GetProcessHeap(), 0, cif); - return E_OUTOFMEMORY; - } - - cif->Length = sizeof(RPC_CLIENT_INTERFACE); - /* RPC interface ID = COM interface ID */ - cif->InterfaceId.SyntaxGUID = This->iid; - /* COM objects always have a version of 0.0 */ - cif->InterfaceId.SyntaxVersion.MajorVersion = 0; - cif->InterfaceId.SyntaxVersion.MinorVersion = 0; - msg->Handle = This->bind; - msg->RpcInterfaceInformation = cif; - - message_state->prefix_data_len = 0; - message_state->binding_handle = This->bind; - - message_state->channel_hook_info.iid = *riid; - message_state->channel_hook_info.cbSize = sizeof(message_state->channel_hook_info); - CoGetCurrentLogicalThreadId(&message_state->channel_hook_info.uCausality); - message_state->channel_hook_info.dwServerPid = This->server_pid; - message_state->channel_hook_info.iMethod = msg->ProcNum & ~RPC_FLAGS_VALID_BIT; - message_state->channel_hook_info.pObject = NULL; /* only present on server-side */ - message_state->target_hwnd = NULL; - message_state->target_tid = 0; - memset(&message_state->params, 0, sizeof(message_state->params)); - - extensions_size = ChannelHooks_ClientGetSize(&message_state->channel_hook_info, - &channel_hook_data, &channel_hook_count, &extension_count); - - msg->BufferLength += FIELD_OFFSET(WIRE_ORPCTHIS, extensions) + sizeof(DWORD); - if (extensions_size) - { - msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent[2*sizeof(DWORD) + extensions_size]); - if (extension_count & 1) - msg->BufferLength += FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]); - } - - RpcBindingInqObject(message_state->binding_handle, &ipid); - hr = ipid_get_dispatch_params(&ipid, &apt, NULL, &message_state->params.stub, - &message_state->params.chan, - &message_state->params.iid, - &message_state->params.iface); - if (hr == S_OK) - { - /* stub, chan, iface and iid are unneeded in multi-threaded case as we go - * via the RPC runtime */ - if (apt->multi_threaded) - { - IRpcStubBuffer_Release(message_state->params.stub); - message_state->params.stub = NULL; - IRpcChannelBuffer_Release(message_state->params.chan); - message_state->params.chan = NULL; - message_state->params.iface = NULL; - } - else - { - message_state->params.bypass_rpcrt = TRUE; - message_state->target_hwnd = apartment_getwindow(apt); - message_state->target_tid = apt->tid; - /* we assume later on that this being non-NULL is the indicator that - * means call directly instead of going through RPC runtime */ - if (!message_state->target_hwnd) - ERR("window for apartment %s is NULL\n", wine_dbgstr_longlong(apt->oxid)); - } - } - if (apt) apartment_release(apt); - message_state->params.handle = ClientRpcChannelBuffer_GetEventHandle(This); - /* Note: message_state->params.msg is initialised in - * ClientRpcChannelBuffer_SendReceive */ - - /* shortcut the RPC runtime */ - if (message_state->target_hwnd) - { - msg->Buffer = HeapAlloc(GetProcessHeap(), 0, msg->BufferLength); - if (msg->Buffer) - status = RPC_S_OK; - else - status = ERROR_OUTOFMEMORY; - } - else - status = I_RpcGetBuffer(msg); - - msg->Handle = message_state; - - if (status == RPC_S_OK) - { - orpcthis = msg->Buffer; - msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPCTHIS, extensions); - - orpcthis->version.MajorVersion = COM_MAJOR_VERSION; - orpcthis->version.MinorVersion = COM_MINOR_VERSION; - orpcthis->flags = message_state->channel_hook_info.dwServerPid ? ORPCF_LOCAL : ORPCF_NULL; - orpcthis->reserved1 = 0; - orpcthis->cid = message_state->channel_hook_info.uCausality; - - /* NDR representation of orpcthis->extensions */ - *(DWORD *)msg->Buffer = extensions_size ? 1 : 0; - msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); - - if (extensions_size) - { - ORPC_EXTENT_ARRAY *orpc_extent_array = msg->Buffer; - orpc_extent_array->size = extension_count; - orpc_extent_array->reserved = 0; - msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent); - /* NDR representation of orpc_extent_array->extent */ - *(DWORD *)msg->Buffer = 1; - msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); - /* NDR representation of [size_is] attribute of orpc_extent_array->extent */ - *(DWORD *)msg->Buffer = (extension_count + 1) & ~1; - msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); - - msg->Buffer = ChannelHooks_ClientFillBuffer(&message_state->channel_hook_info, - msg->Buffer, channel_hook_data, channel_hook_count); - - /* we must add a dummy extension if there is an odd extension - * count to meet the contract specified by the size_is attribute */ - if (extension_count & 1) - { - WIRE_ORPC_EXTENT *wire_orpc_extent = msg->Buffer; - wire_orpc_extent->conformance = 0; - wire_orpc_extent->id = GUID_NULL; - wire_orpc_extent->size = 0; - msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT, data[0]); - } - } - - /* store the prefixed data length so that we can restore the real buffer - * pointer in ClientRpcChannelBuffer_SendReceive. */ - message_state->prefix_data_len = (char *)msg->Buffer - (char *)orpcthis; - msg->BufferLength -= message_state->prefix_data_len; - } - - HeapFree(GetProcessHeap(), 0, channel_hook_data); - - TRACE("-- %d\n", status); - - return HRESULT_FROM_WIN32(status); -} - -static HRESULT WINAPI ServerRpcChannelBuffer_SendReceive(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE *olemsg, ULONG *pstatus) -{ - FIXME("stub\n"); - return E_NOTIMPL; -} - -/* this thread runs an outgoing RPC */ -static DWORD WINAPI rpc_sendreceive_thread(LPVOID param) -{ - struct dispatch_params *data = param; - - /* Note: I_RpcSendReceive doesn't raise exceptions like the higher-level - * RPC functions do */ - data->status = I_RpcSendReceive((RPC_MESSAGE *)data->msg); - - TRACE("completed with status 0x%x\n", data->status); - - SetEvent(data->handle); - - return 0; -} - -static inline HRESULT ClientRpcChannelBuffer_IsCorrectApartment(ClientRpcChannelBuffer *This, struct apartment *apt) -{ - OXID oxid; - if (!apt) - return S_FALSE; - if (apartment_getoxid(apt, &oxid) != S_OK) - return S_FALSE; - if (This->oxid != oxid) - return S_FALSE; - return S_OK; -} - -static HRESULT WINAPI ClientRpcChannelBuffer_SendReceive(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE *olemsg, ULONG *pstatus) -{ - ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface; - HRESULT hr; - RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg; - RPC_STATUS status; - DWORD index; - struct message_state *message_state; - ORPCTHAT orpcthat; - ORPC_EXTENT_ARRAY orpc_ext_array; - WIRE_ORPC_EXTENT *first_wire_orpc_extent = NULL; - HRESULT hrFault = S_OK; - struct apartment *apt = apartment_get_current_or_mta(); - - TRACE("(%p) iMethod=%d\n", olemsg, olemsg->iMethod); - - hr = ClientRpcChannelBuffer_IsCorrectApartment(This, apt); - if (hr != S_OK) - { - ERR("called from wrong apartment, should have been 0x%s\n", - wine_dbgstr_longlong(This->oxid)); - if (apt) apartment_release(apt); - return RPC_E_WRONG_THREAD; - } - /* This situation should be impossible in multi-threaded apartments, - * because the calling thread isn't re-enterable. - * Note: doing a COM call during the processing of a sent message is - * only disallowed if a client call is already being waited for - * completion */ - if (!apt->multi_threaded && - COM_CurrentInfo()->pending_call_count_client && - InSendMessage()) - { - ERR("can't make an outgoing COM call in response to a sent message\n"); - apartment_release(apt); - return RPC_E_CANTCALLOUT_ININPUTSYNCCALL; - } - - message_state = msg->Handle; - /* restore the binding handle and the real start of data */ - msg->Handle = message_state->binding_handle; - msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len; - msg->BufferLength += message_state->prefix_data_len; - - /* Note: this is an optimization in the Microsoft OLE runtime that we need - * to copy, as shown by the test_no_couninitialize_client test. without - * short-circuiting the RPC runtime in the case below, the test will - * deadlock on the loader lock due to the RPC runtime needing to create - * a thread to process the RPC when this function is called indirectly - * from DllMain */ - - message_state->params.msg = olemsg; - if (message_state->params.bypass_rpcrt) - { - TRACE("Calling apartment thread 0x%08x...\n", message_state->target_tid); - - msg->ProcNum &= ~RPC_FLAGS_VALID_BIT; - - if (!PostMessageW(message_state->target_hwnd, DM_EXECUTERPC, 0, - (LPARAM)&message_state->params)) - { - ERR("PostMessage failed with error %u\n", GetLastError()); - - /* Note: message_state->params.iface doesn't have a reference and - * so doesn't need to be released */ - - hr = HRESULT_FROM_WIN32(GetLastError()); - } - } - else - { - /* we use a separate thread here because we need to be able to - * pump the message loop in the application thread: if we do not, - * any windows created by this thread will hang and RPCs that try - * and re-enter this STA from an incoming server thread will - * deadlock. InstallShield is an example of that. - */ - if (!QueueUserWorkItem(rpc_sendreceive_thread, &message_state->params, WT_EXECUTEDEFAULT)) - { - ERR("QueueUserWorkItem failed with error %u\n", GetLastError()); - hr = E_UNEXPECTED; - } - else - hr = S_OK; - } - - if (hr == S_OK) - { - if (WaitForSingleObject(message_state->params.handle, 0)) - { - COM_CurrentInfo()->pending_call_count_client++; - hr = CoWaitForMultipleHandles(0, INFINITE, 1, &message_state->params.handle, &index); - COM_CurrentInfo()->pending_call_count_client--; - } - } - ClientRpcChannelBuffer_ReleaseEventHandle(This, message_state->params.handle); - - /* for WM shortcut, faults are returned in params->hr */ - if (hr == S_OK) - hrFault = message_state->params.hr; - - status = message_state->params.status; - - orpcthat.flags = ORPCF_NULL; - orpcthat.extensions = NULL; - - TRACE("RPC call status: 0x%x\n", status); - if (status != RPC_S_OK) - hr = HRESULT_FROM_WIN32(status); - - TRACE("hrFault = 0x%08x\n", hrFault); - - /* FIXME: this condition should be - * "hr == S_OK && (!hrFault || msg->BufferLength > FIELD_OFFSET(ORPCTHAT, extensions) + 4)" - * but we don't currently reset the message length for PostMessage - * dispatched calls */ - if (hr == S_OK && hrFault == S_OK) - { - HRESULT hr2; - char *original_buffer = msg->Buffer; - - /* handle ORPCTHAT and client extensions */ - - hr2 = unmarshal_ORPCTHAT(msg, &orpcthat, &orpc_ext_array, &first_wire_orpc_extent); - if (FAILED(hr2)) - hr = hr2; - - message_state->prefix_data_len = (char *)msg->Buffer - original_buffer; - msg->BufferLength -= message_state->prefix_data_len; - } - else - message_state->prefix_data_len = 0; - - if (hr == S_OK) - { - ChannelHooks_ClientNotify(&message_state->channel_hook_info, - msg->DataRepresentation, - first_wire_orpc_extent, - orpcthat.extensions && first_wire_orpc_extent ? orpcthat.extensions->size : 0, - hrFault); - } - - /* save away the message state again */ - msg->Handle = message_state; - - if (pstatus) *pstatus = status; - - if (hr == S_OK) - hr = hrFault; - - TRACE("-- 0x%08x\n", hr); - - apartment_release(apt); - return hr; -} - -static HRESULT WINAPI ServerRpcChannelBuffer_FreeBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg) -{ - RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg; - RPC_STATUS status; - struct message_state *message_state; - - TRACE("(%p)\n", msg); - - message_state = msg->Handle; - /* restore the binding handle and the real start of data */ - msg->Handle = message_state->binding_handle; - msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len; - msg->BufferLength += message_state->prefix_data_len; - message_state->prefix_data_len = 0; - - if (message_state->bypass_rpcrt) - { - HeapFree(GetProcessHeap(), 0, msg->Buffer); - status = RPC_S_OK; - } - else - status = I_RpcFreeBuffer(msg); - - msg->Handle = message_state; - - TRACE("-- %d\n", status); - - return HRESULT_FROM_WIN32(status); -} - -static HRESULT WINAPI ClientRpcChannelBuffer_FreeBuffer(LPRPCCHANNELBUFFER iface, RPCOLEMESSAGE* olemsg) -{ - RPC_MESSAGE *msg = (RPC_MESSAGE *)olemsg; - RPC_STATUS status; - struct message_state *message_state; - - TRACE("(%p)\n", msg); - - message_state = msg->Handle; - /* restore the binding handle and the real start of data */ - msg->Handle = message_state->binding_handle; - msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len; - msg->BufferLength += message_state->prefix_data_len; - - if (message_state->params.bypass_rpcrt) - { - HeapFree(GetProcessHeap(), 0, msg->Buffer); - status = RPC_S_OK; - } - else - status = I_RpcFreeBuffer(msg); - - HeapFree(GetProcessHeap(), 0, msg->RpcInterfaceInformation); - msg->RpcInterfaceInformation = NULL; - - if (message_state->params.stub) - IRpcStubBuffer_Release(message_state->params.stub); - if (message_state->params.chan) - IRpcChannelBuffer_Release(message_state->params.chan); - HeapFree(GetProcessHeap(), 0, message_state); - - TRACE("-- %d\n", status); - - return HRESULT_FROM_WIN32(status); -} - -static HRESULT WINAPI ClientRpcChannelBuffer_GetDestCtx(LPRPCCHANNELBUFFER iface, DWORD* pdwDestContext, void** ppvDestContext) -{ - ClientRpcChannelBuffer *This = (ClientRpcChannelBuffer *)iface; - - TRACE("(%p,%p)\n", pdwDestContext, ppvDestContext); - - *pdwDestContext = This->super.dest_context; - *ppvDestContext = This->super.dest_context_data; - - return S_OK; -} - -static HRESULT WINAPI ServerRpcChannelBuffer_GetDestCtx(LPRPCCHANNELBUFFER iface, DWORD* dest_context, void** dest_context_data) -{ - RpcChannelBuffer *This = (RpcChannelBuffer *)iface; - - TRACE("(%p,%p)\n", dest_context, dest_context_data); - - *dest_context = This->dest_context; - *dest_context_data = This->dest_context_data; - return S_OK; -} - -static HRESULT WINAPI RpcChannelBuffer_IsConnected(LPRPCCHANNELBUFFER iface) -{ - TRACE("()\n"); - /* native does nothing too */ - return S_OK; -} - -static const IRpcChannelBufferVtbl ClientRpcChannelBufferVtbl = -{ - RpcChannelBuffer_QueryInterface, - RpcChannelBuffer_AddRef, - ClientRpcChannelBuffer_Release, - ClientRpcChannelBuffer_GetBuffer, - ClientRpcChannelBuffer_SendReceive, - ClientRpcChannelBuffer_FreeBuffer, - ClientRpcChannelBuffer_GetDestCtx, - RpcChannelBuffer_IsConnected -}; - -static const IRpcChannelBufferVtbl ServerRpcChannelBufferVtbl = -{ - RpcChannelBuffer_QueryInterface, - RpcChannelBuffer_AddRef, - ServerRpcChannelBuffer_Release, - ServerRpcChannelBuffer_GetBuffer, - ServerRpcChannelBuffer_SendReceive, - ServerRpcChannelBuffer_FreeBuffer, - ServerRpcChannelBuffer_GetDestCtx, - RpcChannelBuffer_IsConnected -}; - -/* returns a channel buffer for proxies */ -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) -{ - ClientRpcChannelBuffer *This; - WCHAR endpoint[200]; - RPC_BINDING_HANDLE bind; - RPC_STATUS status; - LPWSTR string_binding; - - /* FIXME: get the endpoint from oxid_info->psa instead */ - get_rpc_endpoint(endpoint, oxid); - - TRACE("proxy pipe: connecting to endpoint: %s\n", debugstr_w(endpoint)); - - status = RpcStringBindingComposeW( - NULL, - wszRpcTransport, - NULL, - endpoint, - NULL, - &string_binding); - - if (status == RPC_S_OK) - { - status = RpcBindingFromStringBindingW(string_binding, &bind); - - if (status == RPC_S_OK) - { - IPID ipid2 = *ipid; /* why can't RpcBindingSetObject take a const? */ - status = RpcBindingSetObject(bind, &ipid2); - if (status != RPC_S_OK) - RpcBindingFree(&bind); - } - - RpcStringFreeW(&string_binding); - } - - if (status != RPC_S_OK) - { - ERR("Couldn't get binding for endpoint %s, status = %d\n", debugstr_w(endpoint), status); - return HRESULT_FROM_WIN32(status); - } - - This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); - if (!This) - { - RpcBindingFree(&bind); - return E_OUTOFMEMORY; - } - - This->super.IRpcChannelBuffer_iface.lpVtbl = &ClientRpcChannelBufferVtbl; - This->super.refs = 1; - This->super.dest_context = dest_context; - This->super.dest_context_data = dest_context_data; - This->bind = bind; - apartment_getoxid(apt, &This->oxid); - This->server_pid = oxid_info->dwPid; - This->event = NULL; - This->iid = *iid; - - *chan = &This->super.IRpcChannelBuffer_iface; - - return S_OK; -} - -HRESULT WINAPI RPC_CreateServerChannel(DWORD dest_context, void *dest_context_data, IRpcChannelBuffer **chan) -{ - RpcChannelBuffer *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); - if (!This) - return E_OUTOFMEMORY; - - This->IRpcChannelBuffer_iface.lpVtbl = &ServerRpcChannelBufferVtbl; - This->refs = 1; - This->dest_context = dest_context; - This->dest_context_data = dest_context_data; - - *chan = &This->IRpcChannelBuffer_iface; - - return S_OK; -} - -/* unmarshals ORPC_EXTENT_ARRAY according to NDR rules, but doesn't allocate - * any memory */ -static HRESULT unmarshal_ORPC_EXTENT_ARRAY(RPC_MESSAGE *msg, const char *end, - ORPC_EXTENT_ARRAY *extensions, - WIRE_ORPC_EXTENT **first_wire_orpc_extent) -{ - DWORD pointer_id; - DWORD i; - - memcpy(extensions, msg->Buffer, FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent)); - msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPC_EXTENT_ARRAY, extent); - - if ((const char *)msg->Buffer + 2 * sizeof(DWORD) > end) - return RPC_E_INVALID_HEADER; - - pointer_id = *(DWORD *)msg->Buffer; - msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); - extensions->extent = NULL; - - if (pointer_id) - { - WIRE_ORPC_EXTENT *wire_orpc_extent; - - /* conformance */ - if (*(DWORD *)msg->Buffer != ((extensions->size+1)&~1)) - return RPC_S_INVALID_BOUND; - - msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); - - /* arbitrary limit for security (don't know what native does) */ - if (extensions->size > 256) - { - ERR("too many extensions: %d\n", extensions->size); - return RPC_S_INVALID_BOUND; - } - - *first_wire_orpc_extent = wire_orpc_extent = msg->Buffer; - for (i = 0; i < ((extensions->size+1)&~1); i++) - { - if ((const char *)&wire_orpc_extent->data[0] > end) - return RPC_S_INVALID_BOUND; - if (wire_orpc_extent->conformance != ((wire_orpc_extent->size+7)&~7)) - return RPC_S_INVALID_BOUND; - if ((const char *)&wire_orpc_extent->data[wire_orpc_extent->conformance] > end) - return RPC_S_INVALID_BOUND; - TRACE("size %u, guid %s\n", wire_orpc_extent->size, debugstr_guid(&wire_orpc_extent->id)); - wire_orpc_extent = (WIRE_ORPC_EXTENT *)&wire_orpc_extent->data[wire_orpc_extent->conformance]; - } - msg->Buffer = wire_orpc_extent; - } - - return S_OK; -} - -/* unmarshals ORPCTHIS according to NDR rules, but doesn't allocate any memory */ -static HRESULT unmarshal_ORPCTHIS(RPC_MESSAGE *msg, ORPCTHIS *orpcthis, - ORPC_EXTENT_ARRAY *orpc_ext_array, WIRE_ORPC_EXTENT **first_wire_orpc_extent) -{ - const char *end = (char *)msg->Buffer + msg->BufferLength; - - *first_wire_orpc_extent = NULL; - - if (msg->BufferLength < FIELD_OFFSET(WIRE_ORPCTHIS, extensions) + sizeof(DWORD)) - { - ERR("invalid buffer length\n"); - return RPC_E_INVALID_HEADER; - } - - memcpy(orpcthis, msg->Buffer, FIELD_OFFSET(WIRE_ORPCTHIS, extensions)); - msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPCTHIS, extensions); - - if ((const char *)msg->Buffer + sizeof(DWORD) > end) - return RPC_E_INVALID_HEADER; - - if (*(DWORD *)msg->Buffer) - orpcthis->extensions = orpc_ext_array; - else - orpcthis->extensions = NULL; - - msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); - - if (orpcthis->extensions) - { - HRESULT hr = unmarshal_ORPC_EXTENT_ARRAY(msg, end, orpc_ext_array, - first_wire_orpc_extent); - if (FAILED(hr)) - return hr; - } - - if ((orpcthis->version.MajorVersion != COM_MAJOR_VERSION) || - (orpcthis->version.MinorVersion > COM_MINOR_VERSION)) - { - ERR("COM version {%d, %d} not supported\n", - orpcthis->version.MajorVersion, orpcthis->version.MinorVersion); - return RPC_E_VERSION_MISMATCH; - } - - if (orpcthis->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4)) - { - ERR("invalid flags 0x%x\n", orpcthis->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4)); - return RPC_E_INVALID_HEADER; - } - - return S_OK; -} - -static HRESULT unmarshal_ORPCTHAT(RPC_MESSAGE *msg, ORPCTHAT *orpcthat, - ORPC_EXTENT_ARRAY *orpc_ext_array, WIRE_ORPC_EXTENT **first_wire_orpc_extent) -{ - const char *end = (char *)msg->Buffer + msg->BufferLength; - - *first_wire_orpc_extent = NULL; - - if (msg->BufferLength < FIELD_OFFSET(WIRE_ORPCTHAT, extensions) + sizeof(DWORD)) - { - ERR("invalid buffer length\n"); - return RPC_E_INVALID_HEADER; - } - - memcpy(orpcthat, msg->Buffer, FIELD_OFFSET(WIRE_ORPCTHAT, extensions)); - msg->Buffer = (char *)msg->Buffer + FIELD_OFFSET(WIRE_ORPCTHAT, extensions); - - if ((const char *)msg->Buffer + sizeof(DWORD) > end) - return RPC_E_INVALID_HEADER; - - if (*(DWORD *)msg->Buffer) - orpcthat->extensions = orpc_ext_array; - else - orpcthat->extensions = NULL; - - msg->Buffer = (char *)msg->Buffer + sizeof(DWORD); - - if (orpcthat->extensions) - { - HRESULT hr = unmarshal_ORPC_EXTENT_ARRAY(msg, end, orpc_ext_array, - first_wire_orpc_extent); - if (FAILED(hr)) - return hr; - } - - if (orpcthat->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4)) - { - ERR("invalid flags 0x%x\n", orpcthat->flags & ~(ORPCF_LOCAL|ORPCF_RESERVED1|ORPCF_RESERVED2|ORPCF_RESERVED3|ORPCF_RESERVED4)); - return RPC_E_INVALID_HEADER; - } - - return S_OK; -} - -void WINAPI Internal_RPC_ExecuteCall(struct dispatch_params *params) -{ - struct message_state *message_state = NULL; - RPC_MESSAGE *msg = (RPC_MESSAGE *)params->msg; - char *original_buffer = msg->Buffer; - ORPCTHIS orpcthis; - ORPC_EXTENT_ARRAY orpc_ext_array; - WIRE_ORPC_EXTENT *first_wire_orpc_extent; - GUID old_causality_id; - - /* handle ORPCTHIS and server extensions */ - - params->hr = unmarshal_ORPCTHIS(msg, &orpcthis, &orpc_ext_array, &first_wire_orpc_extent); - if (params->hr != S_OK) - { - msg->Buffer = original_buffer; - goto exit; - } - - message_state = HeapAlloc(GetProcessHeap(), 0, sizeof(*message_state)); - if (!message_state) - { - params->hr = E_OUTOFMEMORY; - msg->Buffer = original_buffer; - goto exit; - } - - message_state->prefix_data_len = (char *)msg->Buffer - original_buffer; - message_state->binding_handle = msg->Handle; - message_state->bypass_rpcrt = params->bypass_rpcrt; - - message_state->channel_hook_info.iid = params->iid; - message_state->channel_hook_info.cbSize = sizeof(message_state->channel_hook_info); - message_state->channel_hook_info.uCausality = orpcthis.cid; - message_state->channel_hook_info.dwServerPid = GetCurrentProcessId(); - message_state->channel_hook_info.iMethod = msg->ProcNum; - message_state->channel_hook_info.pObject = params->iface; - - if (orpcthis.extensions && first_wire_orpc_extent && - orpcthis.extensions->size) - ChannelHooks_ServerNotify(&message_state->channel_hook_info, msg->DataRepresentation, first_wire_orpc_extent, orpcthis.extensions->size); - - msg->Handle = message_state; - msg->BufferLength -= message_state->prefix_data_len; - - /* call message filter */ - - if (COM_CurrentApt()->filter) - { - DWORD handlecall; - INTERFACEINFO interface_info; - CALLTYPE calltype; - - interface_info.pUnk = params->iface; - interface_info.iid = params->iid; - interface_info.wMethod = msg->ProcNum; - - if (IsEqualGUID(&orpcthis.cid, &COM_CurrentInfo()->causality_id)) - calltype = CALLTYPE_NESTED; - else if (COM_CurrentInfo()->pending_call_count_server == 0) - calltype = CALLTYPE_TOPLEVEL; - else - calltype = CALLTYPE_TOPLEVEL_CALLPENDING; - - handlecall = IMessageFilter_HandleInComingCall(COM_CurrentApt()->filter, - calltype, - UlongToHandle(GetCurrentProcessId()), - 0 /* FIXME */, - &interface_info); - TRACE("IMessageFilter_HandleInComingCall returned %d\n", handlecall); - switch (handlecall) - { - case SERVERCALL_REJECTED: - params->hr = RPC_E_CALL_REJECTED; - goto exit_reset_state; - case SERVERCALL_RETRYLATER: -#if 0 /* FIXME: handle retries on the client side before enabling this code */ - params->hr = RPC_E_RETRY; - goto exit_reset_state; -#else - FIXME("retry call later not implemented\n"); - break; -#endif - case SERVERCALL_ISHANDLED: - default: - break; - } - } - - /* invoke the method */ - - /* save the old causality ID - note: any calls executed while processing - * messages received during the SendReceive will appear to originate from - * this call - this should be checked with what Windows does */ - old_causality_id = COM_CurrentInfo()->causality_id; - COM_CurrentInfo()->causality_id = orpcthis.cid; - COM_CurrentInfo()->pending_call_count_server++; - params->hr = IRpcStubBuffer_Invoke(params->stub, params->msg, params->chan); - COM_CurrentInfo()->pending_call_count_server--; - COM_CurrentInfo()->causality_id = old_causality_id; - - /* the invoke allocated a new buffer, so free the old one */ - if (message_state->bypass_rpcrt && original_buffer != msg->Buffer) - HeapFree(GetProcessHeap(), 0, original_buffer); - -exit_reset_state: - message_state = msg->Handle; - msg->Handle = message_state->binding_handle; - msg->Buffer = (char *)msg->Buffer - message_state->prefix_data_len; - msg->BufferLength += message_state->prefix_data_len; - -exit: - HeapFree(GetProcessHeap(), 0, message_state); - if (params->handle) SetEvent(params->handle); -} - -static void __RPC_STUB dispatch_rpc(RPC_MESSAGE *msg) -{ - struct dispatch_params *params; - struct stub_manager *stub_manager; - struct apartment *apt; - IPID ipid; - HRESULT hr; - - RpcBindingInqObject(msg->Handle, &ipid); - - TRACE("ipid = %s, iMethod = %d\n", debugstr_guid(&ipid), msg->ProcNum); - - params = HeapAlloc(GetProcessHeap(), 0, sizeof(*params)); - if (!params) - { - RpcRaiseException(E_OUTOFMEMORY); - return; - } - - hr = ipid_get_dispatch_params(&ipid, &apt, &stub_manager, ¶ms->stub, ¶ms->chan, - ¶ms->iid, ¶ms->iface); - if (hr != S_OK) - { - ERR("no apartment found for ipid %s\n", debugstr_guid(&ipid)); - HeapFree(GetProcessHeap(), 0, params); - RpcRaiseException(hr); - return; - } - - params->msg = (RPCOLEMESSAGE *)msg; - params->status = RPC_S_OK; - params->hr = S_OK; - params->handle = NULL; - params->bypass_rpcrt = FALSE; - - /* Note: this is the important difference between STAs and MTAs - we - * always execute RPCs to STAs in the thread that originally created the - * apartment (i.e. the one that pumps messages to the window) */ - if (!apt->multi_threaded) - { - params->handle = CreateEventW(NULL, FALSE, FALSE, NULL); - - TRACE("Calling apartment thread 0x%08x...\n", apt->tid); - - if (PostMessageW(apartment_getwindow(apt), DM_EXECUTERPC, 0, (LPARAM)params)) - WaitForSingleObject(params->handle, INFINITE); - else - { - ERR("PostMessage failed with error %u\n", GetLastError()); - IRpcChannelBuffer_Release(params->chan); - IRpcStubBuffer_Release(params->stub); - } - CloseHandle(params->handle); - } - else - { - BOOL joined = FALSE; - struct oletls *info = COM_CurrentInfo(); - - if (!info->apt) - { - enter_apartment(info, COINIT_MULTITHREADED); - joined = TRUE; - } - Internal_RPC_ExecuteCall(params); - if (joined) - { - leave_apartment(info); - } - } - - hr = params->hr; - if (params->chan) - IRpcChannelBuffer_Release(params->chan); - if (params->stub) - IRpcStubBuffer_Release(params->stub); - HeapFree(GetProcessHeap(), 0, params); - - stub_manager_int_release(stub_manager); - apartment_release(apt); - - /* if IRpcStubBuffer_Invoke fails, we should raise an exception to tell - * the RPC runtime that the call failed */ - if (hr != S_OK) RpcRaiseException(hr); -} - -/* stub registration */ -HRESULT WINAPI RPC_RegisterInterface(REFIID riid) -{ - struct registered_if *rif; - BOOL found = FALSE; - HRESULT hr = S_OK; - - TRACE("(%s)\n", debugstr_guid(riid)); - - EnterCriticalSection(&csRegIf); - LIST_FOR_EACH_ENTRY(rif, ®istered_interfaces, struct registered_if, entry) - { - if (IsEqualGUID(&rif->If.InterfaceId.SyntaxGUID, riid)) - { - rif->refs++; - found = TRUE; - break; - } - } - if (!found) - { - TRACE("Creating new interface\n"); - - rif = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(*rif)); - if (rif) - { - RPC_STATUS status; - - rif->refs = 1; - rif->If.Length = sizeof(RPC_SERVER_INTERFACE); - /* RPC interface ID = COM interface ID */ - rif->If.InterfaceId.SyntaxGUID = *riid; - rif->If.DispatchTable = &rpc_dispatch; - /* all other fields are 0, including the version asCOM objects - * always have a version of 0.0 */ - status = RpcServerRegisterIfEx( - (RPC_IF_HANDLE)&rif->If, - NULL, NULL, - RPC_IF_OLE | RPC_IF_AUTOLISTEN, - RPC_C_LISTEN_MAX_CALLS_DEFAULT, - NULL); - if (status == RPC_S_OK) - list_add_tail(®istered_interfaces, &rif->entry); - else - { - ERR("RpcServerRegisterIfEx failed with error %d\n", status); - HeapFree(GetProcessHeap(), 0, rif); - hr = HRESULT_FROM_WIN32(status); - } - } - else - hr = E_OUTOFMEMORY; - } - LeaveCriticalSection(&csRegIf); - return hr; -} - -/* stub unregistration */ -void WINAPI RPC_UnregisterInterface(REFIID riid, BOOL wait) -{ - struct registered_if *rif; - EnterCriticalSection(&csRegIf); - LIST_FOR_EACH_ENTRY(rif, ®istered_interfaces, struct registered_if, entry) - { - if (IsEqualGUID(&rif->If.InterfaceId.SyntaxGUID, riid)) - { - if (!--rif->refs) - { - RpcServerUnregisterIf((RPC_IF_HANDLE)&rif->If, NULL, wait); - list_remove(&rif->entry); - HeapFree(GetProcessHeap(), 0, rif); - } - break; - } - } - LeaveCriticalSection(&csRegIf); -} - -/* get the info for an OXID, including the IPID for the rem unknown interface - * and the string binding */ -HRESULT WINAPI RPC_ResolveOxid(OXID oxid, OXID_INFO *oxid_info) -{ - TRACE("%s\n", wine_dbgstr_longlong(oxid)); - - oxid_info->dwTid = 0; - oxid_info->dwPid = 0; - oxid_info->dwAuthnHint = RPC_C_AUTHN_LEVEL_NONE; - /* FIXME: this is a hack around not having an OXID resolver yet - - * this function should contact the machine's OXID resolver and then it - * should give us the IPID of the IRemUnknown interface */ - oxid_info->ipidRemUnknown.Data1 = 0xffffffff; - oxid_info->ipidRemUnknown.Data2 = 0xffff; - oxid_info->ipidRemUnknown.Data3 = 0xffff; - memcpy(oxid_info->ipidRemUnknown.Data4, &oxid, sizeof(OXID)); - oxid_info->psa = NULL /* FIXME */; - - return S_OK; -} - -/* make the apartment reachable by other threads and processes and create the - * IRemUnknown object */ -void WINAPI RPC_StartRemoting(struct apartment *apt) -{ - if (!InterlockedExchange(&apt->remoting_started, TRUE)) - { - WCHAR endpoint[200]; - RPC_STATUS status; - - get_rpc_endpoint(endpoint, &apt->oxid); - - status = RpcServerUseProtseqEpW( - wszRpcTransport, - RPC_C_PROTSEQ_MAX_REQS_DEFAULT, - endpoint, - NULL); - if (status != RPC_S_OK) - ERR("Couldn't register endpoint %s\n", debugstr_w(endpoint)); - - /* FIXME: move remote unknown exporting into this function */ - } - start_apartment_remote_unknown(apt); -}
Hi,
While running your changed tests, I think I found new failures. Being a bot and all I'm not very good at pattern recognition, so I might be wrong, but could you please double-check?
Full results can be found at: https://testbot.winehq.org/JobDetails.pl?Key=77849
Your paranoid android.
=== debiant (64 bit WoW report) ===
ole32: clipboard.c:897: Test failed: IDataObject_GetData failed with error 0x800401d0 clipboard.c:900: Test failed: DataObjectImpl_GetData called 5 times instead of 3 times clipboard.c:864: Test failed: didn't find cf_dataobject clipboard.c:865: Test failed: didn't find cf_ole_priv_data clipboard.c:626: Test failed: got 800401d0 clipboard.c:1019: Test failed: failed to clear clipboard, hr = 0x800401d0. clipboard.c:626: Test failed: got 800401d0 clipboard.c:1029: Test failed: expected data_cmpl ref=0, got 4 clipboard.c:1529: Test failed: gle 5 clipboard.c:1531: Test failed: gle 1418 clipboard.c:1533: Test failed: gle 1418 clipboard.c:1541: Test failed: got 800401d0 Unhandled exception: page fault on read access to 0x00000000 in 32-bit code (0x00406970).
Report validation errors: ole32:clipboard crashed (c0000005)