From 28356337898cc19a87a9874de12f8a45eeeae968 Mon Sep 17 00:00:00 2001 From: Robert Shearman Date: Tue, 4 Jan 2005 11:58:23 +0000 Subject: [PATCH] Implement proxy manager. --- dlls/ole32/compobj.c | 17 +- dlls/ole32/compobj_private.h | 29 +-- dlls/ole32/marshal.c | 355 +++++++++++++++++++++++++++++------ 3 files changed, 317 insertions(+), 84 deletions(-) diff --git a/dlls/ole32/compobj.c b/dlls/ole32/compobj.c index ab66d95f695..c5486bf538e 100644 --- a/dlls/ole32/compobj.c +++ b/dlls/ole32/compobj.c @@ -226,6 +226,7 @@ APARTMENT* COM_CreateApartment(DWORD model) else apt = NtCurrentTeb()->ReservedForOle; + list_init(&apt->proxies); list_init(&apt->stubmgrs); apt->oidc = 1; @@ -263,6 +264,14 @@ static void COM_DestroyApartment(APARTMENT *apt) apt->prev = NULL; apt->next = NULL; LeaveCriticalSection(&csApartment); if (apt->model & COINIT_APARTMENTTHREADED) { + /* disconnect proxies to release the corresponding stubs. + * It is confirmed in "Essential COM" in the sub-chapter on + * "Lifecycle Management and Marshalling" that the native version also + * disconnects proxies in this function. */ + /* FIXME: this should also be called for MTA destruction, but that + * requires restructuring how apartments work slightly. */ + MARSHAL_Disconnect_Proxies(apt); + if (apt->win) DestroyWindow(apt->win); DeleteCriticalSection(&apt->cs); } @@ -545,14 +554,6 @@ void WINAPI CoUninitialize(void) RunningObjectTableImpl_UnInitialize(); - /* disconnect proxies to release the corresponding stubs. - * It is confirmed in "Essential COM" in the sub-chapter on - * "Lifecycle Management and Marshalling" that the native version also - * does some kind of proxy cleanup in this function. - * FIXME: does it just disconnect or completely destroy the proxies? - * FIXME: should this be in the apartment destructor? */ - MARSHAL_Disconnect_Proxies(); - /* Release the references to the registered class objects */ COM_RevokeAllClasses(); diff --git a/dlls/ole32/compobj_private.h b/dlls/ole32/compobj_private.h index eb6c4b0e734..7b8d4c4c06e 100644 --- a/dlls/ole32/compobj_private.h +++ b/dlls/ole32/compobj_private.h @@ -68,29 +68,30 @@ typedef struct tagXOBJECT { DWORD refs; /* external reference count */ } XOBJECT; -/* imported interface */ -typedef struct tagIIF { - struct tagIIF *next; +/* imported interface proxy */ +struct ifproxy +{ + struct list entry; LPVOID iface; /* interface pointer */ IID iid; /* interface ID */ IPID ipid; /* imported interface ID */ LPRPCPROXYBUFFER proxy; /* interface proxy */ DWORD refs; /* imported (public) references */ - HRESULT hres; /* result of proxy creation attempt */ -} IIF; +}; -/* imported object */ -typedef struct tagIOBJECT { - IRemUnknownVtbl *lpVtbl; +/* imported object / proxy manager */ +struct proxy_manager +{ + const IInternalUnknownVtbl *lpVtbl; struct tagAPARTMENT *parent; - struct tagIOBJECT *next; + struct list entry; LPRPCCHANNELBUFFER chan; /* channel to object */ OXID oxid; /* object exported ID */ OID oid; /* object ID */ - IPID ipid; /* first imported interface ID */ - IIF *ifaces; /* imported interfaces */ + struct list interfaces; /* imported interfaces */ DWORD refs; /* proxy reference count */ -} IOBJECT; + CRITICAL_SECTION cs; /* thread safety for this object and children */ +}; /* apartment */ typedef struct tagAPARTMENT { @@ -105,7 +106,7 @@ typedef struct tagAPARTMENT { CRITICAL_SECTION cs; /* thread safety */ LPMESSAGEFILTER filter; /* message filter */ XOBJECT *objs; /* exported objects */ - IOBJECT *proxies; /* imported objects */ + struct list proxies; /* imported objects */ LPUNKNOWN state; /* state object (see Co[Get,Set]State) */ LPVOID ErrorInfo; /* thread error info */ DWORD listenertid; /* id of apartment_listener_thread */ @@ -139,7 +140,7 @@ MARSHAL_Compare_Mids(wine_marshal_id *mid1,wine_marshal_id *mid2) { ; } -HRESULT MARSHAL_Disconnect_Proxies(void); +HRESULT MARSHAL_Disconnect_Proxies(APARTMENT *apt); HRESULT MARSHAL_GetStandardMarshalCF(LPVOID *ppv); /* an interface stub */ diff --git a/dlls/ole32/marshal.c b/dlls/ole32/marshal.c index c770eeac292..1e3684eb2b3 100644 --- a/dlls/ole32/marshal.c +++ b/dlls/ole32/marshal.c @@ -81,14 +81,6 @@ typedef struct _wine_marshal_data { DWORD mshlflags; } wine_marshal_data; -typedef struct _mid2unknown { - wine_marshal_id mid; - LPUNKNOWN pUnk; -} mid2unknown; - -static mid2unknown *proxies = NULL; -static int nrofproxies = 0; - IRpcStubBuffer *mid_to_stubbuffer(wine_marshal_id *mid) { struct stub_manager *m; @@ -132,40 +124,285 @@ static HRESULT register_ifstub(wine_marshal_id *mid, IUnknown *obj, IRpcStubBuff return ifstub ? S_OK : E_OUTOFMEMORY; } -HRESULT -MARSHAL_Disconnect_Proxies() { - int i; - TRACE("Disconnecting %d proxies\n", nrofproxies); - for (i = 0; i < nrofproxies; i++) - IRpcProxyBuffer_Disconnect((IRpcProxyBuffer*)proxies[i].pUnk); - +/* Client-side identity of the server object */ + +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 WINAPI ClientIdentity_QueryInterface(IInternalUnknown * iface, REFIID riid, void ** ppv) +{ + struct proxy_manager * This = (struct proxy_manager *)iface; + HRESULT hr; + struct ifproxy * ifproxy; + + TRACE("%s\n", debugstr_guid(riid)); + + if (IsEqualIID(riid, &IID_IUnknown) || + IsEqualIID(riid, &IID_IInternalUnknown)) + { + *ppv = (void *)iface; + IInternalUnknown_AddRef(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; + } + + FIXME("interface not found %s\n", debugstr_guid(riid)); + + /* FIXME: call IRemUnknown::RemQueryInterface */ + return E_NOINTERFACE; +} + +static ULONG WINAPI ClientIdentity_AddRef(IInternalUnknown * iface) +{ + struct proxy_manager * This = (struct proxy_manager *)iface; + TRACE("%p\n", iface); + return InterlockedIncrement(&This->refs); +} + +static ULONG WINAPI ClientIdentity_Release(IInternalUnknown * iface) +{ + struct proxy_manager * This = (struct proxy_manager *)iface; + ULONG refs = InterlockedDecrement(&This->refs); + TRACE("%p\n", iface); + if (!refs) + proxy_manager_destroy(This); + return refs; +} + +static HRESULT WINAPI ClientIdentity_QueryInternalInterface(IInternalUnknown * iface, REFIID riid, void ** ppv) +{ + FIXME("(%s, %p): stub!\n", debugstr_guid(riid), ppv); + return E_NOINTERFACE; +} + +static const IInternalUnknownVtbl ClientIdentity_Vtbl = +{ + ClientIdentity_QueryInterface, + ClientIdentity_AddRef, + ClientIdentity_Release, + ClientIdentity_QueryInternalInterface +}; + +static HRESULT ifproxy_get_public_ref(struct ifproxy * This) +{ + /* FIXME: call IRemUnknown::RemAddRef if necessary */ return S_OK; } -static HRESULT -MARSHAL_Register_Proxy(wine_marshal_id *mid,LPUNKNOWN punk) { - int i; +static HRESULT ifproxy_release_public_refs(struct ifproxy * This) +{ + /* FIXME: call IRemUnknown::RemRelease */ + return S_OK; +} - for (i=0;ioxid), wine_dbgstr_longlong(mid->oid), debugstr_guid(&mid->iid)); - return E_FAIL; - } +static void ifproxy_disconnect(struct ifproxy * This) +{ + IRpcProxyBuffer_Disconnect(This->proxy); +} + +static void ifproxy_destroy(struct ifproxy * 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->proxy) IRpcProxyBuffer_Release(This->proxy); + HeapFree(GetProcessHeap(), 0, This); +} + +static HRESULT proxy_manager_construct(APARTMENT * apt, OXID oxid, OID oid, IRpcChannelBuffer * channel, struct proxy_manager ** proxy_manager) +{ + struct proxy_manager * This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This)); + if (!This) return E_OUTOFMEMORY; + + This->lpVtbl = &ClientIdentity_Vtbl; + + list_init(&This->entry); + list_init(&This->interfaces); + + InitializeCriticalSection(&This->cs); + + /* 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 = 0; /* will be incremented on creation of first proxy */ + + This->chan = channel; /* FIXME: we should take the binding strings and construct the channel in this function */ + + EnterCriticalSection(&apt->cs); + list_add_head(&apt->proxies, &This->entry); + LeaveCriticalSection(&apt->cs); + + *proxy_manager = This; + return S_OK; +} + +static HRESULT proxy_manager_create_ifproxy(struct proxy_manager * This, IPID ipid, REFIID riid, ULONG cPublicRefs, 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->ipid = ipid; + ifproxy->iid = *riid; + ifproxy->refs = cPublicRefs; + ifproxy->proxy = NULL; + + 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->lpVtbl, riid, + &ifproxy->proxy, &ifproxy->iface); + IPSFactoryBuffer_Release(psfb); + } + + if (hr == S_OK) + hr = IRpcProxyBuffer_Connect(ifproxy->proxy, This->chan); + + /* get at least one external reference to the object to keep it alive */ + if (hr == S_OK) + hr = ifproxy_get_public_ref(ifproxy); + + if (hr == S_OK) + { + EnterCriticalSection(&This->cs); + list_add_tail(&This->interfaces, &ifproxy->entry); + LeaveCriticalSection(&This->cs); + + *iif_out = ifproxy; } - if (nrofproxies) - proxies = HeapReAlloc(GetProcessHeap(),0,proxies,sizeof(proxies[0])*(nrofproxies+1)); else - proxies = HeapAlloc(GetProcessHeap(),0,sizeof(proxies[0])); - memcpy(&(proxies[nrofproxies].mid),mid,sizeof(*mid)); - proxies[nrofproxies].pUnk = punk; - nrofproxies++; - IUnknown_AddRef(punk); + 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; + + EnterCriticalSection(&This->cs); + + 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 void proxy_manager_destroy(struct proxy_manager * This) +{ + struct list * cursor; + + 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); + } + + DeleteCriticalSection(&This->cs); + + HeapFree(GetProcessHeap(), 0, This); +} + +static BOOL find_proxy_manager(APARTMENT * apt, OXID oxid, OID oid, struct proxy_manager ** proxy_found) +{ + BOOL found = FALSE; + struct list * cursor; + + EnterCriticalSection(&apt->cs); + LIST_FOR_EACH(cursor, &apt->proxies) + { + struct proxy_manager * proxy = LIST_ENTRY(cursor, struct proxy_manager, entry); + if ((oxid == proxy->oxid) && (oid == proxy->oid)) + { + *proxy_found = proxy; + found = TRUE; + break; + } + } + LeaveCriticalSection(&apt->cs); + return found; +} + +HRESULT MARSHAL_Disconnect_Proxies(APARTMENT *apt) +{ + struct list * cursor; + + EnterCriticalSection(&apt->cs); + LIST_FOR_EACH(cursor, &apt->proxies) + { + struct proxy_manager * proxy = LIST_ENTRY(cursor, struct proxy_manager, entry); + proxy_manager_disconnect(proxy); + } + LeaveCriticalSection(&apt->cs); + return S_OK; } @@ -302,11 +539,14 @@ StdMarshalImpl_UnmarshalInterface(LPMARSHAL iface, IStream *pStm, REFIID riid, v wine_marshal_data md; ULONG res; HRESULT hres; - IPSFactoryBuffer *psfacbuf; - IRpcProxyBuffer *rpcproxy; IRpcChannelBuffer *chanbuf; + struct proxy_manager *proxy_manager; + APARTMENT *apt = COM_CurrentApt(); TRACE("(...,%s,....)\n",debugstr_guid(riid)); + + if (!apt) return CO_E_NOTINITIALIZED; + hres = IStream_Read(pStm,&mid,sizeof(mid),&res); if (hres) return hres; hres = IStream_Read(pStm,&md,sizeof(md),&res); @@ -326,36 +566,27 @@ StdMarshalImpl_UnmarshalInterface(LPMARSHAL iface, IStream *pStm, REFIID riid, v 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)); - } - hres = get_facbuf_for_iid(riid,&psfacbuf); - if (hres) return hres; - hres = IPSFactoryBuffer_CreateProxy(psfacbuf,NULL,riid,&rpcproxy,ppv); - if (hres) { - FIXME("Failed to create a proxy for %s\n",debugstr_guid(riid)); - return hres; + + if (!find_proxy_manager(apt, mid.oxid, mid.oid, &proxy_manager)) + { + hres = PIPE_GetNewPipeBuf(&mid,&chanbuf); + if (hres == S_OK) + hres = proxy_manager_construct(apt, mid.oxid, mid.oid, chanbuf, &proxy_manager); } - MARSHAL_Register_Proxy(&mid, (LPUNKNOWN) rpcproxy); + if (hres == S_OK) + { + struct ifproxy * ifproxy; + hres = proxy_manager_find_ifproxy(proxy_manager, riid, &ifproxy); + if (hres == S_OK) + IUnknown_AddRef((IUnknown *)ifproxy->iface); + else if (hres == E_NOINTERFACE) + hres = proxy_manager_create_ifproxy(proxy_manager, mid.iid /* FIXME: ipid */, riid, 1, &ifproxy); - hres = PIPE_GetNewPipeBuf(&mid,&chanbuf); - IPSFactoryBuffer_Release(psfacbuf); - if (hres) { - ERR("Failed to get an rpc channel buffer for %s\n",debugstr_guid(riid)); - } else { - /* Connect the channel buffer to the proxy and release the no longer - * needed proxy. - * NOTE: The proxy should have taken an extra reference because it also - * aggregates the object, so we can safely release our reference to it. */ - IRpcProxyBuffer_Connect(rpcproxy,chanbuf); - IRpcProxyBuffer_Release(rpcproxy); - /* IRpcProxyBuffer takes a reference on the channel buffer and - * we no longer need it, so release it */ - IRpcChannelBuffer_Release(chanbuf); + if (hres == S_OK) + *ppv = ifproxy->iface; /* AddRef'd above */ } + return hres; }