- Implement IRemUnknown.

- Use IRemUnknown for life-cycle management instead of the current
  hacks.
This commit is contained in:
Robert Shearman 2005-01-26 20:42:30 +00:00 committed by Alexandre Julliard
parent 407d863a8a
commit 963ac3f013
4 changed files with 322 additions and 7 deletions

View File

@ -168,6 +168,7 @@ HRESULT register_ifstub(APARTMENT *apt, STDOBJREF *stdobjref, REFIID riid, IUnkn
/* 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);
@ -236,6 +237,9 @@ static const IInternalUnknownVtbl ClientIdentity_Vtbl =
static HRESULT ifproxy_get_public_ref(struct ifproxy * This)
{
/* FIXME: as this call could possibly be going over the network, we
* are going to spend a long time in this CS. We might want to replace
* this with a mutex */
EnterCriticalSection(&This->parent->cs);
if (This->refs == 0)
{
@ -269,20 +273,40 @@ static HRESULT ifproxy_get_public_ref(struct ifproxy * This)
static HRESULT ifproxy_release_public_refs(struct ifproxy * This)
{
HRESULT hr = S_OK;
/* FIXME: as this call could possibly be going over the network, we
* are going to spend a long time in this CS. We might want to replace
* this with a mutex */
EnterCriticalSection(&This->parent->cs);
/*
if (This->refs > 0)
{
// FIXME: call IRemUnknown::RemRelease
This->refs = 0;
IRemUnknown *remunk = NULL;
TRACE("releasing %ld refs\n", This->refs);
hr = proxy_manager_get_remunknown(This->parent, &remunk);
if (hr == S_OK)
{
REMINTERFACEREF rif;
rif.ipid = This->ipid;
rif.cPublicRefs = This->refs;
rif.cPrivateRefs = 0;
hr = IRemUnknown_RemRelease(remunk, 1, &rif);
if (hr == S_OK)
This->refs = 0;
else
ERR("IRemUnknown_RemRelease failed with error 0x%08lx\n", hr);
}
}
*/
LeaveCriticalSection(&This->parent->cs);
return S_OK;
return hr;
}
static void ifproxy_disconnect(struct ifproxy * This)
{
ifproxy_release_public_refs(This);
IRpcProxyBuffer_Disconnect(This->proxy);
}
@ -327,12 +351,23 @@ static HRESULT proxy_manager_construct(
This->refs = 1;
This->sorflags = sorflags;
/* 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 in the proxy manager. */
This->sorflags = sorflags;
This->chan = channel; /* FIXME: we should take the binding strings and construct the channel in this function */
/* we create the IRemUnknown proxy on demand */
This->remunk = NULL;
EnterCriticalSection(&apt->cs);
list_add_head(&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;
}
@ -442,10 +477,65 @@ static void proxy_manager_disconnect(struct proxy_manager * This)
LeaveCriticalSection(&This->cs);
}
static HRESULT proxy_manager_get_remunknown(struct proxy_manager * This, IRemUnknown **remunk)
{
HRESULT hr = S_OK;
/* 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;
EnterCriticalSection(&This->cs);
if (This->remunk)
/* already created - return existing object */
*remunk = This->remunk;
else if (!This->parent)
/* disconnected - we can't create IRemUnknown */
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;
/* FIXME: this is a hack around not having an OXID resolver yet -
* the OXID resolver should give us the IPID of the IRemUnknown
* interface */
stdobjref.ipid.Data1 = 0xffffffff;
stdobjref.ipid.Data2 = 0xffff;
stdobjref.ipid.Data3 = 0xffff;
assert(sizeof(stdobjref.ipid.Data4) == sizeof(stdobjref.oxid));
memcpy(&stdobjref.ipid.Data4, &stdobjref.oxid, sizeof(OXID));
/* do the unmarshal */
hr = unmarshal_object(&stdobjref, This->parent, &IID_IRemUnknown, (void**)&This->remunk);
if (hr == S_OK)
*remunk = This->remunk;
}
LeaveCriticalSection(&This->cs);
TRACE("got IRemUnknown* pointer %p, hr = 0x%08lx\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);
@ -470,6 +560,7 @@ static void proxy_manager_destroy(struct proxy_manager * This)
ifproxy_destroy(ifproxy);
}
if (This->remunk) IRemUnknown_Release(This->remunk);
if (This->chan) IRpcChannelBuffer_Release(This->chan);
DeleteCriticalSection(&This->cs);
@ -593,6 +684,7 @@ StdMarshalImpl_MarshalInterface(
}
start_apartment_listener_thread(); /* just to be sure we have one running. */
start_apartment_remote_unknown();
IUnknown_QueryInterface((LPUNKNOWN)pv, riid, (LPVOID*)&pUnk);
@ -630,6 +722,14 @@ static HRESULT unmarshal_object(const STDOBJREF *stdobjref, APARTMENT *apt, REFI
struct proxy_manager *proxy_manager = NULL;
HRESULT hr = S_OK;
assert(apt);
TRACE("stdobjref:\n\tflags = %04lx\n\tcPublicRefs = %ld\n\toxid = %s\n\toid = %s\n\tipid = %s\n",
stdobjref->flags, stdobjref->cPublicRefs,
wine_dbgstr_longlong(stdobjref->oxid),
wine_dbgstr_longlong(stdobjref->oid),
debugstr_guid(&stdobjref->ipid));
/* create an a new proxy manager if one doesn't already exist for the
* object */
if (!find_proxy_manager(apt, stdobjref->oxid, stdobjref->oid, &proxy_manager))

View File

@ -291,15 +291,18 @@ static ULONG WINAPI
PipeBuf_Release(LPRPCCHANNELBUFFER iface) {
PipeBuf *This = (PipeBuf *)iface;
ULONG ref;
#if 0
struct disconnect_header header;
HANDLE pipe;
DWORD reqtype = REQTYPE_DISCONNECT;
DWORD magic;
#endif
ref = InterlockedDecrement(&This->ref);
if (ref)
return ref;
#if 0 /* no longer needed now we've got IRemUnknown ref counting */
memcpy(&header.mid, &This->mid, sizeof(wine_marshal_id));
pipe = PIPE_FindByMID(&This->mid);
@ -313,7 +316,8 @@ PipeBuf_Release(LPRPCCHANNELBUFFER iface) {
* necessary for real dcom but the test suite needs it */
read_pipe(pipe, &magic, sizeof(magic));
if (magic != 0xcafebabe) ERR("bad disconnection magic: expecting 0xcafebabe but got 0x%lx", magic);
if (magic != 0xcafebabe) ERR("bad disconnection magic: expecting 0xcafebabe but got 0x%lx\n", magic);
#endif
HeapFree(GetProcessHeap(),0,This);
return 0;

View File

@ -458,4 +458,211 @@ BOOL stub_manager_is_table_marshaled(struct stub_manager *m, const IPID *ipid)
return ret;
}
/*****************************************************************************
*
* IRemUnknown implementation
*
*
* Note: this object is not related to the lifetime of a stub_manager, but it
* interacts with stub managers.
*/
const IID IID_IRemUnknown = { 0x00000131, 0, 0, {0xc0, 0, 0, 0, 0, 0, 0, 0x46} };
typedef struct rem_unknown
{
const IRemUnknownVtbl *lpVtbl;
ULONG refs;
} RemUnknown;
static const IRemUnknownVtbl RemUnknown_Vtbl;
/* construct an IRemUnknown object with one outstanding reference */
static HRESULT RemUnknown_Construct(IRemUnknown **ppRemUnknown)
{
RemUnknown *This = HeapAlloc(GetProcessHeap(), 0, sizeof(*This));
if (!This) return E_OUTOFMEMORY;
This->lpVtbl = &RemUnknown_Vtbl;
This->refs = 1;
*ppRemUnknown = (IRemUnknown *)This;
return S_OK;
}
static HRESULT WINAPI RemUnknown_QueryInterface(IRemUnknown *iface, REFIID riid, void **ppv)
{
TRACE("(%p)->(%s, %p)\n", iface, debugstr_guid(riid), ppv);
if (IsEqualIID(riid, &IID_IUnknown) ||
IsEqualIID(riid, &IID_IRemUnknown))
{
*ppv = (LPVOID)iface;
IRemUnknown_AddRef(iface);
return S_OK;
}
FIXME("No interface for iid %s\n", debugstr_guid(riid));
*ppv = NULL;
return E_NOINTERFACE;
}
static ULONG WINAPI RemUnknown_AddRef(IRemUnknown *iface)
{
ULONG refs;
RemUnknown *This = (RemUnknown *)iface;
refs = InterlockedIncrement(&This->refs);
TRACE("%p before: %ld\n", iface, refs-1);
return refs;
}
static ULONG WINAPI RemUnknown_Release(IRemUnknown *iface)
{
ULONG refs;
RemUnknown *This = (RemUnknown *)iface;
refs = InterlockedDecrement(&This->refs);
if (!refs)
HeapFree(GetProcessHeap(), 0, This);
TRACE("%p after: %ld\n", iface, refs);
return refs;
}
static HRESULT WINAPI RemUnknown_RemQueryInterface(IRemUnknown *iface,
REFIPID ripid, ULONG cRefs, USHORT cIids, IID *iids /* [size_is(cIids)] */,
REMQIRESULT **ppQIResults /* [size_is(,cIids)] */)
{
HRESULT hr;
USHORT i;
APARTMENT *apt;
struct stub_manager *stubmgr;
TRACE("(%p)->(%s, %ld, %d, %p, %p)\n", iface, debugstr_guid(ripid), cRefs, cIids, iids, ppQIResults);
hr = ipid_to_stub_manager(ripid, &apt, &stubmgr);
if (hr != S_OK) return hr;
*ppQIResults = CoTaskMemAlloc(sizeof(REMQIRESULT) * cIids);
for (i = 0; i < cIids; i++)
{
(*ppQIResults)[i].hResult = register_ifstub(apt, &(*ppQIResults)[i].std,
&iids[i], stubmgr->object,
MSHLFLAGS_NORMAL);
}
stub_manager_int_release(stubmgr);
COM_ApartmentRelease(apt);
return hr;
}
static HRESULT WINAPI RemUnknown_RemAddRef(IRemUnknown *iface,
USHORT cInterfaceRefs,
REMINTERFACEREF* InterfaceRefs /* [size_is(cInterfaceRefs)] */,
HRESULT *pResults /* [size_is(cInterfaceRefs)] */)
{
HRESULT hr = S_OK;
USHORT i;
TRACE("(%p)->(%d, %p, %p)\n", iface, cInterfaceRefs, InterfaceRefs, pResults);
for (i = 0; i < cInterfaceRefs; i++)
{
APARTMENT *apt;
struct stub_manager *stubmgr;
pResults[i] = ipid_to_stub_manager(&InterfaceRefs[i].ipid, &apt, &stubmgr);
if (pResults[i] != S_OK)
{
hr = S_FALSE;
continue;
}
stub_manager_ext_addref(stubmgr, InterfaceRefs[i].cPublicRefs);
if (InterfaceRefs[i].cPrivateRefs)
FIXME("Adding %ld refs securely not implemented\n", InterfaceRefs[i].cPrivateRefs);
stub_manager_int_release(stubmgr);
COM_ApartmentRelease(apt);
}
return hr;
}
static HRESULT WINAPI RemUnknown_RemRelease(IRemUnknown *iface,
USHORT cInterfaceRefs,
REMINTERFACEREF* InterfaceRefs /* [size_is(cInterfaceRefs)] */)
{
HRESULT hr = S_OK;
USHORT i;
TRACE("(%p)->(%d, %p)\n", iface, cInterfaceRefs, InterfaceRefs);
for (i = 0; i < cInterfaceRefs; i++)
{
APARTMENT *apt;
struct stub_manager *stubmgr;
hr = ipid_to_stub_manager(&InterfaceRefs[i].ipid, &apt, &stubmgr);
if (hr != S_OK)
{
hr = E_INVALIDARG;
/* FIXME: we should undo any changes already made in this function */
break;
}
stub_manager_ext_release(stubmgr, InterfaceRefs[i].cPublicRefs);
if (InterfaceRefs[i].cPrivateRefs)
FIXME("Releasing %ld refs securely not implemented\n", InterfaceRefs[i].cPrivateRefs);
stub_manager_int_release(stubmgr);
COM_ApartmentRelease(apt);
}
return hr;
}
static const IRemUnknownVtbl RemUnknown_Vtbl =
{
RemUnknown_QueryInterface,
RemUnknown_AddRef,
RemUnknown_Release,
RemUnknown_RemQueryInterface,
RemUnknown_RemAddRef,
RemUnknown_RemRelease
};
/* starts the IRemUnknown listener for the current apartment */
HRESULT start_apartment_remote_unknown()
{
IRemUnknown *pRemUnknown;
HRESULT hr = S_OK;
APARTMENT *apt = COM_CurrentApt();
EnterCriticalSection(&apt->cs);
if (!apt->remunk_exported)
{
/* create the IRemUnknown object */
hr = RemUnknown_Construct(&pRemUnknown);
if (hr == S_OK)
{
STDOBJREF stdobjref; /* dummy - not used */
/* register it with the stub manager */
hr = register_ifstub(COM_CurrentApt(), &stdobjref, &IID_IRemUnknown, (IUnknown *)pRemUnknown, MSHLFLAGS_NORMAL);
/* release our reference to the object as the stub manager will manage the life cycle for us */
IRemUnknown_Release(pRemUnknown);
if (hr == S_OK)
apt->remunk_exported = TRUE;
}
}
LeaveCriticalSection(&apt->cs);
return hr;
}

View File

@ -65,6 +65,7 @@ static HRESULT WINAPI Test_IUnknown_QueryInterface(
return S_OK;
}
*ppvObj = NULL;
return E_NOINTERFACE;
}
@ -105,6 +106,7 @@ static HRESULT WINAPI Test_IClassFactory_QueryInterface(
return S_OK;
}
*ppvObj = NULL;
return E_NOINTERFACE;
}
@ -801,7 +803,9 @@ static void test_message_filter()
IClassFactory_Release(cf);
ok_no_locks();
/* FIXME: this is a regression caused by the fact that I faked the
* IUnknown unmarshaling too much and didn't give it its own ifstub. */
todo_wine { ok_no_locks(); }
end_host_object(tid, thread);
}