NT4/private/ole32/com/remote/remhdlr.cxx
2020-09-30 17:12:29 +02:00

1646 lines
44 KiB
C++

//+-------------------------------------------------------------------
//
// File: remhdlr.cxx
//
// Contents: remote object handler implementation
//
// Classes: CRemoteHdlr - remote object handler
// CPSIX - proxy/stub interface wrapper
//
// History: 23-Nov-92 Rickhi Created
// 05-Jul-94 BruceMa Check for end of stream
//
//--------------------------------------------------------------------
#include <ole2int.h>
#include <scm.h> // Get internal CLSCTX.
#include <remhdlr.hxx> // CRemoteHdlr
COleStaticMutexSem CRemoteHdlr::_mxs; // global mutext semaphore
#pragma warning(disable:4355) // 'this' used in base member init list
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::CRemoteHdlr, public
//
// Synopsis: constructor for the remote handler object
//
// History: 23-Nov-93 Rickhi Created
// 01-Aug-94 Alexgo Allocate the ChannelBuffer and
// return failure if E_OUTOFMEMORY
//
//--------------------------------------------------------------------
CRemoteHdlr::CRemoteHdlr(IUnknown *punkObj, // object ptr
IStdIdentity *pStdID, // identity
DWORD dwFlags, // flags
HRESULT &hr) // return code
: _punkObj(punkObj),
_pstdID(pStdID),
_dwFlags(dwFlags),
_cReferences(1),
_cCallsInProgress(0),
_pChannel(NULL)
{
Win4Assert(IsValidInterface(_punkObj));
// create an Rpc Channel for this object. we need to tell it if this is
// for a local object, or a remote object, so that the channel can point
// to the correct service object.
_pChannel = new CRpcChannelBuffer(this, NULL, 0, NULL,
IS_LOCAL_RH ? disconnected_cs : client_cs);
if (_pChannel == NULL)
{
hr = E_OUTOFMEMORY;
return;
}
if (IS_LOCAL_RH)
{
// we (now) addref this as long as we hold its value; disconnect
// is the only place that releases it and set it to NULL; many places
// check for NULL.
_punkObj->AddRef();
}
hr = S_OK;
AssertValid();
CairoleDebugOut((DEB_MARSHAL,
"New CRemoteHdlr pRH:%x pChannel:%x\n",
this, _pChannel));
}
#pragma warning(default:4355) // 'this' used in base member init list
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::QueryInterface, public
//
// Synopsis: returns internally supported interfaces
//
// Exceptions: CException if input parameters are bad or
// cant create the interface proxy.
//
// History: 23-Nov-93 Rickhi Created
//
// Notes: IUnknown and IMarshal are always private interfaces when
// this object is aggregated, hence QI, AddRef, Release do not
// get forwarded to the controlling unknown.
//
//--------------------------------------------------------------------
STDMETHODIMP CRemoteHdlr::QueryInterface(REFIID riid, void **ppv)
{
HRESULT sc = S_OK;
AssertValid();
if (IsEqualIID(riid, IID_IMarshal) || // more common than IUnknown
IsEqualIID(riid, IID_IUnknown))
{
*ppv = (IMarshal *) this;
AddRef();
}
else if (IsEqualIID(riid, IID_IRemoteHdlr))
{
*ppv = (IRemoteHdlr *) this;
AddRef();
}
else
{
// don't expect this on the local side.
Assert(!IS_LOCAL_RH);
// find or create the interface we are looking for, and AddRef it
// BUGBUG: we probably want to stablize this QI here per new rules
// i.e., _pUnkObj->AddRef(), Release() around the FindIX call.
// call sets ppv and sc per normal rules (e.g., *ppv == NULL on error).
(void)FindIX(riid, ppv, FLG_QUERYINTERFACE, &sc);
}
return sc;
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::AddRef, public
//
// Synopsis: increments the reference count on the object.
//
// History: 23-Nov-93 Rickhi Created
//
//--------------------------------------------------------------------
STDMETHODIMP_(ULONG) CRemoteHdlr::AddRef(void)
{
AssertValid();
return InterlockedAddRef(&_cReferences);
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::Release, public
//
// Synopsis: decrements the reference count and deletes the object if
// it reaches zero.
//
// History: 23-Nov-93 Rickhi Created
//
//--------------------------------------------------------------------
STDMETHODIMP_(ULONG) CRemoteHdlr::Release(void)
{
DWORD refs;
AssertValid();
if ((refs = InterlockedRelease(&_cReferences)) == 0)
{
delete this;
return 0;
}
return refs;
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::GetChannel, public
//
// Synopsis: Returns the channel; DBG only.
//
// History: 06-Jan-94 CraigWi Created.
//
//--------------------------------------------------------------------
STDMETHODIMP_(IRpcChannelBuffer *) CRemoteHdlr::GetChannel(BOOL fAddRef)
{
#if DBG == 1
// can't call AssertValid() since it is used by the service object asserts
// don't support fAddRef yet because the channel object is part of the RH
Assert(!fAddRef);
return _pChannel;
#else // !DBG
return 0;
#endif
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::LockClient, public
//
// Synopsis: prevents the client RH, and hence all attached proxies and
// channels, from vanishing during an Rpc call. We hold
// pUnkOuter of the aggregate and the whole aggregate must
// persist if any piece of it does.
//
// NOTE: nothing is done on the server side; the server side
// channel addref's the RH for the duration of the connection.
// We can't do that on the client side since the pointer
// from the channel to the RH is a back pointer (which are
// not normally addref'd and further more the channel is embedded
// in the RH and thus its ref count is not too meaningful).
//
// CODEWORK: since this is really only interesting on the client side,
// we can make this much simpler if the channel was an independent object
// (by just addref'ing the channel object only).
//
// History: 23-Nov-93 Rickhi Created
// 21-Dec-93 CraigWi Distinguish between local and remote
// 7-Apr-94 CraigWi Only works remotely now
//
//--------------------------------------------------------------------
STDMETHODIMP_(ULONG) CRemoteHdlr::LockClient(void)
{
AssertValid();
if (!IS_LOCAL_RH && _punkObj != NULL)
{
InterlockedIncrement(&_cCallsInProgress);
return _punkObj->AddRef();
}
else
return 0;
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::UnLockClient, public
//
// Synopsis: See LockClient() above.
//
// History: 23-Nov-93 Rickhi Created
// 21-Dec-93 CraigWi Distinguish between local and remote
// 7-Apr-94 CraigWi Only works remotely now
//
// Note: Special magic that was required in the past is not longer
// necessary since the RH is never has non-addref'd pointers
// to it. This complexity is now in the identity object.
//
//--------------------------------------------------------------------
STDMETHODIMP_(ULONG) CRemoteHdlr::UnLockClient(void)
{
AssertValid();
if (!IS_LOCAL_RH && _punkObj != NULL)
{
if ((InterlockedDecrement(&_cCallsInProgress) == 0) &&
_dwFlags & RHFLAGS_PENDINGDISCONNECT)
{
// disconnect was pending, now do the real disconnect
DisconnectObject(0);
}
return _punkObj->Release();
}
else
return 0;
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::ClearIdentity, public
//
// Synopsis: Clears the _pstdID during identity destruction.
//
// History: 21-Dec-93 CraigWi Created
//
//--------------------------------------------------------------------
STDMETHODIMP_(void) CRemoteHdlr::ClearIdentity(void)
{
AssertValid();
// don't clear this on the client side since we may need it again
if (IS_LOCAL_RH)
_pstdID = NULL;
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::DoesSupportIID, public
//
// Synopsis: returns TRUE if the interface is supported; works
// on both client and server sides.
//
// Arguments: [riid] -- The iid in question
//
// History: 13-Dec-93 CraigWi Created.
//
//--------------------------------------------------------------------
STDMETHODIMP_(BOOL) CRemoteHdlr::DoesSupportIID(REFIID riid)
{
HRESULT hr;
AssertValid();
return FindIX(riid, NULL, IS_LOCAL_RH ? FLG_MARSHAL : FLG_QUERYINTERFACE, &hr) != NULL;
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::AddConnection, private
//
// Synopsis: forwards call to identity object
//
// History: 23-Nov-93 Rickhi Created
// 21-Dec-93 CraigWi Changed to handle weak as well
// 20-Apr-94 CraigWi Change to forward to id object
//
//--------------------------------------------------------------------
STDMETHODIMP CRemoteHdlr::AddConnection(DWORD extconn, DWORD reserved)
{
AssertValid();
if (_pstdID != NULL)
return _pstdID->AddConnection(extconn, reserved);
else
return CO_E_OBJNOTCONNECTED;
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::ReleaseConnection, private
//
// Synopsis: records the removal of a connection (weak or strong)
//
// History: 23-Nov-93 Rickhi Created
// 21-Dec-93 CraigWi Changed to handle weak as well
// 20-Apr-94 CraigWi Change to forward to id object
//
//--------------------------------------------------------------------
STDMETHODIMP CRemoteHdlr::ReleaseConnection(DWORD extconn,
DWORD reserved, BOOL fLastReleaseCloses)
{
AssertValid();
if (_pstdID != NULL)
return _pstdID->ReleaseConnection(extconn, reserved,fLastReleaseCloses);
else
return CO_E_OBJNOTCONNECTED;
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::IsConnected, private
//
// Synopsis: Returns TRUE if most likely connected; FALSE if definitely
// not connected. If returns FALSE, cleans up.
//
// History: 14-Dec-93 CraigWi Created.
//
//--------------------------------------------------------------------
STDMETHODIMP_(BOOL) CRemoteHdlr::IsConnected(void)
{
AssertValid();
Assert(!IS_LOCAL_RH);
if (_pChannel->IsConnected() == S_OK)
{
return TRUE;
}
else
{
// clean up our data (channel already did)
_IXList.DisconnectProxies();
// BUGBUG: not sure about the fMustBeOnCOMThread setting
_pChannel->AssertValid(TRUE, FALSE); // known disconnected
return FALSE;
}
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::Disconnect, private
//
// Synopsis: same as IMarshal::Disconnect(0); this method is
// present for the convenience of the identity object.
//
// History: 14-Dec-93 CraigWi Created.
//
//--------------------------------------------------------------------
STDMETHODIMP_(void) CRemoteHdlr::Disconnect(void)
{
AssertValid();
Assert(!IS_LOCAL_RH);
DisconnectObject(0);
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::LockConnection, private
//
// Synopsis: pass through to channel
//
// History: 14-Dec-93 CraigWi Created.
//
//--------------------------------------------------------------------
STDMETHODIMP CRemoteHdlr::LockConnection(BOOL fLock, BOOL fLastUnlockReleases)
{
AssertValid();
Assert(!IS_LOCAL_RH);
return _pChannel->LockObjectConnection(fLock, fLastUnlockReleases);
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::GetUnmarshalClass, public
//
// Synopsis: returns the class of the code used to unmarshal this
// object
//
// History: 23-Nov-93 Rickhi Created
//
//--------------------------------------------------------------------
STDMETHODIMP CRemoteHdlr::GetUnmarshalClass(REFIID riid, void *pv,
DWORD dwDestCtx, void *pvDestCtx,
DWORD mshlflags, LPCLSID pClassid)
{
AssertValid();
*pClassid = CLSID_StdMarshal;
return S_OK;
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::GetMarshalSizeMax, public
//
// Synopsis: returns the maximum sized buffer needed to marshal this
// object
//
// History: 23-Nov-93 Rickhi Created
//
//--------------------------------------------------------------------
STDMETHODIMP CRemoteHdlr::GetMarshalSizeMax(REFIID riid, void *pv,
DWORD dwDestCtx, void *pvDestCtx,
DWORD mshlflags, DWORD *pSize)
{
AssertValid();
// get the size that the channel needs
HRESULT sc = _pChannel->GetMarshalSizeMax(riid, pv, dwDestCtx,
pvDestCtx, mshlflags, pSize);
// add in the size the handler needs (assume clsid Channel)
#ifdef CODEWORK
*pSize += sizeof(SHandlerDataHdr) + sizeof(CLSID);
#else
*pSize += sizeof(SHandlerDataHdr);
#endif
return sc;
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::MarshalInterface, public
//
// Synopsis: marshalls the specified interface into the given stream
//
// History: 23-Nov-93 Rickhi Created
//
// Note: The format of the data is {SHandlerDataHdr,<channel Data>}
//
//--------------------------------------------------------------------
STDMETHODIMP CRemoteHdlr::MarshalInterface(IStream *pStm, REFIID riid, void *pv,
DWORD dwDestCtx, void *pvDestCtx, DWORD mshlflags)
{
TRACECALL(TRACE_MARSHAL, "CRemoteHdlr::MarshalInterface");
AssertValid();
SHandlerDataHdr hdh;
HRESULT sc = E_NOINTERFACE;
// make sure we have a stub setup for this interface. we check for
// IUnknown specially because there is no proxy/stub for it, it is
// handled by us directly.
// BUGBUG: This call to FindIX is kind of strange for the client side;
// the riid should already exist; if it doesn't (e.g., we weren't pass
// such a pointer), we will assert in CreateInterfaceStub and then
// proceed to create a stub on the remote side!!
if (IsEqualIID(IID_IUnknown, riid) || FindIX(riid, NULL, FLG_MARSHAL, &sc))
{
#ifdef CODEWORK
// get the channel classid
CLSID clsidChannel;
sc = _pChannel->GetUnmarshalClass(riid, pv, dwDestCtx, pvDestCtx,
mshlflags, &clsidChannel);
if (FAILED(sc))
return sc;
if (clsidChannel == CLSID_StdChannel)
set bit in hdh.dwflags;
#else
// since we are single channel, we always have the same clsid; verify
#if DBG == 1
CLSID clsidChannel;
sc = _pChannel->GetUnmarshalClass(riid, pv, dwDestCtx, pvDestCtx,
mshlflags, &clsidChannel);
Assert(sc == NOERROR && IsEqualGUID(clsidChannel, CLSID_RpcChannelBuffer));
#endif
#endif
// store the marshal flags. these are needed for unmarshal and
// release data in order to adjust the reference counts
// appropriately.
hdh.dwflags = (mshlflags & HDLRFLAGS_TABLE) | HDLRFLAGS_STDRPCCHNL;
// store the interface iid in the data
memcpy(&hdh.iid, &riid, sizeof(hdh.iid));
// write the interface header into the stream
sc = pStm->Write(&hdh, sizeof(hdh), NULL);
if (SUCCEEDED(sc))
{
// now marshal the channel data
sc = _pChannel->MarshalInterface(pStm, riid, pv, dwDestCtx,
pvDestCtx, mshlflags);
}
}
return sc;
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::UnmarshalInterface, public
//
// Synopsis: reads the SHandlerDataHdr from the stream, locates or
// creates the appropriate handler for this object, and
// passes the work off to that handler.
//
// History: 23-Nov-93 Rickhi Created
//
//--------------------------------------------------------------------
STDMETHODIMP CRemoteHdlr::UnmarshalInterface(
IStream *pStm,
REFIID riid,
void **ppv)
{
TRACECALL(TRACE_MARSHAL, "CRemoteHdlr::UnmarshalInterface");
AssertValid();
// read the object data from the stream, and ensure
// that this object has not already been released.
SHandlerDataHdr hdh;
CLSID clsidChannel;
HRESULT sc = StRead(pStm, &hdh, sizeof(hdh));
// deal with channel clsid (probably pass to Unmarshal below)
if (SUCCEEDED(sc) && (hdh.dwflags & HDLRFLAGS_STDRPCCHNL) == 0)
sc = StRead(pStm, &clsidChannel, sizeof(clsidChannel));
#ifdef CODEWORK
must use clsidChannel
#else
// single channel; just verify clsid
Assert((hdh.dwflags & HDLRFLAGS_STDRPCCHNL) != 0 ||
IsEqualGUID(clsidChannel, CLSID_RpcChannelBuffer));
#endif
// deal with extension
if (hdh.dwflags & HDLRFLAGS_EXTENSION)
SkipMarshalExtension(pStm);
if (SUCCEEDED(sc))
{
// ask helper routine to do the rest of the work
sc = Unmarshal(pStm, riid, hdh, ppv);
}
return sc;
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::Unmarshal, private
//
// Parameters: [pStm] - stream to unmarshal data from
//
// Synopsis: does the unmarshalling for a particular handler. asks
// the channel to unmarshal his part, finds or creates
// the appropriate interfaces, and tweaks the reference
// counts.
//
// History: 23-Nov-93 Rickhi Created
//
// CODEWORK: merge into ::UnmarshalInterface above. The reasons for
// this routine being separate have gone.
//
//--------------------------------------------------------------------
STDMETHODIMP CRemoteHdlr::Unmarshal(
IStream *pStm,
REFIID riid,
SHandlerDataHdr &hdh,
void **ppv)
{
// CODEWORK: fix for multiple channels
Assert(hdh.dwflags & HDLRFLAGS_STDRPCCHNL);
// unmarshal the channel data; KLUDGE: don't care about iid/ppv;
// we certainly don't want to pass the riid/ppv from above.
// CODEWORK: will probably change this when we lookup channel based on
// destination context.
HRESULT sc = _pChannel->UnmarshalInterface(pStm, IID_NULL, NULL);
if (SUCCEEDED(sc))
{
if (sc == CHAN_S_RECONNECT)
{
// connect proxies
_dwFlags &= ~RHFLAGS_DISCONNECTED; // no longer disconnected
_IXList.ConnectProxies(_pChannel);
}
sc = S_OK; // don't want to propagate random success codes
// look for the interface that was actually marshalled by the
// other side. if not present, create an interface proxy for it.
CPSIX *pIX = NULL;
if (!IsEqualIID(IID_IUnknown, hdh.iid))
{
pIX = FindIX(hdh.iid, NULL, FLG_UNMARSHAL, &sc);
// ignore these errors; a subsequent QI will detect the problem;
// the main point about passing the iid back and forth is
// to tell the client that a certain interface is support
// and avoid an RPC call to the server process.
}
// since the RH is just a middle man in the unmarshaling process,
// we don't allow any interface to be returned; another way
// to look at this is the RH must be precreated in the right place.
if (!IsEqualIID(riid, IID_NULL) || ppv != NULL)
sc = E_UNEXPECTED;
}
return sc;
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::ReleaseMarshalData, public
//
// Synopsis: finds the handler for this object and calls it to release
// its internal state.
//
// History: 23-Nov-93 Rickhi Created
//
// Invocation: This is called by CoUnmarshalInterface when mshlflags
// are NORMAL, and by application code when mshlflags are
// TABLESTRONG or TABLEWEAK.
//
// Notes: So we dont ever unmarshal this data again, we set the
// HDLRFLAGS_RELEASED bit in the SHandlerDataHdr. Unmarshal
// ensures this is not set, and errors if so.
//
//--------------------------------------------------------------------
STDMETHODIMP CRemoteHdlr::ReleaseMarshalData(IStream *pStm)
{
TRACECALL(TRACE_MARSHAL, "CRemoteHdlr::ReleaseMarshalData");
AssertValid();
SHandlerDataHdr hdh;
CLSID clsidChannel;
// read the marshal data from the stream
HRESULT sc = StRead(pStm, &hdh, sizeof(hdh));
if (FAILED(sc))
return sc;
// deal with channel clsid (probably pass to Unmarshal below)
if ((hdh.dwflags & HDLRFLAGS_STDRPCCHNL) == 0)
{
sc = StRead(pStm, &clsidChannel, sizeof(clsidChannel));
if (FAILED(sc))
return sc;
}
#ifdef CODEWORK
must use clsidChannel
#else
// single channel; just verify clsid
Assert((hdh.dwflags & HDLRFLAGS_STDRPCCHNL) != 0 ||
IsEqualGUID(clsidChannel, CLSID_RpcChannelBuffer));
#endif
// deal with extension
if (hdh.dwflags & HDLRFLAGS_EXTENSION)
{
sc = SkipMarshalExtension(pStm);
if (FAILED(sc))
return sc;
}
// we are the remote handler for the object to be released
sc = ReleaseData(pStm, hdh);
return sc;
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::ReleaseData, private
//
// Synopsis: Releases any internal state kept around for marshalled
// interfaces on this particular handler.
//
// History: 23-Nov-93 Rickhi Created
//
// CODEWORK: merge into ::UnmarshalInterface above. The reasons for
// this routine being separate have gone.
//
//+-------------------------------------------------------------------
STDMETHODIMP CRemoteHdlr::ReleaseData(IStream *pStm, SHandlerDataHdr &hdh)
{
CairoleDebugOut((DEB_MARSHAL, "CRemoteHdlr::ReleasMarshalData %x\n", this));
// CODEWORK: fix for multiple channels
Assert(hdh.dwflags & HDLRFLAGS_STDRPCCHNL);
// call ReleaseMarshalData on the channel
return _pChannel->ReleaseMarshalData(pStm);
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::DisconnectObject, public
//
// Synopsis:
//
// History: 23-Nov-93 Rickhi Created
//
//--------------------------------------------------------------------
STDMETHODIMP CRemoteHdlr::DisconnectObject(DWORD dwReserved)
{
AssertValid();
// CODEWORK: thread safety: need to block multiple calls here and
// block out other code which tries to use the data changed here (_punkObj)
if (_dwFlags & RHFLAGS_DISCONNECTED)
{
// already disconnected, nothing to do
return S_OK;
}
if (_cCallsInProgress != 0)
{
// we dont allow disconnect to occur inside a nested call, but we
// remember that we want to disconnect and will do it when the
// stack unwinds.
_dwFlags |= RHFLAGS_PENDINGDISCONNECT;
return S_OK;
}
// No calls in progress, OK to really disconnect.
if (!IS_LOCAL_RH) // client side
{
_IXList.DisconnectProxies();
// client side: disconnect this one channel from the server
_pChannel->DisconnectObject(dwReserved);
// NOTE: on Client side we did not AddRef the _punkObj in the ctor and
// thus no Release (because _punkObj is our pUnkOuter, which might just
// be the id obj). Also, _punkObj is always valid as long as we are
// alive.
}
else // server side
{
// server side:
// walk the list of clients, killing off their channel ids.
// when they make a subsequent call, they will get an Rpc error.
ChannelList.DisconnectHdlr(this);
// release our main hold on the object; use WR if supported
if (_punkObj != NULL)
{
_IXList.DisconnectStubs();
SafeReleaseAndNULL(&_punkObj);
}
Win4Assert(_IXList.CountStubRefs() == 0);
}
_dwFlags |= RHFLAGS_DISCONNECTED; // turn on disconnected
_dwFlags &= ~RHFLAGS_PENDINGDISCONNECT; // turn off pending disconnect
_pChannel->AssertValid(TRUE, TRUE); // known disconnected
return S_OK;
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::FindIX, private
//
// Synopsis: finds or creates an interface object for the given
// interface.
//
// Returns: Pointer to CPSIX for requested interface; NULL if error
// ppv place to put proxy QI result; must be NULL on server
// side; may be NULL for client side.
// *phr always holds result of call.
//
//
// History: 23-Nov-93 Rickhi Created
// 7-May-94 CraigWi Added HRESULT out param
//
// Notes: this code is thread safe
//
//--------------------------------------------------------------------
CPSIX * CRemoteHdlr::FindIX(REFIID riid, void **ppv, DWORD dwFlag, HRESULT *phr)
{
TRACECALL(TRACE_MARSHAL, "CRemoteHdlr::FindIX");
AssertValid();
if (ppv)
*ppv = NULL; // null return value in case of error
// validate input parms. there are no proxies/stubs for these
// interfaces...
Win4Assert(! (IsEqualIID(riid, IID_NULL) ||
IsEqualIID(riid, IID_IUnknown) ||
IsEqualIID(riid, IID_IMarshal)));
// check for disconnected server on local side
if (_punkObj == NULL || _dwFlags & (RHFLAGS_DISCONNECTED))
{
*phr = CO_E_OBJNOTCONNECTED;
return NULL;
}
// single thread access to this routine
_mxs.Request();
// find the IX with the matching IID
CPSIX* pIX;
IUnknown *pUnkProxy = NULL;
if (IS_LOCAL_RH)
{
pIX = _IXList.FindStubIX(riid);
}
else
{
pIX = _IXList.FindProxyIX(riid, (void **)&pUnkProxy);
}
*phr = S_OK;
if (pIX == NULL)
{
// this will take a while. instead of blocking all RHs in the
// process, we will mark this RH as busy and release the mutex
_dwFlags |= RHFLAGS_GETTINGIX;
_mxs.Release();
// no IX in the list. figure out what we should do based on the
// flags that were passed in.
switch (dwFlag)
{
case FLG_QUERYINTERFACE:
// called by QueryInterface. we must call the remote object to
// tell it to instantiate a stub for the requested interface.
if ((*phr = _pChannel->QueryObjectInterface(riid)) != NOERROR)
{
break;
}
// fallthru into UNMARSHAL case
case FLG_UNMARSHAL:
// called by the unmarshalling code. the server side is known to
// have instantiated an interface stub already, so we just create
// an interface proxy here.
pIX = CreateInterfaceProxy(riid, (void **)&pUnkProxy, phr);
break;
case FLG_MARSHAL:
// called by marshalling code. we create an interface stub for
// the interface.
pIX = CreateInterfaceStub(riid, phr);
break;
}
// need to take the mutex again to protect the list.
_mxs.Request();
_dwFlags &= ~RHFLAGS_GETTINGIX;
// add the interface to the list
if (pIX)
{
_IXList.AddToList(pIX);
}
}
// release the mutex
_mxs.Release();
// if pUnkProxy and asked for one, return it; else release.
if (pUnkProxy != NULL)
{
Win4Assert(pIX != NULL); // only makes sense if we succeed
if (ppv != NULL)
*ppv = (void **)pUnkProxy; // transfer addref
else
pUnkProxy->Release(); // not needed
}
return pIX;
}
//+-------------------------------------------------------------------
//
// Function: CreateInterfaceProxy, public
//
// Synopsis: creates an interface proxy and wraps it in a CPSIX
//
// Exceptions: none
//
// History: 23-Nov-93 Rickhi Created
// 7-May-94 CraigWi Added HRESULT out param
//
//--------------------------------------------------------------------
CPSIX * CRemoteHdlr::CreateInterfaceProxy(REFIID riid, void **ppv, HRESULT *phr)
{
TRACECALL(TRACE_MARSHAL, "CreateInterfaceProxy");
AssertValid();
Assert((_dwFlags & RHFLAGS_LOCAL) == 0);
Win4Assert(_punkObj != NULL);
Win4Assert(ppv != NULL);
*ppv = NULL;
IPSFactoryBuffer *pIPSF = NULL;
IRpcProxyBuffer *pIProxy = NULL;
CPSIX *pIX = NULL;
CLSID clsid;
// map iid to classid
HRESULT sc = CoGetPSClsid(riid, &clsid);
if (SUCCEEDED(sc))
{
DWORD dwContext = IsRequestedByWOW(riid) ?
CLSCTX_INPROC_SERVER16 : CLSCTX_INPROC_SERVER | CLSCTX_PS_DLL;
// load the dll and get the PS class object
sc = ICoGetClassObject(clsid, dwContext, NULL, IID_IPSFactoryBuffer,
(void **)&pIPSF);
AssertOutPtrIface(sc, pIPSF);
if (SUCCEEDED(sc))
{
sc = pIPSF->CreateProxy(_punkObj, riid, &pIProxy, ppv);
AssertOutPtrIface(sc, pIProxy);
AssertOutPtrIface(sc, *ppv);
pIPSF->Release();
}
}
if (SUCCEEDED(sc))
{
// create an IX wrapper for this interface proxy
pIX = new CPSIX(riid, pIProxy);
if (pIX == NULL)
{
// release below will release proxy (and disconnect)
sc = E_OUTOFMEMORY;
((IUnknown *)*ppv)->Release();
*ppv = NULL;
}
// connect the proxy to the channel
pIProxy->Connect(_pChannel);
}
if (pIProxy)
pIProxy->Release();
#if DBG == 1
if (!pIX)
{
WCHAR wszGuid[50];
StringFromGUID2(riid, wszGuid, sizeof(wszGuid)/sizeof(WCHAR));
CairoleDebugOut((DEB_ERROR, "No Proxy for interface %ws; error = %lx\n" , wszGuid, sc));
}
#endif
Win4Assert((sc == S_OK) == (pIX != NULL));
Win4Assert((sc == S_OK) == (*ppv != NULL));
*phr = sc;
return pIX;
}
//+-------------------------------------------------------------------
//
// Function: CreateInterfaceStub, public
//
// Synopsis: creates an interface stub and wraps it in a CPSIX
//
// Exceptions: none
//
// History: 23-Nov-93 Rickhi Created
// 7-May-94 CraigWi Added HRESULT out param
//
//--------------------------------------------------------------------
CPSIX * CRemoteHdlr::CreateInterfaceStub(REFIID riid, HRESULT *phr)
{
TRACECALL(TRACE_MARSHAL, "CreateInterfaceStub");
AssertValid();
Assert(_dwFlags & RHFLAGS_LOCAL);
Win4Assert(_punkObj != NULL);
#if DBG == 1
// convert riid to string for debug messages
WCHAR wszGuid[50];
StringFromGUID2(riid, wszGuid, sizeof(wszGuid)/sizeof(WCHAR));
#endif
// first, make sure the object supports the given interface, so we
// dont waste a bunch of effort creating a stub if the interface is
// not supported.
IUnknown *punkIf = NULL;
HRESULT sc = _punkObj->QueryInterface(riid, (void **)&punkIf);
if (FAILED(sc))
{
// the server does not support the requested interface.
CairoleDebugOut((DEB_MARSHAL,
"Server Object does not support Interface:%ws\n", wszGuid));
*phr = sc;
return NULL;
}
// Determine whether the thing we're getting back is a custom WOW proxy
// or not so we know whether to load 16-bit custom proxy code if necessary
BOOL fWowCustom = FALSE;
IThunkManager *pthkmgr;
if (IsWOWThreadCallable() &&
g_pOleThunkWOW->GetThunkManager(&pthkmgr) == NOERROR)
{
fWowCustom = pthkmgr->IsCustom3216Proxy(punkIf, riid);
pthkmgr->Release();
}
punkIf->Release();
IPSFactoryBuffer *pIPSF = NULL;
IRpcStubBuffer *pIStub = NULL;
CPSIX *pIX = NULL;
CLSID clsid;
// map iid to classid
sc = CoGetPSClsid(riid, &clsid);
if (SUCCEEDED(sc))
{
DWORD dwContext;
dwContext = fWowCustom
? CLSCTX_INPROC_SERVER16 : CLSCTX_INPROC_SERVER | CLSCTX_PS_DLL;
// load the dll and get the PS class object
sc = ICoGetClassObject(clsid, dwContext, NULL, IID_IPSFactoryBuffer,
(void **)&pIPSF);
AssertOutPtrIface(sc, pIPSF);
if (SUCCEEDED(sc))
{
sc = pIPSF->CreateStub(riid, _punkObj, &pIStub);
#ifndef _CAIRO_ // BUGBUG [mikese] CMIDL stubs don't obey the convention
AssertOutPtrIface(sc, pIStub);
#endif
pIPSF->Release();
}
}
if (SUCCEEDED(sc))
{
pIX = new CPSIX(riid, pIStub);
if (pIX == NULL)
// release below will release stub (and disconnect)
sc = E_OUTOFMEMORY;
pIStub->Release();
}
else
{
CairoleDebugOut((DEB_WARN, "No Stub for interface %ws; error = %lx\n" , wszGuid, sc));
}
// verify that all is well
_IXList.AssertValid(IS_LOCAL_RH);
Win4Assert((sc == S_OK) == (pIX != NULL));
*phr = sc;
return pIX;
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::LookupStub, public
//
// Synopsis: Returns the stub for the requested IID; this pointer is
// not AddRefd and so callers must hold on to the rh longer
// than they hold this; i.e., the lifetime of a stub pointer
// from this call is no longer than the rh from which it was gotten
//
// Also returns an addref'd pointer on the server to stablize it
// during calls. If such a pointer is returned, the caller must
// call FinishCall with the same pointer when done.
//
// History: 27-Nov-93 AlexMit Created
// 31-May-94 CraigWi Now paired with FinishCall method.
//
//--------------------------------------------------------------------
IRpcStubBuffer *CRemoteHdlr::LookupStub(REFIID riid,
IUnknown **ppUnkServer, HRESULT *phr )
{
IRpcStubBuffer *pStub;
AssertValid();
Win4Assert(IS_LOCAL_RH);
// find the IX with the matching IID
// need to create the stub if it is not present;
// returns error if not connected
CPSIX* pIX = FindIX(riid, NULL, FLG_MARSHAL, phr);
AssertOutPtrParam(*phr, pIX);
if (pIX == NULL)
{
pStub = NULL;
if (ppUnkServer != NULL)
*ppUnkServer = NULL;
}
else
{
pStub = pIX->GetIStub();
if (ppUnkServer != NULL && pStub != NULL)
{
// get server interface from stub; this is a new use of
// DebugServerQueryInterface and we may want to change the
// name of that method someday.
HRESULT hr;
hr = pStub->DebugServerQueryInterface((void **)ppUnkServer);
AssertOutPtrIface(hr, *ppUnkServer);
if (hr == NOERROR)
{
// don't need to addref the pointer here since the stub will
// keep ptr until it is disconnected and we don't disconnect
// stub until after the calls complete;
// CODEWORK: assert in stub that DebugServerRelease
// doesn't happen after disconnect.
InterlockedIncrement(&_cCallsInProgress);
}
}
}
return pStub;
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::FinishCall, public
//
// Synopsis: Releases the pUnk previously returned from LookupStub
// and also finishes pending releases.
// If the object supports weak connections, we use
// IWeakRef to release and keep the server alive.
//
// Arguments: [pStub] -- The stub upon which the call was made
// [pUnkServer] -- The IUnknown on which the call was made
//
// History: 31-May-94 CraigWi Created
//
//--------------------------------------------------------------------
void CRemoteHdlr::FinishCall(IRpcStubBuffer *pStub, IUnknown *pUnkServer)
{
AssertValid();
Win4Assert(IS_LOCAL_RH);
Win4Assert((pStub == NULL) == (pUnkServer == NULL));
// check the pUnkServer argument since there could be a very, very rare
// case when we got a stub, but no server object.
if (pUnkServer != NULL)
{
pStub->DebugServerRelease(pUnkServer);
// one less pending call; check for pending disconnect
if (InterlockedDecrement(&_cCallsInProgress) == 0 &&
_dwFlags & RHFLAGS_PENDINGDISCONNECT)
{
DisconnectObject(0);
}
}
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::GetObjectId, public
//
// Synopsis: returns the remote handlers identity
//
// History: 10-Jan-94 AlexMit Created
//
//--------------------------------------------------------------------
void CRemoteHdlr::GetObjectID( OID * pOid )
{
AssertValid();
// this is only called on the client side presently and so this assert is ok
Win4Assert( _pstdID );
_pstdID->GetObjectID( pOid );
}
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::IsRequestedByWOW, public
//
// Synopsis: return TRUE if requested by WOW app
//
// History: 03-May-94 JohannP Created
//
//--------------------------------------------------------------------
BOOL CRemoteHdlr::IsRequestedByWOW(REFIID riid)
{
AssertValid();
BOOL fRet = FALSE;
if(IsWOWThreadCallable())
{
// dll used by WOW app
// query for the thunkmanager
IThunkManager *pThkMgr;
if (g_pOleThunkWOW->GetThunkManager(&pThkMgr) == NOERROR)
{
fRet = pThkMgr->IsIIDRequested(riid);
pThkMgr->Release();
}
else
{
Win4Assert(FALSE && L"pUnk in WOW does not support IThunkManager.");
}
}
return fRet;
}
#if DBG == 1
//+-------------------------------------------------------------------
//
// Member: CRemoteHdlr::AssertValid
//
// Synopsis: Validates that the state of the object is consistent.
//
// History: 25-Jan-94 CraigWi Created.
//
//--------------------------------------------------------------------
void CRemoteHdlr::AssertValid()
{
Win4Assert((_dwFlags & ~(RHFLAGS_LOCAL | RHFLAGS_GETTINGIX |
RHFLAGS_PENDINGDISCONNECT | RHFLAGS_DISCONNECTED)) == 0);
Win4Assert(_cReferences < 0x7fff && "RemoteHdlr ref count unreasonably high");
if (_punkObj != NULL)
{
// if we have a pointer, it should be valid.
Win4Assert(IsValidInterface(_punkObj));
// NOTE: can't AddRef/Release _punkObj since we may been in a
// situation where there are only weak refs
}
if (!IS_LOCAL_RH)
{
Win4Assert(_punkObj != NULL);
}
if (_pstdID != NULL)
{
Win4Assert(IsValidInterface(_pstdID));
if (IS_LOCAL_RH && _punkObj != NULL
&& (_dwFlags & RHFLAGS_PENDINGDISCONNECT) == 0)
// on remote side or when disconnected, GetServer returns NULL.
Win4Assert(_pstdID->GetServer(FALSE) == _punkObj);
}
Win4Assert(IsValidInterface(_pChannel));
_pChannel->AssertValid(FALSE, FALSE);
_IXList.AssertValid(IS_LOCAL_RH);
}
#endif // DBG == 1
//+-------------------------------------------------------------------
//
// Member: CIXList::FindProxyIX
//
// Synopsis: finds an interface proxy object supporting the specified
// interface and returns that pointer on the proxy. Returns
// NULL indicating no existing proxy supports the interface.
//
// History: 23-Nov-93 Rickhi Created
// 21-Jul-94 CraigWi Less abstract form of original FindIX
//
// Note: This code is called by the channel dispatch.
// Thread synchronization is the responsibility of the caller.
//
//--------------------------------------------------------------------
CPSIX *CIXList::FindProxyIX(REFIID riid, void **ppv)
{
// validate input parms
Win4Assert(!IsEqualIID(riid, IID_NULL));
// starting from the list head
CPSIX *pIX = first();
while (pIX)
{
// we should only find proxies
Win4Assert(pIX->_pIProxy != NULL);
if (pIX->_pIProxy->QueryInterface(riid, ppv) == NOERROR)
return pIX;
pIX = next(pIX);
}
*ppv = NULL;
return NULL;
}
//+-------------------------------------------------------------------
//
// Member: CIXList::FindStubIX
//
// Synopsis: finds an interface stub object supporting the specified
// interface. Returns NULL indicating no existing proxy
// supports the interface.
//
// History: 23-Nov-93 Rickhi Created
// 21-Jul-94 CraigWi Less abstract form of original FindIX
//
// Note: This code is called by the channel dispatch.
// Thread synchronization is the responsibility of the caller.
//
//--------------------------------------------------------------------
CPSIX *CIXList::FindStubIX(REFIID riid)
{
// validate input parms
Win4Assert(!IsEqualIID(riid, IID_NULL));
// starting from the list head
CPSIX *pIX = first();
while (pIX)
{
Win4Assert(pIX->_pIProxy == NULL);
if (IsEqualIID( riid, pIX->_iid ))
return pIX;
pIX = next(pIX);
}
return NULL;
}
//+-------------------------------------------------------------------
//
// Member: CIXList::ConnectProxies
//
// Synopsis: walks the list of proxy IXs and connects each of them
//
// History: 27-Jan-94 CraigWi Created
//
// Note: Thread synchronization is the responsibility of the caller.
//
//--------------------------------------------------------------------
void CIXList::ConnectProxies(IRpcChannelBuffer *pChannel)
{
// validate input parms
Win4Assert(IsValidInterface(pChannel));
// starting from the list head
CPSIX *pIX = first();
while (pIX)
{
Win4Assert(pIX->_pIProxy != NULL);
HRESULT sc = pIX->_pIProxy->Connect(pChannel);
Win4Assert(SUCCEEDED(sc) && "Proxy Connect Failed");
pIX = next(pIX);
}
}
//+-------------------------------------------------------------------
//
// Member: CIXList::CountStubRefs
//
// Synopsis: walks the list of IXs and sum up the refs
//
// History: 1-June-94 CraigWi Created
//
// Note: Thread synchronization is the responsibility of the caller.
//
// CODEWORK: could keep running sum in IXList rather than computing it
//
//--------------------------------------------------------------------
DWORD CIXList::CountStubRefs(void)
{
// starting from the list head
CPSIX *pIX = first();
DWORD cRefs = 0;
while (pIX)
{
Win4Assert(pIX->_pIProxy == NULL);
Win4Assert(pIX->_cRefs == -1 || pIX->_cRefs == pIX->_pStub->CountRefs());
cRefs += pIX->_pStub->CountRefs();
pIX = next(pIX);
}
return cRefs;
}
//+-------------------------------------------------------------------
//
// Member: CIXList::DisconnectStubs
//
// Synopsis: walks the list of IXs and disconnects each of them
//
// History: 23-Nov-93 Rickhi Created
//
// Note: Thread synchronization is the responsibility of the caller.
//
//--------------------------------------------------------------------
void CIXList::DisconnectStubs(void)
{
// starting from the list head
CPSIX *pIX = first();
while (pIX)
{
Win4Assert(pIX->_pIProxy == NULL);
pIX->_pStub->Disconnect();
#if DBG==1
pIX->_cRefs = (DWORD)-1;
#endif
pIX = next(pIX);
}
}
//+-------------------------------------------------------------------
//
// Member: CIXList::DisconnectProxies
//
// Synopsis: walks the list of IXs and disconnects each of them
//
// History: 23-Nov-93 Rickhi Created
//
// Note: Thread synchronization is the responsibility of the caller.
//
//--------------------------------------------------------------------
void CIXList::DisconnectProxies(void)
{
// starting from the list head
CPSIX *pIX = first();
while (pIX)
{
Win4Assert(pIX->_pIProxy != NULL);
pIX->_pIProxy->Disconnect();
pIX = next(pIX);
}
}
#if DBG == 1
//+-------------------------------------------------------------------
//
// Member: CIXList::AssertValid
//
// Synopsis: Validates that the state of the object is consistent.
//
// History: 25-Jan-94 CraigWi Created.
//
//--------------------------------------------------------------------
void CIXList::AssertValid(BOOL fLocal)
{
// check IX list
CPSIX *pIX = first();
BOOL fStubsConnected;
// determine if all stubs are supposed to connected or disconnected
// (this shouldn't be called during the disconnect steps)
if (pIX != NULL && pIX->_pIProxy == NULL && pIX->_cRefs != -1)
fStubsConnected = TRUE;
else
fStubsConnected = FALSE;
while (pIX)
{
// local means stubs and we must have only stubs or only proxies
Win4Assert(!!fLocal == (pIX->_pIProxy == NULL));
if (pIX->_pIProxy)
{
Win4Assert(IsValidInterface(pIX->_pIProxy));
Win4Assert(pIX->_pStub == NULL);
Win4Assert(IsEqualGUID(pIX->_iid, IID_NULL));
}
else
{
Win4Assert(IsValidInterface(pIX->_pStub));
if (pIX->_cRefs != -1)
{
Win4Assert(fStubsConnected);
Win4Assert(pIX->_cRefs == pIX->_pStub->CountRefs());
}
else
{
Win4Assert(!fStubsConnected);
Win4Assert(0 == pIX->_pStub->CountRefs());
}
}
pIX = next(pIX);
}
}
#endif // DBG == 1