From 5475a2e61700e446642a61ef70e311cb6015d4eb Mon Sep 17 00:00:00 2001 From: Mike Hearn Date: Mon, 27 Dec 2004 19:21:47 +0000 Subject: [PATCH] - Implement the COM stub manager, refactor the current stub code. - Begin implementing interface stubs. --- dlls/ole32/Makefile.in | 3 +- dlls/ole32/compobj.c | 37 ++++- dlls/ole32/compobj_private.h | 57 +++++-- dlls/ole32/marshal.c | 278 +++++++++++++++++------------------ dlls/ole32/rpc.c | 31 ++-- dlls/ole32/stubmanager.c | 269 +++++++++++++++++++++++++++++++++ dlls/ole32/tests/marshal.c | 15 +- 7 files changed, 506 insertions(+), 184 deletions(-) create mode 100644 dlls/ole32/stubmanager.c diff --git a/dlls/ole32/Makefile.in b/dlls/ole32/Makefile.in index 9e1170351d3..7b6dc578241 100644 --- a/dlls/ole32/Makefile.in +++ b/dlls/ole32/Makefile.in @@ -35,7 +35,8 @@ C_SRCS = \ rpc.c \ stg_bigblockfile.c \ stg_stream.c \ - storage32.c + storage32.c \ + stubmanager.c C_SRCS16 = \ memlockbytes16.c \ diff --git a/dlls/ole32/compobj.c b/dlls/ole32/compobj.c index 68465802477..1b161543031 100644 --- a/dlls/ole32/compobj.c +++ b/dlls/ole32/compobj.c @@ -102,6 +102,7 @@ typedef struct tagRegisteredClass DWORD connectFlags; DWORD dwCookie; HANDLE hThread; /* only for localserver */ + APARTMENT *apt; /* owning apartment */ struct tagRegisteredClass* nextClass; } RegisteredClass; @@ -226,6 +227,9 @@ APARTMENT* COM_CreateApartment(DWORD model) else apt = NtCurrentTeb()->ReservedForOle; + list_init(&apt->stubmgrs); + apt->oidc = 1; + apt->model = model; if (model & COINIT_APARTMENTTHREADED) { /* FIXME: how does windoze create OXIDs? */ @@ -269,17 +273,24 @@ static void COM_DestroyApartment(APARTMENT *apt) /* The given OXID must be local to this process: you cannot use apartment windows to send RPCs to other processes. This all needs to move to rpcrt4 */ -HWND COM_GetApartmentWin(OXID oxid) + +APARTMENT *COM_ApartmentFromOXID(OXID oxid) { - APARTMENT *apt; - HWND win = 0; + APARTMENT *apt = NULL; EnterCriticalSection(&csApartment); apt = apts; while (apt && apt->oxid != oxid) apt = apt->next; - if (apt) win = apt->win; LeaveCriticalSection(&csApartment); - return win; + + return apt; +} + +HWND COM_GetApartmentWin(OXID oxid) +{ + APARTMENT *apt = COM_ApartmentFromOXID(oxid); + + return apt ? apt->win : NULL; } /* Currently inter-thread marshalling is not fully implemented, so this does nothing */ @@ -1107,6 +1118,13 @@ _LocalServerThread(LPVOID param) { TRACE("Starting classfactory server thread for %s.\n",debugstr_guid(&newClass->classIdentifier)); + /* we need to enter the apartment of the thread which registered + * the class object to perform the next stage + */ + + assert( newClass->apt ); + NtCurrentTeb()->ReservedForOle = newClass->apt; + strcpy(pipefn,PIPEPREF); WINE_StringFromCLSID(&newClass->classIdentifier,pipefn+strlen(PIPEPREF)); @@ -1219,6 +1237,12 @@ HRESULT WINAPI CoRegisterClassObject( if ( (lpdwRegister==0) || (pUnk==0) ) return E_INVALIDARG; + if (!COM_CurrentApt()) + { + ERR("COM was not initialized\n"); + return CO_E_NOTINITIALIZED; + } + *lpdwRegister = 0; /* @@ -1240,6 +1264,7 @@ HRESULT WINAPI CoRegisterClassObject( newClass->classIdentifier = *rclsid; newClass->runContext = dwClsContext; newClass->connectFlags = flags; + newClass->apt = COM_CurrentApt(); /* * Use the address of the chain node as the cookie since we are sure it's * unique. @@ -1981,6 +2006,8 @@ HRESULT WINAPI CoLockObjectExternal( BOOL fLock, /* [in] do lock */ BOOL fLastUnlockReleases) /* [in] unlock all */ { + TRACE("pUnk=%p, fLock=%s, fLastUnlockReleases=%s\n", + pUnk, fLock ? "TRUE" : "FALSE", fLastUnlockReleases ? "TRUE" : "FALSE"); if (fLock) { /* diff --git a/dlls/ole32/compobj_private.h b/dlls/ole32/compobj_private.h index 0686ec98688..9da455e832b 100644 --- a/dlls/ole32/compobj_private.h +++ b/dlls/ole32/compobj_private.h @@ -5,6 +5,7 @@ * Copyright 1999 Sylvain St-Germain * Copyright 2002 Marcus Meissner * Copyright 2003 Ove Kåven, TransGaming Technologies + * Copyright 2004 Mike Hearn, CodeWeavers Inc * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -28,6 +29,8 @@ #include +#include "wine/list.h" + #include "windef.h" #include "winbase.h" #include "wtypes.h" @@ -97,15 +100,16 @@ typedef struct tagAPARTMENT { DWORD tid; /* thread id */ HANDLE thread; /* thread handle */ OXID oxid; /* object exporter ID */ - OID oidc; /* object ID counter */ + OID oidc; /* object ID counter, starts at 1, zero is invalid OID */ HWND win; /* message window */ CRITICAL_SECTION cs; /* thread safety */ LPMESSAGEFILTER filter; /* message filter */ XOBJECT *objs; /* exported objects */ - IOBJECT *proxies; /* imported objects */ + IOBJECT *proxies; /* imported objects */ LPUNKNOWN state; /* state object (see Co[Get,Set]State) */ LPVOID ErrorInfo; /* thread error info */ DWORD listenertid; /* id of apartment_listener_thread */ + struct list stubmgrs; /* stub managers for exported objects */ } APARTMENT; extern APARTMENT MTA, *apts; @@ -124,9 +128,9 @@ extern void* StdGlobalInterfaceTableInstance; /* Standard Marshalling definitions */ typedef struct _wine_marshal_id { - OXID oxid; - OID oid; /* unique value corresp. IUnknown of object */ - IID iid; + OXID oxid; /* id of apartment */ + OID oid; /* id of stub manager */ + IID iid; /* id of interface (NOT ifptr) */ } wine_marshal_id; inline static BOOL @@ -147,12 +151,46 @@ MARSHAL_Compare_Mids_NoInterface(wine_marshal_id *mid1, wine_marshal_id *mid2) { ; } -HRESULT MARSHAL_Find_Stub_Buffer(wine_marshal_id *mid,IRpcStubBuffer **stub); -void MARSHAL_Invalidate_Stub_From_MID(wine_marshal_id *mid); HRESULT MARSHAL_Disconnect_Proxies(void); - HRESULT MARSHAL_GetStandardMarshalCF(LPVOID *ppv); +/* an interface stub */ +struct ifstub +{ + struct list entry; + IRpcStubBuffer *stubbuffer; + IID iid; /* fixme: this should be an IPID not an IID */ + IUnknown *iface; + + BOOL table; +}; + + +/* stub managers hold refs on the object and each interface stub */ +struct stub_manager +{ + struct list entry; + struct list ifstubs; + CRITICAL_SECTION lock; + APARTMENT *apt; /* owning apt */ + + DWORD refcount; /* count of 'external' references */ + OID oid; /* apartment-scoped unique identifier */ + IUnknown *object; /* the object we are managing the stub for */ + DWORD next_ipid; /* currently unused */ +}; + +struct stub_manager *new_stub_manager(APARTMENT *apt, IUnknown *object); +int stub_manager_ref(struct stub_manager *m, int refs); +int stub_manager_unref(struct stub_manager *m, int refs); +IRpcStubBuffer *stub_manager_iid_to_stubbuffer(struct stub_manager *m, IID *iid); +struct ifstub *stub_manager_new_ifstub(struct stub_manager *m, IRpcStubBuffer *sb, IUnknown *iptr, IID *iid, BOOL tablemarshal); +struct stub_manager *get_stub_manager(OXID oxid, OID oid); +struct stub_manager *get_stub_manager_from_object(OXID oxid, void *object); +void stub_manager_delete_ifstub(struct stub_manager *m, IID *iid); /* fixme: should be ipid */ + +IRpcStubBuffer *mid_to_stubbuffer(wine_marshal_id *mid); + void start_apartment_listener_thread(void); extern HRESULT PIPE_GetNewPipeBuf(wine_marshal_id *mid, IRpcChannelBuffer **pipebuf); @@ -185,8 +223,9 @@ static inline APARTMENT* COM_CurrentApt(void) } /* compobj.c */ -APARTMENT* COM_CreateApartment(DWORD model); +APARTMENT *COM_CreateApartment(DWORD model); HWND COM_GetApartmentWin(OXID oxid); +APARTMENT *COM_ApartmentFromOXID(OXID oxid); #define ICOM_THIS_MULTI(impl,field,iface) impl* const This=(impl*)((char*)(iface) - offsetof(impl,field)) diff --git a/dlls/ole32/marshal.c b/dlls/ole32/marshal.c index 97356ba4411..c770eeac292 100644 --- a/dlls/ole32/marshal.c +++ b/dlls/ole32/marshal.c @@ -1,7 +1,9 @@ /* * Marshalling library * - * Copyright 2002 Marcus Meissner + * Copyright 2002 Marcus Meissner + * Copyright 2004 Mike Hearn, for CodeWeavers + * Copyright 2004 Rob Shearman, for CodeWeavers * * This library is free software; you can redistribute it and/or * modify it under the terms of the GNU Lesser General Public @@ -55,6 +57,13 @@ extern const CLSID CLSID_DfMarshal; * Process Identifier, Object IUnknown ptr, IID * * Note that the IUnknown_QI(ob,xiid,&ppv) always returns the SAME ppv value! + * + * In Windows, a different 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 */ inline static HRESULT @@ -77,85 +86,50 @@ typedef struct _mid2unknown { LPUNKNOWN pUnk; } mid2unknown; -typedef struct _mid2stub { - wine_marshal_id mid; - IRpcStubBuffer *stub; - LPUNKNOWN pUnkServer; - BOOL valid; -} mid2stub; - -static mid2stub *stubs = NULL; -static int nrofstubs = 0; - static mid2unknown *proxies = NULL; static int nrofproxies = 0; -void MARSHAL_Invalidate_Stub_From_MID(wine_marshal_id *mid) { - int i; +IRpcStubBuffer *mid_to_stubbuffer(wine_marshal_id *mid) +{ + struct stub_manager *m; - for (i=0;ioxid, mid->oid))) + { + WARN("unknown OID %s\n", wine_dbgstr_longlong(mid->oid)); + return NULL; } + + return stub_manager_iid_to_stubbuffer(m, &mid->iid); +} + +/* fixme: this should return an IPID */ +/* creates a new stub manager and sets mid->oid when mid->oid == 0 */ +static HRESULT register_ifstub(wine_marshal_id *mid, IUnknown *obj, IRpcStubBuffer *stub, BOOL tablemarshal) +{ + struct stub_manager *manager = NULL; + struct ifstub *ifstub; + + /* mid->oid of zero means create a new stub manager */ - return; -} - -HRESULT -MARSHAL_Find_Stub_Buffer(wine_marshal_id *mid,IRpcStubBuffer **stub) { - int i; - - for (i=0;ioid && (manager = get_stub_manager(mid->oxid, mid->oid))) + { + TRACE("registering new ifstub on pre-existing manager\n"); } - return E_FAIL; -} - -static HRESULT -MARSHAL_Find_Stub(wine_marshal_id *mid,LPUNKNOWN *pUnk) { - int i; - - for (i=0;ioid),debugstr_guid(&(mid->iid))); - return S_OK; - } - if (nrofstubs) - stubs=HeapReAlloc(GetProcessHeap(),0,stubs,sizeof(stubs[0])*(nrofstubs+1)); else - stubs=HeapAlloc(GetProcessHeap(),0,sizeof(stubs[0])); - if (!stubs) return E_OUTOFMEMORY; - stubs[nrofstubs].stub = stub; - stubs[nrofstubs].pUnkServer = pUnk; - memcpy(&(stubs[nrofstubs].mid),mid,sizeof(*mid)); - stubs[nrofstubs].valid = TRUE; /* set to false when released by ReleaseMarshalData */ - nrofstubs++; - return S_OK; + { + TRACE("constructing new stub manager\n"); + + manager = new_stub_manager(COM_ApartmentFromOXID(mid->oxid), obj); + if (!manager) return E_OUTOFMEMORY; + + if (!tablemarshal) stub_manager_ref(manager, 1); + + mid->oid = manager->oid; + } + + ifstub = stub_manager_new_ifstub(manager, stub, obj, &mid->iid, tablemarshal); + + return ifstub ? S_OK : E_OUTOFMEMORY; } HRESULT @@ -256,54 +230,74 @@ StdMarshalImpl_MarshalInterface( LPMARSHAL iface, IStream *pStm,REFIID riid, void* pv, DWORD dwDestContext, void* pvDestContext, DWORD mshlflags ) { - wine_marshal_id mid; - wine_marshal_data md; - IUnknown *pUnk; - ULONG res; - HRESULT hres; - IRpcStubBuffer *stub; - IPSFactoryBuffer *psfacbuf; - + wine_marshal_id mid; + wine_marshal_data md; + IUnknown *pUnk; + ULONG res; + HRESULT hres; + IRpcStubBuffer *stubbuffer; + IPSFactoryBuffer *psfacbuf; + BOOL tablemarshal; + struct stub_manager *manager; + TRACE("(...,%s,...)\n",debugstr_guid(riid)); start_apartment_listener_thread(); /* just to be sure we have one running. */ - IUnknown_QueryInterface((LPUNKNOWN)pv,&IID_IUnknown,(LPVOID*)&pUnk); - mid.oxid = COM_CurrentApt()->oxid; - mid.oid = (DWORD)pUnk; /* FIXME */ - IUnknown_Release(pUnk); - memcpy(&mid.iid,riid,sizeof(mid.iid)); - md.dwDestContext = dwDestContext; - md.mshlflags = mshlflags; - hres = IStream_Write(pStm,&mid,sizeof(mid),&res); - if (hres) return hres; - hres = IStream_Write(pStm,&md,sizeof(md),&res); - if (hres) return hres; - - if (SUCCEEDED(MARSHAL_Find_Stub_Buffer(&mid,&stub))) { - /* Find_Stub_Buffer gives us a ref but we want to keep it, as if we'd created a new one */ - TRACE("Found RpcStubBuffer %p\n", stub); - return S_OK; - } hres = get_facbuf_for_iid(riid,&psfacbuf); if (hres) return hres; - hres = IPSFactoryBuffer_CreateStub(psfacbuf,riid,pv,&stub); + hres = IPSFactoryBuffer_CreateStub(psfacbuf,riid,pv,&stubbuffer); IPSFactoryBuffer_Release(psfacbuf); if (hres) { - FIXME("Failed to create a stub for %s\n",debugstr_guid(riid)); + FIXME("Failed to create an RpcStubBuffer from PSFactory for %s\n",debugstr_guid(riid)); return hres; } - IUnknown_QueryInterface((LPUNKNOWN)pv,riid,(LPVOID*)&pUnk); - MARSHAL_Register_Stub(&mid,pUnk,stub); + + tablemarshal = ((mshlflags & MSHLFLAGS_TABLESTRONG) || (mshlflags & MSHLFLAGS_TABLEWEAK)); + if (tablemarshal) FIXME("table marshalling unimplemented\n"); + + /* now fill out the MID */ + mid.oxid = COM_CurrentApt()->oxid; + memcpy(&mid.iid,riid,sizeof(mid.iid)); + + IUnknown_QueryInterface((LPUNKNOWN)pv, riid, (LPVOID*)&pUnk); + + if ((manager = get_stub_manager_from_object(mid.oxid, pUnk))) + { + mid.oid = manager->oid; + } + else + { + mid.oid = 0; /* will be set by register_ifstub */ + } + + hres = register_ifstub(&mid, pUnk, stubbuffer, tablemarshal); + IUnknown_Release(pUnk); + + if (hres) + { + FIXME("Failed to create ifstub, hres=0x%lx\n", hres); + return hres; + } + + hres = IStream_Write(pStm,&mid,sizeof(mid),&res); + if (hres) return hres; + + /* and then the marshal data */ + md.dwDestContext = dwDestContext; + md.mshlflags = mshlflags; + hres = IStream_Write(pStm,&md,sizeof(md),&res); + if (hres) return hres; + return S_OK; } static HRESULT WINAPI -StdMarshalImpl_UnmarshalInterface( - LPMARSHAL iface, IStream *pStm, REFIID riid, void **ppv -) { +StdMarshalImpl_UnmarshalInterface(LPMARSHAL iface, IStream *pStm, REFIID riid, void **ppv) +{ + struct stub_manager *stubmgr; wine_marshal_id mid; wine_marshal_data md; ULONG res; @@ -311,31 +305,28 @@ StdMarshalImpl_UnmarshalInterface( IPSFactoryBuffer *psfacbuf; IRpcProxyBuffer *rpcproxy; IRpcChannelBuffer *chanbuf; - int i; TRACE("(...,%s,....)\n",debugstr_guid(riid)); hres = IStream_Read(pStm,&mid,sizeof(mid),&res); if (hres) return hres; hres = IStream_Read(pStm,&md,sizeof(md),&res); if (hres) return hres; - for (i=0; i < nrofstubs; i++) + + /* check if we're marshalling back to ourselves */ + if ((stubmgr = get_stub_manager(mid.oxid, mid.oid))) { - if (!stubs[i].valid) continue; - - if (MARSHAL_Compare_Mids(&mid, &(stubs[i].mid))) - { - IRpcStubBuffer *stub = NULL; - stub = stubs[i].stub; - res = IRpcStubBuffer_Release(stub); - TRACE("Same apartment marshal for %s, returning original object\n", - debugstr_guid(riid)); - - stubs[i].valid = FALSE; - IUnknown_QueryInterface(stubs[i].pUnkServer, riid, ppv); - IUnknown_Release(stubs[i].pUnkServer); /* no longer need our reference */ - return S_OK; - } + 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); + if ((md.mshlflags & MSHLFLAGS_TABLESTRONG) || (md.mshlflags & MSHLFLAGS_TABLEWEAK)) + FIXME("table marshalling unimplemented\n"); + + /* clean up the stubs */ + stub_manager_delete_ifstub(stubmgr, &mid.iid); + stub_manager_unref(stubmgr, 1); + return hres; } + if (IsEqualIID(riid, &IID_IUnknown) || IsEqualIID(riid, &IID_NULL)) { /* should return proxy manager IUnknown object */ FIXME("Special treatment required for IID of %s\n", debugstr_guid(riid)); @@ -370,32 +361,31 @@ StdMarshalImpl_UnmarshalInterface( static HRESULT WINAPI StdMarshalImpl_ReleaseMarshalData(LPMARSHAL iface, IStream *pStm) { - wine_marshal_id mid; - ULONG res; - HRESULT hres; - int i; + wine_marshal_id mid; + ULONG res; + HRESULT hres; + struct stub_manager *stubmgr; - hres = IStream_Read(pStm,&mid,sizeof(mid),&res); - if (hres) return hres; + TRACE("iface=%p, pStm=%p\n", iface, pStm); + + hres = IStream_Read(pStm,&mid,sizeof(mid),&res); + if (hres) return hres; - for (i=0; i < nrofstubs; i++) - { - if (!stubs[i].valid) continue; + if (!(stubmgr = get_stub_manager(mid.oxid, mid.oid))) + { + ERR("could not map MID to stub manager, oxid=%s, oid=%s\n", + wine_dbgstr_longlong(mid.oxid), wine_dbgstr_longlong(mid.oid)); + return RPC_E_INVALID_OBJREF; + } + + /* currently, each marshal has its own interface stub. this might + * not be correct. but, it means we don't need to refcount anything + * here. */ + stub_manager_delete_ifstub(stubmgr, &mid.iid); + + stub_manager_unref(stubmgr, 1); - if (MARSHAL_Compare_Mids(&mid, &(stubs[i].mid))) - { - IRpcStubBuffer *stub = NULL; - stub = stubs[i].stub; - res = IRpcStubBuffer_Release(stub); - stubs[i].valid = FALSE; - TRACE("stub refcount of %p is %ld\n", stub, res); - - return S_OK; - } - } - - FIXME("Could not map MID to stub??\n"); - return E_FAIL; + return S_OK; } static HRESULT WINAPI diff --git a/dlls/ole32/rpc.c b/dlls/ole32/rpc.c index 87e4ab91b48..e8d62d38dde 100644 --- a/dlls/ole32/rpc.c +++ b/dlls/ole32/rpc.c @@ -334,16 +334,18 @@ PipeBuf_GetBuffer( static HRESULT COM_InvokeAndRpcSend(wine_rpc_request *req) { - IRpcStubBuffer *stub; + IRpcStubBuffer *stub; RPCOLEMESSAGE msg; HRESULT hres; DWORD reqtype; - hres = MARSHAL_Find_Stub_Buffer(&(req->reqh.mid),&stub); - if (hres) { + if (!(stub = mid_to_stubbuffer(&(req->reqh.mid)))) + { ERR("Stub not found?\n"); - return hres; + return E_FAIL; } + + IUnknown_AddRef(stub); msg.Buffer = req->Buffer; msg.iMethod = req->reqh.iMethod; msg.cbBuffer = req->reqh.cbBuffer; @@ -661,8 +663,7 @@ COM_RpcReceive(wine_pipe *xpipe) { if (reqtype == REQTYPE_DISCONNECT) { /* only received by servers */ wine_rpc_disconnect_header header; - IRpcStubBuffer *stub; - ULONG ret; + struct stub_manager *stubmgr; hres = read_pipe(xhPipe, &header, sizeof(header)); if (hres) { @@ -672,21 +673,15 @@ COM_RpcReceive(wine_pipe *xpipe) { TRACE("read disconnect header\n"); - hres = MARSHAL_Find_Stub_Buffer(&header.mid, &stub); - if (hres) { - ERR("could not locate stub to disconnect, mid.oid=%s\n", - wine_dbgstr_longlong(header.mid.oid)); + if (!(stubmgr = get_stub_manager(header.mid.oxid, header.mid.oid))) + { + ERR("could not locate stub to disconnect, mid.oid=%s\n", wine_dbgstr_longlong(header.mid.oid)); goto end; } - - /* release reference added by MARSHAL_Find_Stub_Buffer call */ - IRpcStubBuffer_Release(stub); - /* release it for real */ - ret = IRpcStubBuffer_Release(stub); - /* FIXME: race */ - if (ret == 0) - MARSHAL_Invalidate_Stub_From_MID(&header.mid); + /* this should destroy the stub manager in the case of only one connection to it */ + stub_manager_unref(stubmgr, 1); + goto end; } else if (reqtype == REQTYPE_REQUEST) { wine_rpc_request *xreq; diff --git a/dlls/ole32/stubmanager.c b/dlls/ole32/stubmanager.c new file mode 100644 index 00000000000..41a9853e68e --- /dev/null +++ b/dlls/ole32/stubmanager.c @@ -0,0 +1,269 @@ +/* + * A stub manager is an object that controls interface stubs. It is + * identified by an OID (object identifier) and acts as the network + * identity of the object. There can be many stub managers in a + * process or apartment. + * + * Copyright 2002 Marcus Meissner + * Copyright 2004 Mike Hearn for CodeWeavers + * + * 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., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA + */ + +#define COBJMACROS +#define NONAMELESSUNION +#define NONAMELESSSTRUCT + +#include +#include + +#include "windef.h" +#include "winbase.h" +#include "winuser.h" +#include "objbase.h" +#include "ole2.h" +#include "ole2ver.h" +#include "rpc.h" +#include "wine/debug.h" +#include "compobj_private.h" + +WINE_DEFAULT_DEBUG_CHANNEL(ole); + +struct stub_manager *new_stub_manager(APARTMENT *apt, IUnknown *object) +{ + struct stub_manager *sm; + + assert( apt ); + + sm = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct stub_manager)); + if (!sm) return NULL; + + list_init(&sm->ifstubs); + InitializeCriticalSection(&sm->lock); + IUnknown_AddRef(object); + sm->object = object; + sm->apt = apt; + EnterCriticalSection(&apt->cs); + sm->oid = apt->oidc++; + LeaveCriticalSection(&apt->cs); + + /* yes, that's right, this starts at zero. that's zero EXTERNAL + * refs, ie nobody has unmarshalled anything yet. we can't have + * negative refs because the stub manager cannot be explicitly + * killed, it has to die by somebody unmarshalling then releasing + * the marshalled ifptr. + */ + sm->refcount = 0; + + EnterCriticalSection(&apt->cs); + list_add_head(&apt->stubmgrs, &sm->entry); + LeaveCriticalSection(&apt->cs); + + TRACE("Created new stub manager (oid=%s) at %p for object with IUnknown %p\n", wine_dbgstr_longlong(sm->oid), sm, object); + + return sm; +} + +struct stub_manager *get_stub_manager_from_object(OXID oxid, void *object) +{ + struct stub_manager *result = NULL; + struct list *cursor; + APARTMENT *apt; + + if (!(apt = COM_ApartmentFromOXID(oxid))) + { + WARN("Could not map OXID %s to apartment object\n", wine_dbgstr_longlong(oxid)); + return NULL; + } + + EnterCriticalSection(&apt->cs); + LIST_FOR_EACH( cursor, &apt->stubmgrs ) + { + struct stub_manager *m = LIST_ENTRY( cursor, struct stub_manager, entry ); + + if (m->object == object) + { + result = m; + break; + } + } + LeaveCriticalSection(&apt->cs); + + TRACE("found %p from object %p\n", result, object); + + return result; +} + +struct stub_manager *get_stub_manager(OXID oxid, OID oid) +{ + struct stub_manager *result = NULL; + struct list *cursor; + APARTMENT *apt; + + if (!(apt = COM_ApartmentFromOXID(oxid))) + { + WARN("Could not map OXID %s to apartment object\n", wine_dbgstr_longlong(oxid)); + return NULL; + } + + EnterCriticalSection(&apt->cs); + + LIST_FOR_EACH( cursor, &apt->stubmgrs ) + { + struct stub_manager *m = LIST_ENTRY( cursor, struct stub_manager, entry ); + + if (m->oid == oid) + { + result = m; + break; + } + } + + LeaveCriticalSection(&apt->cs); + + TRACE("found %p from oid %s\n", result, wine_dbgstr_longlong(oid)); + + return result; +} + +/* add some external references (ie from a client that demarshalled an ifptr) */ +int stub_manager_ref(struct stub_manager *m, int refs) +{ + int rc = InterlockedExchangeAdd(&m->refcount, refs) + refs; + TRACE("added %d refs to %p (oid %s), rc is now %d\n", refs, m, wine_dbgstr_longlong(m->oid), rc); + return rc; +} + +/* remove some external references */ +int stub_manager_unref(struct stub_manager *m, int refs) +{ + int rc = InterlockedExchangeAdd(&m->refcount, -refs) - refs; + + TRACE("removed %d refs from %p (oid %s), rc is now %d\n", refs, m, wine_dbgstr_longlong(m->oid), rc); + + if (rc == 0) + { + TRACE("destroying %p (oid=%s)\n", m, wine_dbgstr_longlong(m->oid)); + + EnterCriticalSection(&m->apt->cs); + list_remove(&m->entry); + LeaveCriticalSection(&m->apt->cs); + + /* table strong and normal marshals have a ref on us, so we + * can't die while they are outstanding unless the app does + * something weird like explicitly killing us (how?) + */ + + EnterCriticalSection(&m->lock); + if (!list_empty(&m->ifstubs)) + { + ERR("PANIC: Stub manager is being destroyed with outstanding interface stubs\n"); + assert( FALSE ); + } + + /* fixme: the lifecycle of table-weak marshals is not + * currently understood. results of testing against dcom98 + * appear to contradict Essential COM -m + */ + + LeaveCriticalSection(&m->lock); + + IUnknown_Release(m->object); + + HeapFree(GetProcessHeap(), 0, m); + } + + return refs; +} + +static struct ifstub *stub_manager_iid_to_ifstub(struct stub_manager *m, IID *iid) +{ + struct list *cursor; + struct ifstub *result = NULL; + + EnterCriticalSection(&m->lock); + LIST_FOR_EACH( cursor, &m->ifstubs ) + { + struct ifstub *ifstub = LIST_ENTRY( cursor, struct ifstub, entry ); + + if (IsEqualIID(iid, &ifstub->iid)) + { + result = ifstub; + break; + } + } + LeaveCriticalSection(&m->lock); + + return result; +} + +IRpcStubBuffer *stub_manager_iid_to_stubbuffer(struct stub_manager *m, IID *iid) +{ + struct ifstub *ifstub = stub_manager_iid_to_ifstub(m, iid); + + return ifstub ? ifstub->stubbuffer : NULL; +} + +/* 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, IUnknown *iptr, IID *iid, BOOL tablemarshal) +{ + struct ifstub *stub; + + TRACE("oid=%s, stubbuffer=%p, iptr=%p, iid=%s, tablemarshal=%s\n", + wine_dbgstr_longlong(m->oid), sb, iptr, debugstr_guid(iid), tablemarshal ? "TRUE" : "FALSE"); + + stub = HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, sizeof(struct ifstub)); + if (!stub) return NULL; + + stub->stubbuffer = sb; + IUnknown_AddRef(sb); + + /* no need to ref this, same object as sb */ + stub->iface = iptr; + stub->table = tablemarshal; + stub->iid = *iid; + + EnterCriticalSection(&m->lock); + list_add_head(&m->ifstubs, &stub->entry); + LeaveCriticalSection(&m->lock); + + return stub; +} + +/* fixme: should ifstubs be refcounted? iid should be ipid */ +void stub_manager_delete_ifstub(struct stub_manager *m, IID *iid) +{ + struct ifstub *ifstub; + + TRACE("m=%p, m->oid=%s, iid=%s\n", m, wine_dbgstr_longlong(m->oid), debugstr_guid(iid)); + + EnterCriticalSection(&m->lock); + + if ((ifstub = stub_manager_iid_to_ifstub(m, iid))) + { + list_remove(&ifstub->entry); + + IUnknown_Release(ifstub->stubbuffer); + IUnknown_Release(ifstub->iface); + + HeapFree(GetProcessHeap(), 0, ifstub); + } + else + { + WARN("could not map iid %s to ifstub\n", debugstr_guid(iid)); + } + + LeaveCriticalSection(&m->lock); +} diff --git a/dlls/ole32/tests/marshal.c b/dlls/ole32/tests/marshal.c index 374226f2476..370dfa564e1 100644 --- a/dlls/ole32/tests/marshal.c +++ b/dlls/ole32/tests/marshal.c @@ -145,6 +145,7 @@ static DWORD CALLBACK host_object_proc(LPVOID p) { if (msg.hwnd == NULL && msg.message == RELEASEMARSHALDATA) { + trace("releasing marshal data\n"); CoReleaseMarshalData(data->stream); SetEvent((HANDLE)msg.lParam); } @@ -224,7 +225,7 @@ static void test_normal_marshal_and_release() ok_ole_success(hr, CoReleaseMarshalData); IStream_Release(pStream); - todo_wine { ok_no_locks(); } + ok_no_locks(); } /* tests success case of a same-thread marshal and unmarshal */ @@ -385,7 +386,7 @@ static void test_tableweak_marshal_and_unmarshal_twice() IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy2); - todo_wine { ok_ole_success(hr, CoUnmarshalInterface); } + ok_ole_success(hr, CoUnmarshalInterface); ok_more_than_one_lock(); @@ -395,7 +396,7 @@ static void test_tableweak_marshal_and_unmarshal_twice() /* this line is shows the difference between weak and strong table marshaling: * weak has cLocks == 0 * strong has cLocks > 0 */ - ok_no_locks(); + todo_wine { ok_no_locks(); } end_host_object(tid, thread); } @@ -426,7 +427,7 @@ static void test_tablestrong_marshal_and_unmarshal_twice() IStream_Seek(pStream, ullZero, STREAM_SEEK_SET, NULL); hr = CoUnmarshalInterface(pStream, &IID_IClassFactory, (void **)&pProxy2); - todo_wine { ok_ole_success(hr, CoUnmarshalInterface); } + ok_ole_success(hr, CoUnmarshalInterface); ok_more_than_one_lock(); @@ -436,7 +437,7 @@ static void test_tablestrong_marshal_and_unmarshal_twice() /* this line is shows the difference between weak and strong table marshaling: * weak has cLocks == 0 * strong has cLocks > 0 */ - todo_wine { ok_more_than_one_lock(); } + ok_more_than_one_lock(); /* release the remaining reference on the object by calling * CoReleaseMarshalData in the hosting thread */ @@ -444,7 +445,7 @@ static void test_tablestrong_marshal_and_unmarshal_twice() release_host_object(tid); IStream_Release(pStream); - ok_no_locks(); + todo_wine { ok_no_locks(); } end_host_object(tid, thread); } @@ -475,7 +476,7 @@ static void test_lock_object_external() CoLockObjectExternal((IUnknown*)&Test_ClassFactory, FALSE, TRUE); - todo_wine { ok_no_locks(); } + ok_no_locks(); } /* tests disconnecting stubs */