2020-09-30 17:12:29 +02:00

4975 lines
158 KiB
C++

//+-------------------------------------------------------------------
//
// File: marshal.cxx
//
// Contents: class implementing standard COM interface marshaling
//
// Classes: CStdMarshal
//
// History: 20-Feb-95 Rickhi Created
//
// DCOMWORK: (maybe) implement Extended form marshal packet
//
// PERFWORK: during unmarshal and RMD compare the MOXID in the STDOBJREF
// to the one for the current apartment. If equal, then i know the IPID is
// just an index into the IPID table and i can index into it, grab the
// channel ptr and hence the stdid ptr and do very fast unmarshal or RMD
// with no table lookup or list walking.
//
//--------------------------------------------------------------------
#include <ole2int.h>
#include <marshal.hxx> // CStdMarshal
#include <ipidtbl.hxx> // CIPIDTable, COXIDTable, CMIDTable
#include <riftbl.hxx> // CRIFTable
#include <resolver.hxx> // CRpcResolver
#include <stdid.hxx> // CStdIdentity
#include <channelb.hxx> // CRpcChannelBuffer
#include <callctrl.hxx> // CAptRpcChnl, CSrvCallCtrl
#include <scm.h> // CLSCTX_PS_DLL
#include <service.hxx> // SASIZE
#include <locks.hxx> // LOCK/UNLOCK etc
#include <thunkapi.hxx> // GetAppCompatabilityFlags
#if DBG==1
// this flag and interface are used in debug to enable simpler testing
// of the esoteric NonNDR stub code feature.
BOOL gfFakeNonNDR = FALSE;
const GUID IID_ICube =
{0x00000139,0x0001,0x0008,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
#endif // DBG
// BUGBUG: this is not quite reliable enough. Maybe best solution is
// CoGetCurrentProcessId plus sequence number.
LONG gIPIDSeqNum = 0;
// mappings from MSHLFLAGS to STDOBJREF flags
static ULONG mapMFtoSORF[] =
{
SORF_NULL, // MSHLFLAGS_NORMAL
SORF_NULL, // MSHLFLAGS_TABLESTRONG
SORF_TBLWEAK // MSHLFLAGS_TABLEWEAK
};
// NULL resolver string array
DUALSTRINGARRAY saNULL = {0,0};
// number of remote AddRefs to acquire when we need more.
#define REM_ADDREF_CNT 5
// out internal psclass factory implementation
EXTERN_C HRESULT PrxDllGetClassObject(REFCLSID clsid, REFIID iid, void **ppv);
// structure used to post a delayed remote release call to ourself.
typedef struct tagPOSTRELRIFREF
{
OXIDEntry *pOXIDEntry; // server OXIDEntry
USHORT cRifRef; // count of entries in arRifRef
REMINTERFACEREF arRifRef; // array of REMINTERFACEREFs
} POSTRELRIFREF;
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::CStdMarshal/Init, public
//
// Synopsis: constructor/initializer of a standard marshaler
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
CStdMarshal::CStdMarshal() : _dwFlags(0), _pChnl(NULL)
{
// Caller must call Init before doing anything! This just makes it
// easier for the identity object to figure out the init parameters
// before initializing us.
}
void CStdMarshal::Init(IUnknown *punkObj, CStdIdentity *pStdId,
REFCLSID rclsidHandler, DWORD dwFlags)
{
ASSERT_LOCK_DONTCARE // may be released if def handler calls CreateIdHdlr
// server side we need to do the FirstMarshal work.
// client side we assume disconnected until we connect the first IPIDEntry
// and assume NOPING until we see any interface that needs pinging
_dwFlags = dwFlags;
_dwFlags |= (ServerSide()) ? SMFLAGS_FIRSTMARSHAL
: SMFLAGS_DISCONNECTED | SMFLAGS_NOPING;
_pFirstIPID = NULL;
_cIPIDs = 0;
_pStdId = pStdId;
_pChnl = NULL;
_cNestedCalls = 0;
_cTableRefs = 0;
_dwMarshalTime = 0;
_clsidHandler = rclsidHandler;
_pSecureRemUnk = NULL;
ComDebOut((DEB_MARSHAL,"CStdMarshal %s New this:%x pStdId:%x punkObj:%x\n",
(ClientSide()) ? "CLIENT" : "SERVER", this, pStdId, punkObj));
AssertValid();
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::~CStdMarshal, public
//
// Synopsis: destructor of a standard marshaler
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
CStdMarshal::~CStdMarshal()
{
ComDebOut((DEB_MARSHAL, "CStdMarshal %s Deleted this:%x\n",
(ClientSide()) ? "CLIENT" : "SERVER", this));
ASSERT_LOCK_RELEASED
if (ClientSide())
{
// Due to backward compatibility, we are not allowed to release
// interface proxies in Disconnect since the client might try to
// reconnect later and expects the same interface pointer values.
// Since we are going away now, we go release the proxies.
ReleaseCliIPIDs();
if (_pSecureRemUnk != NULL)
{
_pSecureRemUnk->Release();
}
}
if (_pChnl)
{
// release the channel
_pChnl->Release();
}
ASSERT_LOCK_RELEASED
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::GetUnmarshalClass, public
//
// Synopsis: returns the clsid of the standard marshaller
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
STDMETHODIMP CStdMarshal::GetUnmarshalClass(REFIID riid, LPVOID pv,
DWORD dwDestCtx, LPVOID pvDestCtx, DWORD mshlflags, LPCLSID pClsid)
{
AssertValid();
ASSERT_LOCK_RELEASED
*pClsid = CLSID_StdMarshal;
return S_OK;
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::GetMarshalSizeMax, public
//
// Synopsis: Returns an upper bound on the amount of data for
// a standard interface marshal.
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
STDMETHODIMP CStdMarshal::GetMarshalSizeMax(REFIID riid, LPVOID pv,
DWORD dwDestCtx, LPVOID pvDestCtx, DWORD mshlflags, LPDWORD pSize)
{
AssertValid();
Win4Assert(gdwPsaMaxSize != 0);
ASSERT_LOCK_RELEASED
*pSize = sizeof(OBJREF) + gdwPsaMaxSize;
return S_OK;
}
//+-------------------------------------------------------------------
//
// Function: MarshalObjRef, private
//
// Synopsis: Marshals interface into the objref.
//
// Arguements: [objref] - object reference
// [riid] - interface id to marshal
// [pv] - interface to marshal
// [mshlflags] - marshal flags
//
// Algorithm: Get the correct standard identity and ask it to do
// all the work.
//
// History: 25-Mar-95 AlexMit Created
//
//--------------------------------------------------------------------
INTERNAL MarshalObjRef(OBJREF &objref, REFIID riid, void *pv, DWORD mshlflags)
{
TRACECALL(TRACE_MARSHAL, "MarshalObjRef");
ComDebOut((DEB_MARSHAL, "MarshalObjRef: riid:%I pv:%x flags:%x\n",
&riid, pv, mshlflags));
ASSERT_LOCK_RELEASED
HRESULT hr = InitChannelIfNecessary();
if (SUCCEEDED(hr))
{
// Find or create the StdId for this object. We need to get a strong
// reference to guard against an incoming last release on another
// thread which would cause us to Disconnect this StdId.
DWORD dwFlags = IDLF_CREATE | IDLF_STRONG;
dwFlags |= (mshlflags & MSHLFLAGS_NOPING) ? IDLF_NOPING : 0;
CStdIdentity *pStdID;
hr = LookupIDFromUnk((IUnknown *)pv, dwFlags, &pStdID);
if (hr == NOERROR)
{
hr = pStdID->MarshalObjRef(objref, riid, pv, mshlflags);
pStdID->DecStrongCnt(TRUE); // fKeepAlive
}
}
ASSERT_LOCK_RELEASED
ComDebOut((DEB_MARSHAL, "MarshalObjRef: hr:%x\n", hr));
return hr;
}
//+-------------------------------------------------------------------
//
// Function: MarshalInternalObjRef, private
//
// Synopsis: Marshals an internal interface into the objref.
//
// Arguements: [objref] - object reference
// [riid] - interface id to marshal
// [pv] - interface to marshal
// [mshlflags] - marshal flags
// [ppStdId] - StdId to return (may be NULL)
//
// Algorithm: Create a StdIdentity and ask it to do the work.
//
// Notes: This differs from the normal MarshalObjRef in that it does
// not look in the OID table for an already marshaled interface,
// nor does it register the marshaled interface in the OID table.
// This is used for internal interfaces such as the IObjServer
// and IRemUnknown.
//
// History: 25-Oct-95 Rickhi Created
//
//--------------------------------------------------------------------
INTERNAL MarshalInternalObjRef(OBJREF &objref, REFIID riid, void *pv,
DWORD mshlflags, void **ppStdId)
{
TRACECALL(TRACE_MARSHAL, "MarshalInternalObjRef");
ComDebOut((DEB_MARSHAL, "MarshalInternalObjRef: riid:%I pv:%x flags:%x\n",
&riid, pv, mshlflags));
ASSERT_LOCK_RELEASED
HRESULT hr = InitChannelIfNecessary();
if (SUCCEEDED(hr))
{
if (!IsEqualGUID(riid, IID_IRundown))
{
// NOTE: make sure the local OXID is registered with the resolver.
// See the discussion on the Chicken and Egg problem in ipidtbl.cxx
// COXIDTable::GetLocalEntry for why this is necessary.
LOCK
MOID moid;
hr = gResolver.ServerGetPreRegMOID(&moid);
UNLOCK
}
if (SUCCEEDED(hr))
{
// Find or create the StdId for this object. We need to get a strong
// reference to guard against an incoming last release on another
// thread which would cause us to Disconnect this StdId.
IUnknown *pUnkId; // ignored
CStdIdentity *pStdId = new CStdIdentity(STDID_SERVER, NULL,
(IUnknown *)pv, &pUnkId);
if (pStdId != NULL)
{
hr = pStdId->MarshalObjRef(objref, riid, pv, mshlflags);
if (SUCCEEDED(hr) && ppStdId)
{
*ppStdId = (void *)pStdId;
}
else
{
pStdId->Release();
}
}
else
{
hr = E_OUTOFMEMORY;
}
}
}
ASSERT_LOCK_RELEASED
ComDebOut((DEB_MARSHAL, "MarshalInternalObjRef: hr:%x\n", hr));
return hr;
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::MarshalInterface, public
//
// Synopsis: marshals the interface into the stream.
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
STDMETHODIMP CStdMarshal::MarshalInterface(IStream *pStm, REFIID riid,
LPVOID pv, DWORD dwDestCtx, LPVOID pvDestCtx, DWORD mshlflags)
{
ComDebOut((DEB_MARSHAL,
"CStdMarshal::MarshalInterface this:%x pStm:%x riid:%I pv:%x dwCtx:%x pvCtx:%x flags:%x\n",
this, pStm, &riid, pv, dwDestCtx, pvDestCtx, mshlflags));
AssertValid();
ASSERT_LOCK_RELEASED
// Marshal the interface into an objref, then write the objref
// into the provided stream.
OBJREF objref;
HRESULT hr = MarshalObjRef(objref, riid, pv, mshlflags);
if (SUCCEEDED(hr))
{
// write the objref into the stream
hr = WriteObjRef(pStm, objref, dwDestCtx);
if (FAILED(hr))
{
// undo whatever we just did, ignore error from here since
// the stream write error supercedes any error from here.
ReleaseMarshalObjRef(objref);
}
// free resources associated with the objref.
FreeObjRef(objref);
}
ASSERT_LOCK_RELEASED
ComDebOut((DEB_MARSHAL,"CStdMarshal::MarshalInterface this:%x hr:%x\n",
this, hr));
return hr;
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::MarshalObjRef, public
//
// Synopsis: marshals the interface into the objref.
//
// History: 25-Mar-95 AlexMit Seperated from MarshalInterface
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::MarshalObjRef(OBJREF &objref, REFIID riid,
LPVOID pv, DWORD mshlflags)
{
ComDebOut((DEB_MARSHAL,
"CStdMarshal::MarsalObjRef this:%x riid:%I pv:%x flags:%x\n",
this, &riid, pv, mshlflags));
AssertValid();
// validate the parameters. we dont allow TABLE cases if we are
// a client side object.
if ((mshlflags & MSHLFLAGS_TABLE) && ClientSide())
return E_INVALIDARG;
// count of Refs we are handing out. In the table cases we pass out
// zero refs because we dont know how many times it will be unmarshaled
// (and hence how many references to count). Zero refs will cause the
// client to call back and ask for more references if it does not already
// have any (which has the side effect of making sure the object still
// exists, which is required by RunningObjectTable).
ULONG cRefs = (mshlflags & MSHLFLAGS_TABLE) ? 0 :
(ClientSide()) ? 1 : REM_ADDREF_CNT;
ASSERT_LOCK_RELEASED
LOCK
HRESULT hr = PreventDisconnect();
if (SUCCEEDED(hr))
{
// The first time through we have some extra work to do so go off
// and do that now. Next time we can just bypass all that work.
if (_dwFlags & SMFLAGS_FIRSTMARSHAL)
{
hr = FirstMarshal((IUnknown *)pv, mshlflags);
}
if (SUCCEEDED(hr))
{
// Create the IPID table entry. On the server side this may
// cause the creation of an interface stub, on the client side
// it may just take away one of our references or it may call
// the server to get more references for the interface being
// marshaled.
IPIDEntry *pIPIDEntry;
hr = MarshalIPID(riid, cRefs, mshlflags, &pIPIDEntry);
if (SUCCEEDED(hr))
{
// fill in the rest of the OBJREF
FillObjRef(objref, cRefs, mshlflags, pIPIDEntry);
}
}
}
UNLOCK
ASSERT_LOCK_RELEASED
// it is now OK to allow real disconnects in.
HRESULT hr2 = HandlePendingDisconnect(hr);
if (FAILED(hr2) && SUCCEEDED(hr))
{
// a disconnect came in while marshaling. The ObjRef has a
// reference to the OXIDEntry so go free that now.
FreeObjRef(objref);
}
ComDebOut((DEB_MARSHAL, "CStdMarshal::MarshalObjRef this:%x hr:%x\n",
this, hr2));
return hr2;
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::FillObjRef, private
//
// Synopsis: Fill in the fields of an OBJREF
//
// History: 21-Sep-95 Rickhi Created
//
//+-------------------------------------------------------------------
void CStdMarshal::FillObjRef(OBJREF &objref, ULONG cRefs, DWORD mshlflags,
IPIDEntry *pIPIDEntry)
{
ComDebOut((DEB_MARSHAL, "FillObjRef pObjRef:%x\n", &objref));
ASSERT_LOCK_HELD
AssertDisconnectPrevented();
Win4Assert(pIPIDEntry);
OXIDEntry **ppOXIDEntry;
// first, fill in the STDOBJREF section
STDOBJREF *pStd = &ORSTD(objref).std;
FillSTD(pStd, cRefs, mshlflags, pIPIDEntry);
// next fill in the rest of the OBJREF
objref.signature = OBJREF_SIGNATURE; // 'MEOW'
objref.iid = pIPIDEntry->iid; // interface iid
if (_dwFlags & SMFLAGS_HANDLER)
{
// handler form, copy in the clsid
objref.flags = OBJREF_HANDLER;
ORHDL(objref).clsid = _clsidHandler;
ppOXIDEntry = (OXIDEntry **) &ORHDL(objref).saResAddr;
}
else
{
objref.flags = OBJREF_STANDARD;
ppOXIDEntry = (OXIDEntry **) &ORSTD(objref).saResAddr;
}
// TRICK: in order to keep the objref a fixed size internally,
// we use the saResAddr.size field as a ptr to the OXIDEntry. We
// pay attention to this in ReadObjRef, WriteObjRef, and FreeObjRef.
*ppOXIDEntry = pIPIDEntry->pOXIDEntry;
Win4Assert(*ppOXIDEntry != NULL);
IncOXIDRefCnt(*ppOXIDEntry);
ASSERT_LOCK_HELD
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::FillSTD, public
//
// Synopsis: Fill in the STDOBJREF fields of an OBJREF
//
// History: 21-Sep-95 Rickhi Created
//
//+-------------------------------------------------------------------
void CStdMarshal::FillSTD(STDOBJREF *pStd, ULONG cRefs, DWORD mshlflags,
IPIDEntry *pIPIDEntry)
{
// fill in the STDOBJREF to return to the caller.
pStd->flags = mapMFtoSORF[mshlflags & MSHLFLAGS_TABLE];
pStd->flags |= (pIPIDEntry->dwFlags & IPIDF_NOPING) ? SORF_NOPING : 0;
pStd->flags |= (pIPIDEntry->dwFlags & IPIDF_NONNDRSTUB) ? SORF_NONNDR : 0;
pStd->cPublicRefs = cRefs;
pStd->ipid = pIPIDEntry->ipid;
OIDFromMOID(_pStdId->GetOID(), &pStd->oid);
OXIDFromMOXID(pIPIDEntry->pOXIDEntry->moxid, &pStd->oxid);
ValidateSTD(pStd);
DbgDumpSTD(pStd);
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::FirstMarshal, private
//
// Synopsis: Does some first-time server side marshal stuff
//
// Parameters: [pUnk] - interface being marshalled
// [mshlflags] - flags for marshaling
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::FirstMarshal(IUnknown *pUnk, DWORD mshlflags)
{
ComDebOut((DEB_MARSHAL,
"CStdMarshal::FirstMarshal this:%x pUnk:%x\n", this, pUnk));
Win4Assert(ServerSide());
Win4Assert(_dwFlags & SMFLAGS_FIRSTMARSHAL);
Win4Assert(_pChnl == NULL);
AssertValid();
AssertDisconnectPrevented();
ASSERT_LOCK_HELD
// have now executed this code so dont do it again.
_dwFlags &= ~SMFLAGS_FIRSTMARSHAL;
if (mshlflags & MSHLFLAGS_NOPING)
{
// if the first interface is marked as NOPING, then all interfaces
// for the object are treated as NOPING, otherwise, all interfaces
// are marked as PING. MakeSrvIPIDEntry will look at _dwFlags to
// determine whether to mark each IPIDEntry as NOPING or not.
_dwFlags |= SMFLAGS_NOPING;
}
// get our local OXID. This should have already been created, and
// so wont cause the LOCK to be released.
OXIDEntry *pOXIDEntry;
HRESULT hr = gOXIDTbl.GetLocalEntry(&pOXIDEntry);
if (SUCCEEDED(hr))
{
// create a channel for this object.
CRpcChannelBuffer *pChnl;
hr = CreateChannel(pOXIDEntry, 0, GUID_NULL, GUID_NULL, &pChnl);
}
ASSERT_LOCK_HELD
AssertDisconnectPrevented();
ComDebOut((DEB_MARSHAL,
"CStdMarshal::FirstMarshal this:%x hr:%x\n", this, hr));
return hr;
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::MarshalIPID, private
//
// Synopsis: finds or creates an interface stub and IPID entry
// for the given object interface.
//
// Arguments: [riid] - interface to look for
// [cRefs] - count of references wanted
// [mshlflags] - marshal flags
// [ppEntry] - place to return IPIDEntry ptr
//
// Returns: S_OK if succeeded
//
// History: 20-Feb-95 Rickhi Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::MarshalIPID(REFIID riid, ULONG cRefs, DWORD mshlflags,
IPIDEntry **ppIPIDEntry)
{
TRACECALL(TRACE_MARSHAL, "CStdMarshal::MarshalIPID");
ComDebOut((DEB_MARSHAL,
"CStdMarshal::MarshalIPID this:%x riid:%I cRefs:%x mshlflags:%x ppEntry:%x\n",
this, &riid, cRefs, mshlflags, ppIPIDEntry));
AssertValid();
AssertDisconnectPrevented();
ASSERT_LOCK_HELD
// validate input parms.
Win4Assert(!(IsEqualIID(riid, IID_NULL) || IsEqualIID(riid, IID_IMarshal)));
// look for an existing IPIDEntry for the requested interface
IPIDEntry *pEntry;
HRESULT hr = FindIPIDEntry(riid, &pEntry);
if (FAILED(hr))
{
// no entry currently exists. on the server side we try to create one.
// on the client side we do a remote QI for the requested interface.
if (ServerSide())
{
// this call fail if we are disconnected during a yield.
hr = MakeSrvIPIDEntry(riid, &pEntry);
}
else
{
hr = RemQIAndUnmarshal(1, (GUID *)&riid, NULL);
if (SUCCEEDED(hr))
{
hr = FindIPIDEntry(riid, &pEntry);
}
}
}
if (SUCCEEDED(hr))
{
// REFCOUNTING:
if (ServerSide())
{
// remember the latest marshal time so we can tell if the ping
// server has run us down too early. This can happen when an
// existing client dies and we remarshal the interface just
// moments before the pingserver tells us the first guy is gone
// and before the new client has had time to unmarshal and ping.
_dwMarshalTime = GetCurrentTime();
// inc the refcnt for the IPIDEntry and optionaly the stdid. Note
// that for TABLE marshals cRefs is 0 (that's the number that gets
// placed in the packet) but we do want a reference so we ask for
// 1 here. ReleaseMarshalData will undo the 1.
ULONG cRefs2 = (mshlflags & MSHLFLAGS_TABLE) ? 1 : cRefs;
IncSrvIPIDCnt(pEntry, cRefs2, 0, NULL, mshlflags);
}
else // client side,
{
// we dont support marshaling weak refs on the client side, though
// we do support marshaling strong from a weak client by going to
// the server and getting a strong reference.
Win4Assert(!(mshlflags & MSHLFLAGS_WEAK));
if (cRefs >= pEntry->cStrongRefs)
{
// need more references than we own, go get more from server
// to satisfy the marshal. Get a few extra refs for ourselves
// unless we are a weak client.
ULONG cExtraRefs = (_dwFlags & SMFLAGS_WEAKCLIENT)
? 0 : REM_ADDREF_CNT;
hr = RemoteAddRef(pEntry, pEntry->pOXIDEntry, cRefs + cExtraRefs, 0);
if (SUCCEEDED(hr))
{
// add in the extra references we asked for (if any).
pEntry->cStrongRefs += cExtraRefs;
}
}
else
{
// we have enough references to satisfy this request (and still
// keep some for ourselves), just subtract from the IPIDEntry
pEntry->cStrongRefs -= cRefs;
}
// mark this object as having been client-side marshaled so
// that we can tell the resolver whether or not it needs to
// ping this object if we release it before the OID is registered.
_dwFlags |= SMFLAGS_CLIENTMARSHALED;
}
// do some debug stuff
ValidateIPIDEntry(pEntry);
ComDebOut((DEB_MARSHAL, "pEntry:%x cRefs:%x cStdId:%x\n", pEntry,
pEntry->cStrongRefs, _pStdId->GetRC()));
}
*ppIPIDEntry = pEntry;
ASSERT_LOCK_HELD
AssertDisconnectPrevented();
ComDebOut((DEB_MARSHAL, "CStdMarshal::MarshalIPID hr:%x pIPIDEntry\n", hr, *ppIPIDEntry));
return hr;
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::UnmarshalInterface, public
//
// Synopsis: Unmarshals an Interface from a stream.
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
STDMETHODIMP CStdMarshal::UnmarshalInterface(LPSTREAM pStm,
REFIID riid, VOID **ppv)
{
ComDebOut((DEB_MARSHAL, "CStdMarshal::UnmarsalInterface this:%x pStm:%x riid:%I\n",
this, pStm, &riid));
AssertValid();
ASSERT_LOCK_RELEASED
// read the objref from the stream and find or create an instance
// of CStdMarshal for its OID. Then ask that guy to do the rest of
// the unmarshal (create the interface proxy)
OBJREF objref;
HRESULT hr = ReadObjRef(pStm, objref);
if (SUCCEEDED(hr))
{
// pass objref to subroutine to unmarshal the objref
hr = ::UnmarshalObjRef(objref, ppv);
// release the objref we read
FreeObjRef(objref);
}
ASSERT_LOCK_RELEASED
ComDebOut((DEB_MARSHAL,
"UnmarsalInterface this:%x pv:%x hr:\n", this, *ppv, hr));
return hr;
}
//+-------------------------------------------------------------------
//
// Function: UnmarshalObjRef, private
//
// Synopsis: UnMarshals interface from objref.
//
// Arguements: [objref] - object reference
// [ppv] - proxy
//
// Algorithm: Get the correct standard identity and ask it to do
// all the work.
//
// History: 25-Mar-95 AlexMit Created
//
//--------------------------------------------------------------------
INTERNAL UnmarshalObjRef(OBJREF &objref, void **ppv)
{
ASSERT_LOCK_RELEASED
CStdMarshal *pStdMshl;
HRESULT hr = FindStdMarshal(objref, &pStdMshl);
if (SUCCEEDED(hr))
{
// pass objref to subroutine to unmarshal the objref
hr = pStdMshl->UnmarshalObjRef(objref, ppv);
CALLHOOKOBJECTCREATE(S_OK,ORHDL(objref).clsid,objref.iid,(IUnknown **)ppv);
pStdMshl->Release();
}
else
{
// we could not create the indentity or handler, release the
// marshaled objref.
ReleaseMarshalObjRef(objref);
}
ASSERT_LOCK_RELEASED
return hr;
}
//+-------------------------------------------------------------------
//
// Function: ChkIfLocalOID, private
//
// Synopsis: Helper function for UnmarshalInternalObjRef & FindStdMarshal
//
// Arguements: [objref] - object reference
// [ppStdMshl] - CStdMarshal returned
//
// Algorithm: Read the objref, get the OID. If we already have an identity
// for this OID return it AddRefd.
//
// History: 21-May-95 MurthyS Created.
//
//--------------------------------------------------------------------
INTERNAL_(BOOL) ChkIfLocalOID(OBJREF &objref, CStdIdentity **ppStdId)
{
STDOBJREF *pStd = &ORSTD(objref).std;
BOOL flocal = FALSE;
ComDebOut((DEB_MARSHAL, "ChkIfLocalOID (IN) poid: %x\n", &pStd->oid));
Win4Assert((*ppStdId == NULL) && "ChkIfLocalOID: pStdId != NULL");
ASSERT_LOCK_RELEASED
LOCK
OXIDEntry *pOXIDEntry = GetOXIDFromObjRef(objref);
if (pOXIDEntry == GetLocalOXIDEntry())
{
flocal = TRUE;
// OXID is for this apartment, look IPID up in the IPIDTable
// directly, and extract the CStdMarshal from it.
IPIDEntry *pEntry = gIPIDTbl.LookupIPID(pStd->ipid);
if (pEntry && pEntry->pChnl)
{
// get the Identity
*ppStdId = pEntry->pChnl->GetStdId();
(*ppStdId)->AddRef();
}
}
UNLOCK
ASSERT_LOCK_RELEASED
return flocal;
}
//+-------------------------------------------------------------------
//
// Function: UnmarshalInternalObjRef, private
//
// Synopsis: UnMarshals an internally-used interface from objref.
//
// Arguements: [objref] - object reference
// [ppv] - proxy
//
// Algorithm: Create a StdId and ask it to do the work.
//
// Notes: This differs from UnmarshalObjRef in that it does not lookup
// or register the OID. This saves a fair amount of work and
// avoids initializing the OID table.
//
// History: 25-Oct-95 Rickhi Created
//
//--------------------------------------------------------------------
INTERNAL UnmarshalInternalObjRef(OBJREF &objref, void **ppv)
{
ASSERT_LOCK_RELEASED
HRESULT hr = S_OK;
CStdIdentity *pStdId = NULL;
if (ChkIfLocalOID(objref, &pStdId))
{
if (pStdId)
{
// set OID in objref to match that in returned std identity
OIDFromMOID(pStdId->GetOID(), &ORSTD(objref).std.oid);
}
else
{
hr = CO_E_OBJNOTCONNECTED;
}
}
else
{
ASSERT_LOCK_RELEASED
hr = CreateIdentityHandler(NULL, ORSTD(objref).std.flags,
IID_IStdIdentity, (void **)&pStdId);
}
if (SUCCEEDED(hr))
{
// pass objref to subroutine to unmarshal the objref. tell StdId not
// to register the OID in the OID table.
pStdId->IgnoreOID();
hr = pStdId->UnmarshalObjRef(objref, ppv);
CALLHOOKOBJECTCREATE(S_OK,ORHDL(objref).clsid,objref.iid,(IUnknown **)ppv);
pStdId->Release();
}
ASSERT_LOCK_RELEASED
return hr;
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::UnmarshalObjRef, private
//
// Synopsis: unmarshals the objref. Called by CoUnmarshalInterface,
// UnmarshalObjRef APIs, and UnmarshalInterface method.
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::UnmarshalObjRef(OBJREF &objref, void **ppv)
{
ComDebOut((DEB_MARSHAL, "CStdMarshal::UnmarsalObjRef this:%x objref:%x riid:%I\n",
this, &objref, &objref.iid));
AssertValid();
STDOBJREF *pStd = &ORSTD(objref).std;
OXIDEntry *pOXIDEntry = GetOXIDFromObjRef(objref);
DbgDumpSTD(pStd);
ASSERT_LOCK_RELEASED
LOCK
// Prevent a disconnect from occuring while unmarshaling the
// interface since we may have to yield the ORPC lock.
HRESULT hr = PreventPendingDisconnect();
if (SUCCEEDED(hr))
{
if (objref.flags & OBJREF_HANDLER)
{
// handler form, extract the handler clsid and set our flags
_dwFlags |= SMFLAGS_HANDLER;
_clsidHandler = ORHDL(objref).clsid;
}
// if no OID registered yet, do that now. only possible on client side
// during reconnect.
MOID moid;
MOIDFromOIDAndMID(pStd->oid, pOXIDEntry->pMIDEntry->mid, &moid);
hr = _pStdId->SetOID(moid);
if (SUCCEEDED(hr))
{
// find or create the IPID entry for the interface. On the client
// side this may cause the creation of an interface proxy. It will
// also manipulate the reference counts.
hr = UnmarshalIPID(objref.iid, pStd, pOXIDEntry, ppv);
}
}
UNLOCK
ASSERT_LOCK_RELEASED
if (ClientSide())
{
if (SUCCEEDED(hr))
{
if (_pStdId->IsAggregated())
{
// we are currently holding a proxy pointer. If aggregated,
// the controlling unknown may want to override this pointer
// with his own version, so issue a QI to give it that chance.
IUnknown *pUnk = (IUnknown *)*ppv;
#ifdef WX86OLE
if (gcwx86.IsN2XProxy(pUnk))
{
// Tell wx86 thunk layer to thunk as IUnknown
gcwx86.SetStubInvokeFlag((BOOL)1);
}
#endif
hr = pUnk->QueryInterface(objref.iid, ppv);
pUnk->Release();
}
}
else
{
// cleanup our state on failure (only meaningful on client side,
// since if the unmarshal failed on the server side, the interface
// is already cleaned up).
ReleaseMarshalObjRef(objref);
}
}
// now let pending disconnect through. on server-side, ignore any
// error from HPD and pay attention only to the unmarshal result, since
// a successful unmarshal on the server side may result in a disconnect
// if that was the last external reference to the object.
HRESULT hr2 = HandlePendingDisconnect(hr);
if (FAILED(hr2) && ClientSide())
{
if (SUCCEEDED(hr))
{
// a disconnect came in while unmarshaling. ppv contains an
// AddRef'd interface pointer so go Release that now.
((IUnknown *)*ppv)->Release();
}
hr = hr2;
}
ComDebOut((DEB_MARSHAL, "CStdMarshal::UnmarsalObjRef this:%x hr:%x\n",
this, hr));
return hr;
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::UnmarshalIPID, private
//
// Synopsis: finds or creates an interface proxy for the given
// interface. may also do a remote query interface.
//
// Arguements: [riid] - the interface to return
// [std] - standard objref to unmarshal from
// [pOXIDEntry] - ptr to OXIDEntry of the server
// [ppv] - interface ptr of type riid returned, AddRef'd
//
// Returns: S_OK if succeeded
//
// History: 20-Feb-95 Rickhi Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::UnmarshalIPID(REFIID riid, STDOBJREF *pStd,
OXIDEntry *pOXIDEntry, void **ppv)
{
TRACECALL(TRACE_MARSHAL, "CStdMarshal::UnmarshalIPID");
ComDebOut((DEB_MARSHAL,
"CStdMarshal::UnmarshalIPID this:%x riid:%I pStd:%x pOXIDEntry:%x\n",
this, &riid, pStd, pOXIDEntry));
DbgDumpSTD(pStd);
AssertValid();
AssertDisconnectPrevented();
ASSERT_LOCK_HELD
// validate input params.
Win4Assert(!(IsEqualIID(riid, IID_NULL) || IsEqualIID(riid, IID_IMarshal)));
Win4Assert(pStd != NULL);
ValidateSTD(pStd);
Win4Assert(pOXIDEntry);
// look for an existing IPIDEntry for the requested interface.
IPIDEntry *pEntry;
HRESULT hr = FindIPIDEntry(riid, &pEntry);
#ifdef WX86OLE
BOOL fSameApt = SUCCEEDED(hr);
PVOID pvPSThunk = NULL;
#endif
// REFCOUNTING:
if (ClientSide())
{
if (FAILED(hr))
{
// no IPID Entry exists yet for the requested interface. We do
// have a STDOBJREF. Create the interface proxy and IPIDEntry
// now, and connect it up. If successful, the proxy will be
// fully connected upon return, with pEntry->cStrongRefs set
// to pStd->cPublicRefs.
if (ppv)
*ppv = NULL;
hr = MakeCliIPIDEntry(riid, pStd, pOXIDEntry, &pEntry);
}
else if (pEntry->dwFlags & IPIDF_DISCONNECTED)
{
// reconnect the IPID entry to the server. this will set
// pEntry->cStrongRefs to pStd->cPublicRefs. Even though we could
// yield, the IPIDEntry is guarenteed connected on return
// (cause we are holding the lock on return).
hr = ConnectIPIDEntry(pStd, pOXIDEntry, pEntry);
}
else if ((pStd->flags & SORF_WEAKREF) &&
(pEntry->pOXIDEntry->dwFlags & OXIDF_MACHINE_LOCAL))
{
// add the refcnt to our weak total for this IPIDEntry
pEntry->cWeakRefs += pStd->cPublicRefs;
}
else
{
// add the refcnt to our strong total for this IPIDEntry
pEntry->cStrongRefs += pStd->cPublicRefs;
}
}
else if (SUCCEEDED(hr))
{
// unmarshaling in the server apartment. If the cRefs is zero,
// then the interface was TABLE marshalled and we dont do
// anything to the IPID RefCnts since the object must live until
// ReleaseMarshalData is called on it.
#ifdef WX86OLE
pvPSThunk = gcwx86.UnmarshalledInSameApt(pEntry->pv, riid);
#endif
if (pStd->cPublicRefs > 0)
{
// normal case, dec the ref counts from the IPID entry,
// OLE always passed fLastReleaseCloses = FALSE on
// Unmarshal and RMD so do the same here.
DWORD mshlflags = (pStd->flags & SORF_WEAKREF)
? (MSHLFLAGS_WEAK | MSHLFLAGS_KEEPALIVE)
: (MSHLFLAGS_NORMAL | MSHLFLAGS_KEEPALIVE);
DecSrvIPIDCnt(pEntry, pStd->cPublicRefs, 0, NULL, mshlflags);
}
}
if (SUCCEEDED(hr) && ppv)
{
ValidateIPIDEntry(pEntry);
// extract and AddRef the pointer to return to the caller.
// Do this before releasing the lock (which we might do below
// on the server-side in DecSrvIPIDCnt.
// NOTE: we are calling App code while holding the lock,
// but there is no way to avoid this.
Win4Assert(IsValidInterface(pEntry->pv));
*ppv = pEntry->pv;
((IUnknown *)*ppv)->AddRef();
AssertOutPtrIface(hr, *ppv);
if (_dwFlags & SMFLAGS_WEAKCLIENT && !(pStd->flags & SORF_WEAKREF))
{
// make the client interface weak, ignore errors.
UNLOCK
ASSERT_LOCK_RELEASED
RemoteChangeRef(0,0);
ASSERT_LOCK_RELEASED
LOCK
}
#ifdef WX86OLE
// If we unmarshalled in the same apartment as the object and Wx86
// recognized the interface then change the returned proxy to the
// proxy created for the Wx86 PSThunk.
if (pvPSThunk == (PVOID)-1)
{
// Wx86 recognized the interface, but could not establish a
// PSThunk for it. Force an error return.
*ppv = NULL;
hr = E_NOINTERFACE;
}
else if (pvPSThunk != NULL)
{
// Wx86 recognized the interface and did establish a PSThunk
// for it. Force a successful return with Wx86 proxy interface.
*ppv = pvPSThunk;
}
#endif
}
ComDebOut((DEB_MARSHAL, "pEntry:%x cRefs:%x cStdId:%x\n", pEntry,
(SUCCEEDED(hr)) ? pEntry->cStrongRefs : 0, _pStdId->GetRC()));
ASSERT_LOCK_HELD
AssertDisconnectPrevented();
ComDebOut((DEB_MARSHAL, "CStdMarshal::UnmarshalIPID hr:%x\n", hr));
return hr;
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::PrivateCopyProxy, internal
//
// Synopsis: Creates a copy of a proxy and IPID entry.
//
// Arguements: [pProxy] - Proxy to copy
// [ppProxy] - return copy here.
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::PrivateCopyProxy( IUnknown *pv, IUnknown **ppv )
{
TRACECALL(TRACE_MARSHAL, "CStdMarshal::PrivateCopyProxy");
ComDebOut((DEB_MARSHAL, "CStdMarshal::PrivateCopyProxy this:%x pv:%x\n",
this, pv));
// Don't copy stubs.
if (ServerSide())
return E_INVALIDARG;
ASSERT_LOCK_RELEASED
LOCK
// Prevent a disconnect from occuring while unmarshaling the
// interface since we may have to yield the ORPC lock.
HRESULT hr = PreventPendingDisconnect();
if (SUCCEEDED(hr))
{
// Find the proxy to copy.
IPIDEntry *pEntry;
hr = FindIPIDEntryByInterface(pv, &pEntry);
if (SUCCEEDED(hr))
{
// Don't copy disconnected proxies.
if (pEntry->dwFlags & IPIDF_DISCONNECTED)
hr = RPC_E_DISCONNECTED;
// IUnknown can't be copied.
else if (IsEqualGUID( pEntry->iid, IID_IUnknown ))
hr = E_INVALIDARG;
else
{
BOOL fNonNDRProxy;
IRpcProxyBuffer *pProxy;
hr = CreateProxy(pEntry->iid, &pProxy, (void **)ppv,
&fNonNDRProxy);
if (SUCCEEDED(hr))
{
IPIDEntry *pIpidCopy;
// add a disconnected IPID entry to the table.
hr = AddIPIDEntry(NULL, &pEntry->ipid, pEntry->iid, NULL,
pProxy, *ppv, &pIpidCopy);
if (SUCCEEDED(hr))
{
// mark this IPID as a copy so we dont free it during
// ReleaseIPIDs.
pIpidCopy->dwFlags |= IPIDF_COPY;
// connect the IPIDEntry before adding it to the table so
// that we dont have to worry about races between Unmarshal,
// Disconnect, and ReconnectProxies.
// Make up an objref. Mark it as NOPING since we dont
// really have any references and we dont really need
// any because if we ever try to marshal it we will
// find the original IPIDEntry and use that. NOPING
// also lets us skip this IPID in DisconnectCliIPIDs.
STDOBJREF std;
OXIDFromMOXID(pEntry->pOXIDEntry->moxid, &std.oxid);
std.ipid = pEntry->ipid;
std.cPublicRefs = 1;
std.flags = SORF_NOPING;
hr = ConnectIPIDEntry(&std, pEntry->pOXIDEntry, pIpidCopy);
// Add this IPID entry after the original.
pIpidCopy->pNextOID = pEntry->pNextOID;
pEntry->pNextOID = pIpidCopy;
_cIPIDs++;
}
else
{
// could not get an IPIDEntry, release the proxy, need to
// release the lock to do this.
UNLOCK
ASSERT_LOCK_RELEASED
pProxy->Release();
((IUnknown *)*ppv)->Release();
ASSERT_LOCK_RELEASED
LOCK
}
}
}
}
if (SUCCEEDED(hr))
{
ValidateIPIDEntry(pEntry);
AssertOutPtrIface(hr, *ppv);
}
AssertDisconnectPrevented();
}
ASSERT_LOCK_HELD
UNLOCK
ASSERT_LOCK_RELEASED
// Now let pending disconnect through.
HRESULT hr2 = HandlePendingDisconnect(hr);
if (FAILED(hr2) && SUCCEEDED(hr))
{
// a disconnect came in while creating the proxy. ppv contains
// an AddRef'd interface pointer so go Release that now.
((IUnknown *)*ppv)->Release();
}
ComDebOut((DEB_MARSHAL, "CStdMarshal::PrivateCopyProxy hr:%x\n", hr2));
return hr2;
}
//+-------------------------------------------------------------------
//
// Member: MakeSrvIPIDEntry, private
//
// Synopsis: creates a server side IPID table entry
//
// Arguements: [riid] - the interface to return
// [ppEntry] - IPIDEntry returned
//
// History: 20-Feb-95 Rickhi Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::MakeSrvIPIDEntry(REFIID riid, IPIDEntry **ppEntry)
{
Win4Assert(ServerSide());
AssertValid();
AssertDisconnectPrevented();
ASSERT_LOCK_HELD
BOOL fNonNDRStub;
void *pv;
IRpcStubBuffer *pStub;
HRESULT hr = CreateStub(riid, &pStub, &pv, &fNonNDRStub);
if (SUCCEEDED(hr))
{
OXIDEntry *pOXIDEntry = _pChnl->GetOXIDEntry();
IPID ipidDummy;
hr = AddIPIDEntry(pOXIDEntry, &ipidDummy, riid, _pChnl, pStub, pv,
ppEntry);
if (SUCCEEDED(hr))
{
if (_dwFlags & SMFLAGS_NOPING)
{
// object does no need pinging, turn on NOPING
(*ppEntry)->dwFlags |= IPIDF_NOPING;
}
if (fNonNDRStub)
{
// the stub was a custom 16bit one requested by WOW, mark the
// IPIDEntry as holding a non-NDR stub so we know to set the
// SORF_NONNDR flag in the StdObjRef when marshaling. This
// tells local clients whether to create a MIDL generated
// proxy or custom proxy. Functionality to support OLE
// Automation on DCOM.
(*ppEntry)->dwFlags |= IPIDF_NONNDRSTUB;
}
// increment the OXIDEntry ref count so that it stays
// around as long as the IPIDEntry points to it. It gets
// decremented when we disconnect the IPIDEntry.
IncOXIDRefCnt(pOXIDEntry);
// chain the IPIDEntries for this OID together
(*ppEntry)->pNextOID = _pFirstIPID;
_pFirstIPID = *ppEntry;
}
else
{
// release the stub. we need to release the lock to do this.
UNLOCK
ASSERT_LOCK_RELEASED
pStub->Release();
ASSERT_LOCK_RELEASED
LOCK
}
}
ASSERT_LOCK_HELD
AssertDisconnectPrevented();
return hr;
}
//+-------------------------------------------------------------------
//
// Member: MakeCliIPIDEntry, private
//
// Synopsis: creates a client side IPID table entry
//
// Arguements: [riid] - the interface to return
// [pStd] - standard objref
// [pOXIDEntry] - OXIDEntry of the server
// [ppEntry] - IPIDEntry returned
//
// History: 20-Feb-95 Rickhi Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::MakeCliIPIDEntry(REFIID riid, STDOBJREF *pStd,
OXIDEntry *pOXIDEntry,
IPIDEntry **ppEntry)
{
Win4Assert(ClientSide());
AssertValid();
AssertDisconnectPrevented();
Win4Assert(pOXIDEntry);
ASSERT_LOCK_HELD
BOOL fNonNDRProxy;
void *pv;
IRpcProxyBuffer *pProxy;
HRESULT hr = CreateProxy(riid, &pProxy, &pv, &fNonNDRProxy);
if (SUCCEEDED(hr))
{
// add a disconnected IPID entry to the table.
hr = AddIPIDEntry(NULL, &pStd->ipid, riid, NULL, pProxy, pv, ppEntry);
if (pv)
{
// throw away our reference here, we will get it back later
// in UnmarshalIPID
((IUnknown *)pv)->Release();
}
if (SUCCEEDED(hr))
{
if (fNonNDRProxy)
{
// the proxy is a custom 16bit one requested by WOW, mark the
// IPIDEntry as holding a non-NDR proxy so we know to set the
// LOCALF_NOTNDR flag in the local header when we call on it
// (see CRpcChannelBuffer::ClientGetBuffer). Functionality to
// support OLE Automation on DCOM.
(*ppEntry)->dwFlags |= IPIDF_NONNDRPROXY;
}
if (pStd->flags & SORF_NONNDR)
{
// need to remember this flag so we can tell other
// unmarshalers if we remarshal it.
(*ppEntry)->dwFlags |= IPIDF_NONNDRSTUB;
}
// connect the IPIDEntry before adding it to the table so
// that we dont have to worry about races between Unmarshal,
// Disconnect, and ReconnectProxies.
hr = ConnectIPIDEntry(pStd, pOXIDEntry, *ppEntry);
// chain the IPIDEntries for this OID together. On client side
// always add the entry to the list regardless of whether connect
// succeeded.
(*ppEntry)->pNextOID = _pFirstIPID;
_pFirstIPID = *ppEntry;
_cIPIDs++;
}
else
{
// could not get an IPIDEntry, release the proxy, need to
// release the lock to do this.
UNLOCK
ASSERT_LOCK_RELEASED
pProxy->Release();
ASSERT_LOCK_RELEASED
LOCK
}
}
ASSERT_LOCK_HELD
AssertDisconnectPrevented();
return hr;
}
//+-------------------------------------------------------------------
//
// Member: ConnectIPIDEntry, private
//
// Synopsis: connects a client side IPID table entry to the server
//
// Arguments: [pStd] - standard objref
// [pOXIDEntry] - OXIDEntry for the server
// [pEntry] - IPIDEntry to connect, already has a proxy
// and the IID filled in.
//
// Notes: This routine is re-entrant, it may be called multiple
// times for the same IPIDEntry, with part of the work done
// in one call and part in another. Only if the entry is
// fully set up will it return S_OK and mark the entry as
// connected. DisconnectCliIPIDs handles cleanup of partial
// connections.
//
// History: 20-Feb-95 Rickhi Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::ConnectIPIDEntry(STDOBJREF *pStd,
OXIDEntry *pOXIDEntry,
IPIDEntry *pEntry)
{
ComDebOut((DEB_MARSHAL,
"CStdMarshal::ConnectIPIDEntry this:%x ipid:%I pOXIDEntry:%x pIPIDEntry:%x\n",
this, &pStd->ipid, pOXIDEntry, pEntry));
Win4Assert(ClientSide());
AssertDisconnectPrevented();
AssertValid();
Win4Assert(pOXIDEntry);
ASSERT_LOCK_HELD
HRESULT hr = S_OK;
// mark the object as having attempted to connect an IPIDEntry so that
// if we fail somewhere in this routine and dont mark the whole object
// as connected, Disconnect will still try to clean things up.
_dwFlags |= SMFLAGS_TRIEDTOCONNECT;
if (!(pStd->flags & SORF_NOPING))
{
// this interface requires pinging, turn off NOPING for this object
// and this IPIDEntry.
_dwFlags &= ~SMFLAGS_NOPING;
pEntry->dwFlags &= ~IPIDF_NOPING;
}
if (!(_dwFlags & (SMFLAGS_REGISTEREDOID | SMFLAGS_NOPING)))
{
// register the OID with the ping server so it will get pinged
hr = gResolver.ClientRegisterOIDWithPingServer(pStd->oid, pOXIDEntry);
if (FAILED(hr))
{
return hr;
}
_dwFlags |= SMFLAGS_REGISTEREDOID;
}
// Go get any references we need that are not already included in the
// STDOBJREF. These references will have been added to the counts in
// the IPIDEntry upon return. Any references in the STDOBJREF will be
// added to the IPIDEntry count only if the connect succeeds, otherwise
// ReleaseMarshalObjRef (which will clean up STDOBJREF references) will
// get called by higher level code.
hr = GetNeededRefs(pStd, pOXIDEntry, pEntry);
if (FAILED(hr))
{
return hr;
}
if (pEntry->pChnl == NULL)
{
// create a channel for this oxid/ipid pair. On the client side we
// create one channel per proxy (and hence per IPID).
hr = CreateChannel(pOXIDEntry, pStd->flags, pStd->ipid,
pEntry->iid, &pEntry->pChnl);
if (SUCCEEDED(hr))
{
// update this IPID table entry. must update ipid too since
// on reconnect it differs from the old value.
IncOXIDRefCnt(pOXIDEntry);
pEntry->pOXIDEntry = pOXIDEntry;
pEntry->ipid = pStd->ipid;
pEntry->pChnl->SetIPIDEntry(pEntry);
}
}
if (SUCCEEDED(hr))
{
// Release the lock while we connect the proxy. We have to do
// this because the IDispatch proxy makes an Rpc call during
// Connect (Yuk!), which causes the channel to assert that the
// lock is released. The proxy MUST be able to handle multiple
// simultaneous or nested connects to the same channel ptr, since
// it is possible when we yield the lock for another thread to
// come in here and try a connect.
void *pv = NULL;
IRpcProxyBuffer * pProxy = (IRpcProxyBuffer *)(pEntry->pStub);
if (pProxy)
{
// HACKALERT: OleAutomation returns NULL pv in CreateProxy
// in cases where they dont know whether to return an NDR
// proxy or a custom-format proxy. So we have to go connect
// the proxy first then Query for the real interface once that
// is done.
BOOL fGetpv = (pEntry->pv) ? FALSE : TRUE;
UNLOCK
ASSERT_LOCK_RELEASED
hr = pProxy->Connect(pEntry->pChnl);
if (fGetpv && SUCCEEDED(hr))
{
#ifdef WX86OLE
if (gcwx86.IsN2XProxy(pProxy))
{
// If we are creating a proxy for an object that is
// living on the x86 side then we need to set the
// StubInvoke flag to allow QI to thunk the
// custom interface QI.
gcwx86.SetStubInvokeFlag((BOOL)2);
}
#endif
hr = pProxy->QueryInterface(pEntry->iid, &pv);
AssertOutPtrIface(hr, pv);
if(SUCCEEDED(hr))
{
#ifdef WX86OLE
// Call whole32 thunk layer to play with the ref count
// and aggregate the proxy to the controlling unknown.
gcwx86.AggregateProxy(_pStdId->GetCtrlUnk(),
(IUnknown *)pv);
#endif
// Release our reference here.
// We keep a weak reference to pv.
((IUnknown *)pv)->Release();
}
}
ASSERT_LOCK_RELEASED
LOCK
}
// Regardless of errors from Connect and QI we wont try to cleanup
// any of the work we have done so far in this routine. The routine
// is reentrant (by the same thread or by different threads) and
// those calls could be using some of resources we have already
// allocated. Instead, we rely on DisconnectCliIPIDs to cleanup
// the partial allocation of resources.
if (pEntry->dwFlags & IPIDF_DISCONNECTED)
{
// Mark the IPIDEntry as connected so we dont try to connect
// again. Also, as long as there is one IPID connected, the
// whole object is considered connected. This allows disconnect
// to find the newly connected IPID and disconnect it later.
// Infact, DisconnectCliIPIDs relies on there being at least
// one IPID with a non-NULL OXIDEntry. It is safe to set this
// now because Disconnects have been temporarily turned off.
if (SUCCEEDED(hr))
{
if (pv)
{
// assign the interface pointer
pEntry->pv = pv;
}
AssertDisconnectPrevented();
pEntry->dwFlags &= ~IPIDF_DISCONNECTED;
_dwFlags &= ~SMFLAGS_DISCONNECTED;
}
}
else
{
// while the lock was released, the IPIDEntry got connected
// by another thread (or by a nested call on this thread).
// Ignore any errors from Connect or QI since apparently
// things are connected now.
hr = S_OK;
}
if (SUCCEEDED(hr))
{
// Add in any references we were given. If we were given 0 refs
// and the interface is noping, then pretend like we got 1 ref.
ULONG cRefs = ((pStd->cPublicRefs == 0) && (pStd->flags & SORF_NOPING))
? 1 : pStd->cPublicRefs;
// figure out if we have weak or strong references. To be weak
// they must be local to this machine and the SORF flag set.
BOOL fWeak = ((pStd->flags & SORF_WEAKREF) &&
(pOXIDEntry->dwFlags & OXIDF_MACHINE_LOCAL));
if (fWeak)
pEntry->cWeakRefs += cRefs;
else
pEntry->cStrongRefs += cRefs;
}
// in debug build, ensure that we did not screw up
ValidateIPIDEntry(pEntry);
}
ASSERT_LOCK_HELD
AssertDisconnectPrevented();
ComDebOut((DEB_MARSHAL,
"CStdMarshal::ConnectIPIDEntry this:%x pOXIDEntry:%x pChnl:%x hr:%x\n",
this, pEntry->pOXIDEntry, pEntry->pChnl, hr));
return hr;
}
//+-------------------------------------------------------------------
//
// Member: GetNeededRefs, private
//
// Synopsis: Figures out if any references are needed and goes and gets
// them from the server.
//
// Arguments: [pStd] - standard objref
// [pOXIDEntry] - OXIDEntry for the server
// [pEntry] - IPIDEntry to connect, already has a proxy
// and the IID filled in.
//
// History: 20-Feb-95 Rickhi Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::GetNeededRefs(STDOBJREF *pStd, OXIDEntry *pOXIDEntry,
IPIDEntry *pEntry)
{
HRESULT hr = S_OK;
if ((pStd->flags & (SORF_NOPING | SORF_WEAKREF)) == 0)
{
// if we dont have any and weren't given any strong refs, go get some.
ULONG cNeedStrong = ((pEntry->cStrongRefs + pStd->cPublicRefs) == 0)
? REM_ADDREF_CNT : 0;
// if we are using secure refs and we dont have any, go get some.
ULONG cNeedSecure = ((gCapabilities & EOAC_SECURE_REFS) &&
(pEntry->cPrivateRefs == 0)) ? 1 : 0;
if (cNeedStrong || cNeedSecure)
{
// Need to go get some references from the remote server. Note
// that we will yield here but we dont have to worry about it because
// the IPIDEntry is still marked as disconnected.
hr = RemoteAddRef(pEntry, pOXIDEntry, cNeedStrong, cNeedSecure);
if (SUCCEEDED(hr))
{
pEntry->cStrongRefs += cNeedStrong;
pEntry->cPrivateRefs += cNeedSecure;
}
}
}
return hr;
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::ReconnectProxies
//
// Synopsis: Reconnects the proxies to a new server (functionality
// used by the OLE default handler).
//
// History: 20-Feb-95 Rickhi Created.
//
// CODEWORK: CreateServer should just ask for all these interfaces
// during the create.
//
// BUGBUG: fail this call if freethreaded
//
//--------------------------------------------------------------------
void CStdMarshal::ReconnectProxies()
{
ComDebOut((DEB_MARSHAL,"CStdMarshal::ReconnectProxies this:%x pFirst:%x\n",
this, _pFirstIPID));
AssertValid();
Win4Assert(ClientSide());
ASSERT_LOCK_RELEASED
LOCK
// must be at least 1 proxy already connected in order to be able
// to reconnect the other proxies. We cant just ASSERT that's true
// because we were not holding the lock on entry.
HRESULT hr = PreventDisconnect();
if (SUCCEEDED(hr))
{
// allocate a stack buffer to hold the IPIDs
IID *pIIDsAlloc = (IID *) _alloca(_cIPIDs * sizeof(IID));
IID *pIIDs = pIIDsAlloc;
USHORT cIIDs = 0;
IPIDEntry *pNextIPID = _pFirstIPID;
while (pNextIPID)
{
// Don't allow reconnection for fancy new servers or with
// secure proxies.
if (pNextIPID->dwFlags & IPIDF_COPY)
{
hr = E_FAIL;
break;
}
if ((pNextIPID->dwFlags & IPIDF_DISCONNECTED))
{
// not connected, add it to the list to be connected.
*pIIDs = pNextIPID->iid;
pIIDs++;
cIIDs++;
}
pNextIPID = pNextIPID->pNextOID;
}
if (cIIDs != 0 && SUCCEEDED(hr))
{
// we have looped filling in the IID list, and there are
// entries int he list. go call QI on server now and
// unmarshal the results.
hr = RemQIAndUnmarshal(cIIDs, pIIDsAlloc, NULL);
}
}
DbgWalkIPIDs();
UNLOCK
ASSERT_LOCK_RELEASED
// this will handle any Disconnect that came in while we were busy.
hr = HandlePendingDisconnect(hr);
ComDebOut((DEB_MARSHAL,"CStdMarshal::ReconnectProxies [OUT] this:%x\n", this));
return;
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::ReleaseMarshalData, public
//
// Synopsis: Releases the references added by MarshalInterface
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
STDMETHODIMP CStdMarshal::ReleaseMarshalData(LPSTREAM pStm)
{
ComDebOut((DEB_MARSHAL,
"CStdMarshal::ReleaseMarshalData this:%x pStm:%x\n", this, pStm));
AssertValid();
ASSERT_LOCK_RELEASED
OBJREF objref;
HRESULT hr = ReadObjRef(pStm, objref);
if (SUCCEEDED(hr))
{
// call worker API to do the rest of the work
hr = ::ReleaseMarshalObjRef(objref);
// deallocate the objref we read
FreeObjRef(objref);
}
ASSERT_LOCK_RELEASED
ComDebOut((DEB_MARSHAL,
"CStdMarshal::ReleaseMarshalData this:%x hr:%x\n", this, hr));
return hr;
}
//+-------------------------------------------------------------------
//
// Function: ReleaseMarshalObjRef, private
//
// Synopsis: Releases the references added by MarshalObjRef
//
// Arguements: [objref] - object reference
//
// Algorithm: Get the correct standard identity and ask it to do
// a ReleaseMarshalData.
//
// History: 19-Jun-95 Rickhi Created
//
//--------------------------------------------------------------------
INTERNAL ReleaseMarshalObjRef(OBJREF &objref)
{
ComDebOut((DEB_MARSHAL, "ReleaseMarshalObjRef objref:%x\n", &objref));
ASSERT_LOCK_RELEASED
HRESULT hr = InitChannelIfNecessary();
if (SUCCEEDED(hr))
{
CStdMarshal *pStdMshl;
hr = FindStdMarshal(objref, &pStdMshl);
if (SUCCEEDED(hr))
{
// only do the RMD if on the server side.
if (pStdMshl->ServerSide())
{
// pass objref to subroutine to Release the marshaled data
hr = pStdMshl->ReleaseMarshalObjRef(objref);
}
pStdMshl->Release();
}
else
{
// we could not find or create an identity. If the server is
// outside this apartment, try to issue a remote release on
// the interface. if the OXID is local and we could not find
// the identity, there is nothing left to cleanup.
LOCK
OXIDEntry *pOXIDEntry = GetOXIDFromObjRef(objref);
if (pOXIDEntry != GetLocalOXIDEntry())
{
// make a remote release call
RemoteReleaseObjRef(objref);
}
UNLOCK
}
}
ASSERT_LOCK_RELEASED
ComDebOut((DEB_MARSHAL, "ReleaseMarshalObjRef hr:%x\n", hr));
return hr;
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::ReleaseMarshalObjRef, public
//
// Synopsis: Releases the references added by MarshalObjRef
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::ReleaseMarshalObjRef(OBJREF &objref)
{
ComDebOut((DEB_MARSHAL,
"CStdMarshal::ReleaseMarshalObjRef this:%x objref:%x\n", this, &objref));
AssertValid();
HRESULT hr = S_OK;
STDOBJREF *pStd = &ORSTD(objref).std;
ValidateSTD(pStd);
ASSERT_LOCK_RELEASED
LOCK
// REFCOUNTING:
if (ServerSide())
{
// look for an existing IPIDEntry for the given IPID
IPIDEntry *pEntry;
hr = FindIPIDEntryByIPID(pStd->ipid, &pEntry);
if (SUCCEEDED(hr) && !(pEntry->dwFlags & IPIDF_DISCONNECTED))
{
// subtract the ref count from the IPIDEntry, may Release the
// StdId if this was the last reference for this IPIDEntry.
// we need to figure out how it was marshalled, strong/weak etc
// in order to set the flags and cRefs correctly to pass to
// DecSrvIPIDCnt.
if (pStd->cPublicRefs == 0)
{
// table case
DWORD mshlflags = (pStd->flags & SORF_TBLWEAK)
? MSHLFLAGS_TABLEWEAK : MSHLFLAGS_TABLESTRONG;
DecSrvIPIDCnt(pEntry, 1, 0, NULL, mshlflags);
}
else
{
// normal or weak case
DWORD mshlflags = (pStd->flags & SORF_WEAKREF)
? MSHLFLAGS_WEAK : MSHLFLAGS_NORMAL;
DecSrvIPIDCnt(pEntry, pStd->cPublicRefs, 0, NULL, mshlflags);
}
}
}
else // client side
{
if ((pStd->cPublicRefs == 0) || (pStd->flags & SORF_NOPING))
{
// there are no references, or this interface does not
// need pinging, so there is nothing to do.
;
}
else
{
// look for an existing IPIDEntry for the given IPID
IPIDEntry *pEntry;
hr = FindIPIDEntryByIPID(pStd->ipid, &pEntry);
if (SUCCEEDED(hr) && !(pEntry->dwFlags & IPIDF_DISCONNECTED))
{
// add these to the cRefs of this entry, they will get freed
// when we do the remote release. Saves an Rpc call now.
if ((pStd->flags & SORF_WEAKREF) &&
(pEntry->pOXIDEntry->dwFlags & OXIDF_MACHINE_LOCAL))
pEntry->cWeakRefs += pStd->cPublicRefs;
else
pEntry->cStrongRefs += pStd->cPublicRefs;
}
else
{
// client side, no matching IPIDEntry so just contact the remote
// server to remove the reference. ignore errors since there is
// nothing we can do about them anyway.
RemoteReleaseObjRef(objref);
hr = S_OK;
}
}
}
UNLOCK
ASSERT_LOCK_RELEASED
ComDebOut((DEB_MARSHAL,
"CStdMarshal::ReleaseMarshalObjRef this:%x hr:%x\n", this, hr));
return hr;
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::PreventDisconnect, public
//
// Synopsis: Prevents a Disconnect from occurring until a matching
// HandlePendingDisconnect is called.
//
// History: 21-Sep-95 Rickhi Created
//
// The ORPC LOCK is yielded at many places in order to make calls on
// application interfaces (server-side objects, stubs, proxies,
// handlers, remote objects, resolver, etc). In order to keep the
// code (reasonably?) simple, disconnects are prevented from occuring
// while in the middle of (potentially) complex operations, and while
// there are outstanding calls on interfaces to this object.
//
// To accomplish this, a counter (_cNestedCalls) is atomically incremented.
// When _cNestedCalls != 0 and a Disconnect arrives, the object is flagged
// as PendingDisconnect. When HandlePendingDisconnect is called, it
// decrements the _cNestedCalls. If the _cNestedCalls == 0 and there is
// a pending disconnect, the real Disconnect is done.
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::PreventDisconnect()
{
ASSERT_LOCK_HELD
// treat this as a nested call so that if we yield, a real
// disconnect wont come through, instead it will be treated
// as pending. That allows us to avoid checking our state
// for Disconnected every time we yield the ORPC LOCK.
InterlockedIncrement(&_cNestedCalls);
if (_dwFlags & (SMFLAGS_DISCONNECTED | SMFLAGS_PENDINGDISCONNECT))
return CO_E_OBJNOTCONNECTED;
return S_OK;
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::PreventPendingDisconnect, public
//
// Synopsis: similar to PreventDisconnect but special case for use
// in UnmarshalObjRef (since the client side starts out
// in the Disconnected state until the first unmarshal is done).
//
// History: 21-Sep-95 Rickhi Created
//
//+-------------------------------------------------------------------
HRESULT CStdMarshal::PreventPendingDisconnect()
{
ASSERT_LOCK_HELD
InterlockedIncrement(&_cNestedCalls);
if (_dwFlags &
(ClientSide() ? SMFLAGS_PENDINGDISCONNECT
: SMFLAGS_PENDINGDISCONNECT | SMFLAGS_DISCONNECTED))
return CO_E_OBJNOTCONNECTED;
return S_OK;
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::HandlePendingDisconnect, public
//
// Synopsis: Reverses a call to PreventDisconnect and lets a
// pending disconnect through.
//
// History: 21-Sep-95 Rickhi Created
//
//+-------------------------------------------------------------------
HRESULT CStdMarshal::HandlePendingDisconnect(HRESULT hr)
{
ASSERT_LOCK_RELEASED
// treat this as a nested call so that if we yield, a real
// disconnect wont come through, instead it will be treated
// as pending. That allows us to avoid checking our state
// for Disconnected every time we yield the ORPC LOCK.
if (InterlockedDecrement(&_cNestedCalls) == 0 &&
(_dwFlags & SMFLAGS_PENDINGDISCONNECT))
{
Disconnect();
hr = FAILED(hr) ? hr : CO_E_OBJNOTCONNECTED;
}
return hr;
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::DisconnectObject, public
//
// Synopsis: part of IMarshal interface, this is legal only on the
// server side.
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
STDMETHODIMP CStdMarshal::DisconnectObject(DWORD dwReserved)
{
ComDebOut((DEB_MARSHAL,
"CStdMarshal::DisconnectObject this:%x dwRes:%x\n", this, dwReserved));
AssertValid();
ASSERT_LOCK_RELEASED
// this operation is not legal from the client side (although
// IProxyManager::Disconnect is), but we still have to return S_OK
// in either case for backward compatibility.
if (ServerSide())
{
Disconnect();
}
ASSERT_LOCK_RELEASED
return S_OK;
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::Disconnect, public
//
// Synopsis: client side - disconnects proxies from the channel.
// server side - disconnects stubs from the server object.
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
void CStdMarshal::Disconnect(void)
{
ComDebOut((DEB_MARSHAL, "CStdMarshal::Disconnect this:%x\n", this));
AssertValid();
ASSERT_LOCK_RELEASED
LOCK
if ((_dwFlags & SMFLAGS_DISCONNECTED) &&
!(_dwFlags & SMFLAGS_TRIEDTOCONNECT))
{
// already disconnected, no partial connects, nothing to do
ComDebOut((DEB_MARSHAL,"CStdMarshal::Disconnect [already done]:%x\n",this));
UNLOCK
ASSERT_LOCK_RELEASED
return;
}
// Revoke ID from the ID table if registered. This prevents other
// marshals/unmarshals from finding this identity that is about to
// be disconnected. This is the ONLY state that should change, since
// we dont want to screw up any work-in-progress on other threads
// or in calls higher up the stack.
_pStdId->RevokeOID();
if (_cNestedCalls != 0)
{
// we dont allow disconnect to occur inside a nested call since we
// dont want state to vanish in the middle of a call, but we do
// remember that we want to disconnect and will do it when the
// stack unwinds (or other threads complete).
_dwFlags |= SMFLAGS_PENDINGDISCONNECT;
ComDebOut((DEB_MARSHAL,"CStdMarshal::Disconnect [pending]:%x\n",this));
UNLOCK;
ASSERT_LOCK_RELEASED
return;
}
// No calls in progress and not already disconnected, OK to really
// disconnect now. First mark ourself as disconnected incase we
// get reentered while releasing a stub pointer.
_dwFlags |= SMFLAGS_DISCONNECTED; // turn on disconnected
_dwFlags &= ~(SMFLAGS_PENDINGDISCONNECT | // turn off pending disconnect
SMFLAGS_TRIEDTOCONNECT); // turn off tried to connect
// disconnect all our IPIDs
if (ServerSide())
DisconnectSrvIPIDs();
else
DisconnectCliIPIDs();
UNLOCK
ASSERT_LOCK_RELEASED
if (ServerSide())
{
// HACK - 16 and 32 bit Word 6.0 crash if you release all the objects
// it left lying around at CoUninitialize. Leak them.
COleTls tls;
// If we are not uninitializing, then call the release.
if ((tls->dwFlags & OLETLS_THREADUNINITIALIZING) == 0 ||
// If we are in WOW and the app is not word, then call the release.
(IsWOWThread() &&
(g_pOleThunkWOW->GetAppCompatibilityFlags() & OACF_NO_UNINIT_CLEANUP) == 0) ||
// If the app is not 32 bit word, then call the release.
!IsTaskName( L"winword.exe" ))
{
// on the server side, we have to tell the stdid to release his
// controlling unknown of the real object.
_pStdId->ReleaseCtrlUnk();
}
}
ASSERT_LOCK_RELEASED
ComDebOut((DEB_MARSHAL,"CStdMarshal::Disconnect [complete]:%x\n",this));
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::DisconnectCliIPIDs
//
// Synopsis: disconnects client side IPIDs for this object.
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
void CStdMarshal::DisconnectCliIPIDs()
{
ComDebOut((DEB_MARSHAL,"CStdMarshal::DisconnectCliIPIDs this:%x pFirst:%x\n",
this, _pFirstIPID));
Win4Assert(ClientSide());
Win4Assert(_dwFlags & SMFLAGS_DISCONNECTED);
// YIELD WARNING: Do not yield between here and the matching comment
// below, since we are mucking with internal state that could get
// messed up if a reconnect (or unmarshal) is done.
ASSERT_LOCK_HELD
// on client side, we cant actually release the proxies until the
// object goes away (backward compatibility), so we just release
// our references to the remote guy, disconnect the proxies, and
// delete the channels, but hold on to the IPIDEntries.
REMINTERFACEREF *pRifRefAlloc = (REMINTERFACEREF *)
_alloca(_cIPIDs * 2 * sizeof(REMINTERFACEREF));
REMINTERFACEREF *pRifRef = pRifRefAlloc;
OXIDEntry *pOXID = NULL;
USHORT cRifRef = 0;
IPIDEntry *pEntry = _pFirstIPID;
while (pEntry)
{
// we have to handle the case where ConnectIPIDEntry partially (but
// not completely) set up the IPIDEntry, hence we cant just check
// for the IPIDF_DISCONNECTED flag.
ValidateIPIDEntry(pEntry);
// NOTE: we are calling Proxy code here while holding the ORPC LOCK.
// There is no way to get around this without introducing race
// conditions. We cant just disconnect the channel and leave the
// proxy connected cause some proxies (like IDispatch) do weird shit,
// like keeping separate pointers to the server.
if (pEntry->pStub) // NULL for IUnknown IPID
{
ComDebOut((DEB_MARSHAL, "Disconnect pProxy:%x\n", pEntry->pStub));
((IRpcProxyBuffer *)pEntry->pStub)->Disconnect();
pEntry->pv = NULL;
}
if (!(pEntry->dwFlags & IPIDF_NOPING))
{
// the object pays attention to pings (and hence refcounts)
if (pEntry->cStrongRefs > 0 || pEntry->cPrivateRefs > 0)
{
// we own some strong references on this interface, fill
// in an interfaceref so we release them.
pRifRef->cPublicRefs = pEntry->cStrongRefs;
pRifRef->cPrivateRefs = pEntry->cPrivateRefs;
pRifRef->ipid = pEntry->ipid;
pRifRef++;
cRifRef++;
}
if (pEntry->cWeakRefs > 0)
{
// we own some weak references on this interface, fill
// in an interfaceref so we release them.
pRifRef->cPublicRefs = pEntry->cWeakRefs;
pRifRef->cPrivateRefs = 0;
pRifRef->ipid = pEntry->ipid;
// mark the IPID as weak so that RemRelease on the server
// knows to release weak references instead of strong refs.
pRifRef->ipid.Data1 |= IPIDFLAG_WEAKREF;
pRifRef++;
cRifRef++;
}
}
pEntry->cStrongRefs = 0;
pEntry->cWeakRefs = 0;
pEntry->cPrivateRefs = 0;
pEntry->dwFlags |= IPIDF_DISCONNECTED | IPIDF_NOPING;
if (pEntry->pChnl)
{
// release the channel for this IPID
pEntry->pChnl->Release();
pEntry->pChnl = NULL;
}
if (pEntry->pOXIDEntry)
{
// We will be decrementing the OXID refcnt as we release IPIDEntries
// but we dont want the OXIDEntry to go away until after we make the
// RemoteRelease call below, so we hold on to it here.
if (pOXID == NULL)
{
pOXID = pEntry->pOXIDEntry;
IncOXIDRefCnt(pOXID);
}
// If we ever go to a model where different IPIDEntries on the
// same object can point to different OXIDEntires, then we need
// to re-write this code to batch the releases by OXID.
Win4Assert(pOXID == pEntry->pOXIDEntry);
// release the RefCnt on the OXIDEntry
DecOXIDRefCnt(pEntry->pOXIDEntry);
pEntry->pOXIDEntry = NULL;
}
// get next IPID in chain for this object
pEntry = pEntry->pNextOID;
}
if (_pChnl)
{
// release the last client side channel
_pChnl->Release();
_pChnl = NULL;
}
if (_dwFlags & SMFLAGS_REGISTEREDOID)
{
// Tell the resolver to stop pinging the OID. The OID is only
// registered on the client side.
Win4Assert(ClientSide());
gResolver.ClientDeRegisterOIDFromPingServer(_pStdId->GetOID(),
_dwFlags & SMFLAGS_CLIENTMARSHALED);
}
// turn these flags off so re-connect (with new OID) will behave properly.
_dwFlags &= ~(SMFLAGS_CLIENTMARSHALED | SMFLAGS_REGISTEREDOID |
SMFLAGS_NOPING);
// YIELD WARNING: Up this this point we have been mucking with our
// internal state. We cant yield before this point or a reconnect
// proxies could get all screwed up. It is OK to yield after this point
// because all internal state changes are now complete. The function
// to release the remote references yield.
if (cRifRef != 0)
{
// we have looped filling in the RifRef and entries exist in the
// array. go call the server now to release the IPIDs.
Win4Assert(pOXID); // must have been at least one
RemoteReleaseRifRef(pOXID, cRifRef, pRifRefAlloc);
}
if (pOXID)
{
// Now release the refcnt (if any) we put on the OXIDEntry above
// to hold it
DecOXIDRefCnt(pOXID);
}
ASSERT_LOCK_HELD
DbgWalkIPIDs();
ComDebOut((DEB_MARSHAL, "CStdMarshal::DisconnectCliIPIDs this:%x\n",this));
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::DisconnectSrvIPIDs
//
// Synopsis: disconnects the server side IPIDs for this object.
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
void CStdMarshal::DisconnectSrvIPIDs()
{
ComDebOut((DEB_MARSHAL,
"CStdMarshal::DisconnectSrvIPIDs this:%x pFirst:%x\n",this, _pFirstIPID));
Win4Assert(ServerSide());
// there should be no other threads looking at these IPIDs at this time,
// since Marshal, Unmarshal, and Dispatch all call PreventDisconnect,
// Disconnect checks the disconnected flag directly, RMD holds the
// lock over it's whole execution, RemAddRef and RemRelease hold the
// lock and check the disconnected flag of the IPIDEntry, and
// RemQueryInterface calls PreventDisconnect.
Win4Assert(_dwFlags & SMFLAGS_DISCONNECTED);
Win4Assert(_cNestedCalls == 0);
ASSERT_LOCK_HELD
// while holding the lock, flag each IPID as disconnected so that no
// more incoming calls are dispatched to this object. We also unchain
// the IPIDs to ensure that no other threads are pointing at them.
IPIDEntry *pFirstIPID = _pFirstIPID;
_pFirstIPID = NULL;
IPIDEntry *pEntry = pFirstIPID;
while (pEntry)
{
pEntry->dwFlags |= IPIDF_VACANT | IPIDF_DISCONNECTED;
// release the refcnt on the OXIDEntry and NULL it
DecOXIDRefCnt(pEntry->pOXIDEntry);
pEntry->pOXIDEntry = NULL;
pEntry = pEntry->pNextOID;
}
// now release the LOCK since we will be calling into app code to
// disconnect the stubs, and to release the external connection counts.
// There should be no other pointers to these IPIDEntries now, so it
// is safe to muck with their fields (except the dwFlags which is looked
// at by Dispatch and was already set above).
UNLOCK
ASSERT_LOCK_RELEASED
IPIDEntry *pLastIPID;
pEntry = pFirstIPID;
while (pEntry)
{
if (pEntry->dwFlags & IPIDF_NOTIFYACT)
{
// the activation code asked to be notified when the refcnt
// on this interface reaches zero. Turn the flag off so we
// don't call twice.
pEntry->dwFlags &= ~IPIDF_NOTIFYACT;
NotifyActivation(FALSE, (IUnknown *)(pEntry->pv));
}
if (pEntry->pStub) // pStub is NULL for IUnknown IPID
{
ComDebOut((DEB_MARSHAL, "Disconnect pStub:%x\n", pEntry->pStub));
((IUnknown *)pEntry->pv)->Release();
((IRpcStubBuffer *)pEntry->pStub)->Disconnect();
pEntry->pStub->Release();
pEntry->pStub = NULL;
pEntry->pv = NULL;
}
if (pEntry->cWeakRefs > 0)
{
// Release weak references on the StdId.
pEntry->cWeakRefs = 0;
_pStdId->Release();
}
if (pEntry->cStrongRefs > 0)
{
// Release strong references on the StdId. Note that 16bit
// 16bit OLE always passed fLastReleaseCloses = FALSE in
// DisconnectObject so we do the same here.
pEntry->cStrongRefs = 0;
_pStdId->DecStrongCnt(TRUE); // fKeepAlive
}
if (pEntry->cPrivateRefs > 0)
{
// Release private references on the StdId. Note that 16bit
// 16bit OLE always passed fLastReleaseCloses = FALSE in
// DisconnectObject so we do the same here.
pEntry->cPrivateRefs = 0;
_pStdId->DecStrongCnt(TRUE); // fKeepAlive
}
pLastIPID = pEntry;
pEntry = pEntry->pNextOID;
}
ASSERT_LOCK_RELEASED
LOCK
if (pFirstIPID)
{
// now we release all entries.
gIPIDTbl.ReleaseEntryList(pFirstIPID, pLastIPID);
}
ASSERT_LOCK_HELD
DbgWalkIPIDs();
ComDebOut((DEB_MARSHAL,
"CStdMarshal::DisconnectSrvIPIDs [OUT] this:%x\n",this));
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::InstantiatedProxy, public
//
// Synopsis: return requested interfaces to the caller if instantiated
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
BOOL CStdMarshal::InstantiatedProxy(REFIID riid, void **ppv, HRESULT *phr)
{
ComDebOut((DEB_MARSHAL,
"CStdMarshal::InstantiatedProxy this:%x riid:%I ppv:%x\n",
this, &riid, ppv));
AssertValid();
Win4Assert(ClientSide());
Win4Assert(*ppv == NULL);
Win4Assert(*phr == S_OK);
BOOL fRet = FALSE;
ASSERT_LOCK_RELEASED
LOCK
// look for an existing IPIDEntry for the requested interface
IPIDEntry *pEntry;
HRESULT hr = FindIPIDEntry(riid, &pEntry);
if (SUCCEEDED(hr) && pEntry->pv)
{
// found the ipid entry, now extract the interface
// pointer to return to the caller.
Win4Assert(IsValidInterface(pEntry->pv));
*ppv = pEntry->pv;
fRet = TRUE;
}
else if (_cIPIDs == 0)
{
// no IPIDEntry for the requested interface, and we have never
// been connected to the server. Return E_NOINTERFACE in this
// case. This is different from having been connected then
// disconnected, where we return CO_E_OBJNOTCONNECTED.
*phr = E_NOINTERFACE;
Win4Assert(fRet == FALSE);
}
else if (_dwFlags & SMFLAGS_PENDINGDISCONNECT)
{
// no IPIDEntry for the requested interface and disconnect is
// pending, so return an error.
*phr = CO_E_OBJNOTCONNECTED;
Win4Assert(fRet == FALSE);
}
else
{
// no IPIDEntry, we are not disconnected, and we do have other
// instantiated proxies. QueryMultipleInterfaces expects
// *phr == S_OK and FALSE returned.
Win4Assert(*phr == S_OK);
Win4Assert(fRet == FALSE);
}
UNLOCK
ASSERT_LOCK_RELEASED
ComDebOut((DEB_MARSHAL,
"CStdMarshal::InstantiatedProxy hr:%x pv:%x fRet:%x\n", *phr, *ppv, fRet));
return fRet;
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::QueryRemoteInterfaces, public
//
// Synopsis: return requested interfaces to the caller if supported
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::QueryRemoteInterfaces(USHORT cIIDs, IID *pIIDs, SQIResult *pQIRes)
{
ComDebOut((DEB_MARSHAL,
"CStdMarshal::QueryRemoteInterfaces this:%x pIIDs:%x pQIRes:%x\n",
this, pIIDs, pQIRes));
AssertValid();
Win4Assert(ClientSide());
Win4Assert(cIIDs > 0);
ASSERT_LOCK_RELEASED
LOCK
HRESULT hr = PreventDisconnect();
if (SUCCEEDED(hr))
{
// call QI on the remote guy and unmarshal the results
hr = RemQIAndUnmarshal(cIIDs, pIIDs, pQIRes);
}
else
{
// cant call out because we're disconnected so return error for
// each requested interface.
for (USHORT i=0; i<cIIDs; i++, pQIRes++)
{
pQIRes->hr = hr;
}
}
UNLOCK
ASSERT_LOCK_RELEASED
// if the object was disconnected while in the middle of the call,
// then we still return SUCCESS for any interfaces we acquired. The
// reason is that we do have the proxies, and this matches the
// behaviour of a QI for an instantiated proxy on a disconnected
// object.
hr = HandlePendingDisconnect(hr);
ComDebOut((DEB_MARSHAL,
"CStdMarshal::QueryRemoteInterfaces this:%x hr:%x\n", this, hr));
return hr;
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::RemQIAndUnmarshal, private
//
// Synopsis: call QI on remote guy, then unmarshal the STDOBJREF
// to create the IPID, and return the interface ptr.
//
// History: 20-Feb-95 Rickhi Created.
//
// Notes: Caller must guarantee at least one IPIDEntry is connected.
// This function does a sparse fill of the result array.
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::RemQIAndUnmarshal(USHORT cIIDs, IID *pIIDs,
SQIResult *pQIRes)
{
ComDebOut((DEB_MARSHAL,
"CStdMarshal::RemQIAndUnmarshal this:%x cIIDs:%x pIIDs:%x pQIRes:%x\n",
this, cIIDs, pIIDs, pQIRes));
AssertValid();
AssertDisconnectPrevented();
Win4Assert(_pFirstIPID); // must be at least 1 IPIDEntry
ASSERT_LOCK_HELD
// we need an IPID to call RemoteQueryInterface with, any one will
// do so long as it is connected (in the reconnect case there may be
// only one connected IPID) so we pick the first one in the chain that
// is connected.
IPIDEntry *pIPIDEntry = GetConnectedIPID();
// remember what type of reference to get since we yield the lock
// and cant rely on _dwFlags later.
BOOL fWeakClient = (_dwFlags & SMFLAGS_WEAKCLIENT);
// call the remote guy
REMQIRESULT *pRemQiRes = NULL;
IRemUnknown *pRemUnk;
HRESULT hr = GetSecureRemUnk( &pRemUnk, pIPIDEntry->pOXIDEntry );
if (SUCCEEDED(hr))
{
hr = RemoteQueryInterface(pRemUnk, pIPIDEntry, cIIDs, pIIDs, &pRemQiRes,
fWeakClient);
}
// need to remember the result ptr so we can free it.
REMQIRESULT *pRemQiResNext = pRemQiRes;
// unmarshal each STDOBJREF returned. Note that while we did the
// RemoteQI we could have yielded (or nested) and did another
// RemoteQI for the same interfaces, so we have to call UnmarshalIPID
// which will find any existing IPIDEntry and bump its refcnt.
HRESULT hr2;
HRESULT *phr = &hr2;
void *pv;
void **ppv = &pv;
for (USHORT i=0; i<cIIDs; i++)
{
if (pQIRes)
{
// caller wants the pointers returned, set ppv and phr.
ppv = &pQIRes->pv;
phr = &pQIRes->hr;
pQIRes++;
}
if (SUCCEEDED(hr))
{
if (SUCCEEDED(pRemQiResNext->hResult))
{
if (fWeakClient)
{
// mark the std objref with the weak reference flag so
// that UnmarshalIPID adds the references to the correct
// count.
pRemQiResNext->std.flags |= SORF_WEAKREF;
}
*phr = UnmarshalIPID(*pIIDs, &pRemQiResNext->std,
pIPIDEntry->pOXIDEntry,
(pQIRes) ? ppv : NULL);
if (FAILED(*phr))
{
// could not unmarshal, release the resources with the
// server.
RemoteReleaseStdObjRef(&pRemQiResNext->std,
pIPIDEntry->pOXIDEntry);
}
}
else if (pQIRes)
{
// the requested interface was not returned so set the
// return code and interface ptr.
*phr = pRemQiResNext->hResult;
*ppv = NULL;
}
pIIDs++;
pRemQiResNext++;
}
else
{
// the whole call failed so return the error for each
// requested interface.
*phr = hr;
*ppv = NULL;
}
// make sure the ptr value is NULL on failure. It may be NULL or
// non-NULL on success. (ReconnectProxies wants NULL).
Win4Assert(SUCCEEDED(*phr) || *ppv == NULL);
}
// free the result buffer
CoTaskMemFree(pRemQiRes);
ASSERT_LOCK_HELD
AssertDisconnectPrevented();
ComDebOut((DEB_MARSHAL,
"CStdMarshal::RemQIAndUnmarshal this:%x hr:%x\n", this, hr));
return hr;
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::RemIsConnected, private
//
// Synopsis: Returns TRUE if most likely connected, FALSE if definitely
// not connected or pending disconnect.
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
BOOL CStdMarshal::RemIsConnected(void)
{
AssertValid();
Assert(ClientSide());
// the default link depends on us returning FALSE if we are either
// disconnected or just pending disconnect, in order that they avoid
// running their cleanup code twice.
BOOL fRes = (_dwFlags & (SMFLAGS_DISCONNECTED | SMFLAGS_PENDINGDISCONNECT))
? FALSE : TRUE;
ComDebOut((DEB_MARSHAL,
"CStdMarshal::RemIsConnected this:%x fResult:%x\n", this, fRes));
return fRes;
}
//+-------------------------------------------------------------------
//
// Member: CreateChannel, private
//
// Synopsis: Creates an instance of the Rpc Channel.
//
// History: 20-Feb-95 Rickhi Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::CreateChannel(OXIDEntry *pOXIDEntry, DWORD dwFlags,
REFIPID ripid, REFIID riid, CRpcChannelBuffer **ppChnl)
{
ASSERT_LOCK_HELD
HRESULT hr = S_OK;
if (_pChnl == NULL)
{
DWORD cState = ServerSide() ? server_cs : client_cs;
cState |= (dwFlags & SORF_FREETHREADED) ? freethreaded_cs : 0;
// make a channel. We dont need the call control stuff so just
// create the base class.
_pChnl = new CRpcChannelBuffer(_pStdId, pOXIDEntry, cState);
if (_pChnl == NULL)
{
hr = E_OUTOFMEMORY;
}
}
if (SUCCEEDED(hr) && ClientSide())
{
*ppChnl = _pChnl->Copy(pOXIDEntry, ripid, riid);
if (*ppChnl == NULL)
{
hr = E_OUTOFMEMORY;
}
}
else
{
*ppChnl = _pChnl;
}
ASSERT_LOCK_HELD
return hr;
}
//+-------------------------------------------------------------------
//
// Member: GetPSFactory, private
//
// Synopsis: loads the proxy/stub factory for given IID
//
// History: 20-Feb-95 Rickhi Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::GetPSFactory(REFIID riid, IUnknown *pUnkWow, BOOL fServer,
IPSFactoryBuffer **ppIPSF, BOOL *pfNonNDR)
{
ComDebOut((DEB_MARSHAL,
"CStdMarshal::GetPSFactory this:%x riid:%I pUnkWow:%x\n",
this, &riid, pUnkWow));
ASSERT_LOCK_RELEASED
// map iid to classid
CLSID clsid;
HRESULT hr = gRIFTbl.RegisterInterface(riid, fServer, &clsid);
#ifdef WX86OLE
BOOL fWx86 = FALSE;
#endif
if (SUCCEEDED(hr))
{
BOOL fWow = FALSE;
if (IsWOWThread())
{
// figure out if this is a custom interface from a 16bit
// app, since we have to load the 16bit proxy code if so.
IThunkManager *pThkMgr;
g_pOleThunkWOW->GetThunkManager(&pThkMgr);
Win4Assert(pThkMgr && "pUnk in WOW does not support IThunkManager.");
if (pUnkWow)
fWow = pThkMgr->IsCustom3216Proxy(pUnkWow, riid);
else
fWow = pThkMgr->IsIIDRequested(riid);
pThkMgr->Release();
}
#ifdef WX86OLE
// If we are in a Wx86 process then we need to determine if the
// PSFactory needs to be an x86 or native one.
else if (gcwx86.IsWx86Enabled())
{
// Callout to wx86 to ask it to determine if an x86 PS factory
// is required. Whole32 can tell if the stub needs to be x86
// by determining if pUnkWow is a custom interface proxy or not.
// Whole32 can determine if a x86 proxy is required by checking
// if the riid is one for a custom interface that is expected
// to be returned.
fWx86 = gcwx86.NeedX86PSFactory(pUnkWow, riid);
}
#endif
// if we are loading a 16bit custom proxy then mark it as non NDR
*pfNonNDR = (fWow) ? TRUE : FALSE;
if (IsEqualGUID(clsid, CLSID_PSOlePrx32))
{
// its our internal CLSID so go straight to our class factory.
hr = PrxDllGetClassObject(clsid, IID_IPSFactoryBuffer,
(void **)ppIPSF);
}
else
{
#ifdef WX86OLE
DWORD dwContext = fWow ? CLSCTX_INPROC_SERVER16
: (fWx86 ? CLSCTX_INPROC_SERVERX86 :
CLSCTX_INPROC_SERVER)
| CLSCTX_PS_DLL;
#else
DWORD dwContext = fWow ? CLSCTX_INPROC_SERVER16
: CLSCTX_INPROC_SERVER | CLSCTX_PS_DLL;
#endif
// load the dll and get the PS class object
hr = ICoGetClassObject(clsid, dwContext, NULL, IID_IPSFactoryBuffer,
(void **)ppIPSF);
#ifdef WX86OLE
if (fWx86 && FAILED(hr))
{
// if we are looking for an x86 PSFactory and we didn't find
// one on InprocServerX86 key then we need to check
// InprocServer32 key as well.
hr = ICoGetClassObject(clsid,
CLSCTX_INPROC_SERVER | CLSCTX_PS_DLL,
NULL, IID_IPSFactoryBuffer,
(void **)ppIPSF);
if (SUCCEEDED(hr) &&
(! gcwx86.IsN2XProxy((IUnknown *)*ppIPSF)))
{
((IUnknown *)*ppIPSF)->Release();
hr = REGDB_E_CLASSNOTREG;
}
}
#endif
AssertOutPtrIface(hr, *ppIPSF);
}
}
#if DBG==1
// if the fake NonNDR flag is set and its the test interface, then
// trick the code into thinking this is a nonNDR proxy. This is to
// enable simpler testing of an esoteric feature.
if (gfFakeNonNDR && IsEqualIID(riid, IID_ICube))
{
*pfNonNDR = TRUE;
}
#endif
ComDebOut((DEB_MARSHAL,
"CStdMarshal::GetPSFactory this:%x pIPSF:%x fNonNDR:%x hr:%x\n",
this, *ppIPSF, *pfNonNDR, hr));
ASSERT_LOCK_RELEASED
return hr;
}
//+-------------------------------------------------------------------
//
// Member: CreateProxy, private
//
// Synopsis: creates an interface proxy for the given interface
//
// Returns: [ppv] - interface of type riid, AddRef'd
//
// History: 20-Feb-95 Rickhi Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::CreateProxy(REFIID riid, IRpcProxyBuffer **ppProxy,
void **ppv, BOOL *pfNonNDR)
{
TRACECALL(TRACE_MARSHAL, "CreateProxy");
ComDebOut((DEB_MARSHAL,
"CStdMarshal::CreateProxy this:%x riid:%I\n", this, &riid));
AssertValid();
Win4Assert(ClientSide());
Win4Assert(ppProxy != NULL);
ASSERT_LOCK_HELD
// get the controlling IUnknown of this object
IUnknown *punkCtrl = _pStdId->GetCtrlUnk();
Win4Assert(punkCtrl != NULL);
if (InlineIsEqualGUID(riid, IID_IUnknown))
{
// there is no proxy for IUnknown so we handle that case here
punkCtrl->AddRef();
*ppv = (void **)punkCtrl;
*ppProxy = NULL;
*pfNonNDR = FALSE;
return S_OK;
}
UNLOCK
ASSERT_LOCK_RELEASED
// now construct the proxy for the interface
IPSFactoryBuffer *pIPSF = NULL;
HRESULT hr = GetPSFactory(riid, NULL, FALSE, &pIPSF, pfNonNDR);
if (SUCCEEDED(hr))
{
// got the class factory, now create an instance
hr = pIPSF->CreateProxy(punkCtrl, riid, ppProxy, ppv);
AssertOutPtrIface(hr, *ppProxy);
pIPSF->Release();
}
ASSERT_LOCK_RELEASED
LOCK
ComDebOut((DEB_MARSHAL,
"CStdMarshal::CreateProxy this:%x pProxy:%x pv:%x fNonNDR:%x hr:%x\n",
this, *ppProxy, *ppv, *pfNonNDR, hr));
return hr;
}
//+-------------------------------------------------------------------
//
// Member: CreateStub, private
//
// Synopsis: creates an interface stub and adds it to the IPID table
//
// History: 20-Feb-95 Rickhi Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::CreateStub(REFIID riid, IRpcStubBuffer **ppStub,
void **ppv, BOOL *pfNonNDR)
{
TRACECALL(TRACE_MARSHAL, "CreateStub");
ComDebOut((DEB_MARSHAL,
"CStdMarshal::CreateStub this:%x riid:%I\n", this, &riid));
AssertValid();
Win4Assert(ServerSide());
Win4Assert(ppStub != NULL);
ASSERT_LOCK_HELD
// get the IUnknown of the object
IUnknown *punkObj = _pStdId->GetServer();
Win4Assert(punkObj != NULL);
if (InlineIsEqualGUID(riid, IID_IUnknown))
{
// there is no stub for IUnknown so we handle that here
*ppv = (void *)punkObj;
*ppStub = NULL;
*pfNonNDR = FALSE;
return S_OK;
}
UNLOCK
ASSERT_LOCK_RELEASED
// 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 hr;
#ifdef WX86OLE
if (gcwx86.IsN2XProxy(punkObj))
{
// If we are creating a stub for an object that is living on the
// x86 side then we need to set the StubInvoke flag to allow QI
// to thunk the custom interface QI.
gcwx86.SetStubInvokeFlag((BOOL)1);
}
#endif
hr = punkObj->QueryInterface(riid, (void **)&pUnkIf);
AssertOutPtrIface(hr, pUnkIf);
if (SUCCEEDED(hr))
{
// now construct the stub for the interface
IPSFactoryBuffer *pIPSF = NULL;
hr = GetPSFactory(riid, pUnkIf, TRUE, &pIPSF, pfNonNDR);
if (SUCCEEDED(hr))
{
// got the class factory, now create an instance
hr = pIPSF->CreateStub(riid, punkObj, ppStub);
AssertOutPtrIface(hr, *ppStub);
pIPSF->Release();
}
if (SUCCEEDED(hr))
{
// remember the interface pointer
*ppv = (void *)pUnkIf;
}
else
{
// error, release the interface and return NULL
pUnkIf->Release();
*ppv = NULL;
}
}
ASSERT_LOCK_RELEASED
LOCK
ComDebOut((DEB_MARSHAL,
"CStdMarshal::CreateStub this:%x pStub:%x pv:%x fNonNDR:%x hr:%x\n",
this, *ppStub, *ppv, *pfNonNDR, hr));
return hr;
}
//+-------------------------------------------------------------------
//
// Member: FindIPIDEntry, private
//
// Synopsis: Finds an IPIDEntry, chained off this object, with the
// given riid.
//
// History: 20-Feb-95 Rickhi Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::FindIPIDEntry(REFIID riid, IPIDEntry **ppEntry)
{
ComDebOut((DEB_OXID,"CStdMarshal::FindIPIDEntry ppEntry:%x riid:%I\n",
ppEntry, &riid));
ASSERT_LOCK_HELD
IPIDEntry *pEntry = _pFirstIPID;
while (pEntry)
{
if (InlineIsEqualGUID(riid, pEntry->iid))
{
*ppEntry = pEntry;
return S_OK;
}
pEntry = pEntry->pNextOID; // get next entry in object chain
}
ASSERT_LOCK_HELD
return E_NOINTERFACE;
}
//+-------------------------------------------------------------------
//
// Member: FindIPIDEntryByIPID, private
//
// Synopsis: returns the IPIDEntry ptr for the given IPID
//
// History: 20-Feb-95 Rickhi Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::FindIPIDEntryByIPID(REFIPID ripid, IPIDEntry **ppEntry)
{
ASSERT_LOCK_HELD
IPIDEntry *pEntry = _pFirstIPID;
while (pEntry)
{
if (InlineIsEqualGUID(pEntry->ipid, ripid))
{
*ppEntry = pEntry;
return S_OK;
}
pEntry = pEntry->pNextOID; // get next entry in object chain
}
ASSERT_LOCK_HELD
return E_NOINTERFACE;
}
//+-------------------------------------------------------------------
//
// Member: FindIPIDEntryByInterface, internal
//
// Synopsis: returns the IPIDEntry ptr for the given proxy
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::FindIPIDEntryByInterface(void *pProxy, IPIDEntry **ppEntry)
{
ASSERT_LOCK_HELD
IPIDEntry *pEntry = _pFirstIPID;
*ppEntry = NULL;
while (pEntry)
{
if (pEntry->pv == pProxy)
{
*ppEntry = pEntry;
break;
}
pEntry = pEntry->pNextOID;
}
if (*ppEntry != NULL)
return S_OK;
else
return E_NOINTERFACE;
}
//+-------------------------------------------------------------------
//
// Member: IncSrvIPIDCnt, protected
//
// Synopsis: increments the refcnt on the IPID entry, and optionally
// AddRefs the StdId.
//
// History: 20-Feb-95 Rickhi Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::IncSrvIPIDCnt(IPIDEntry *pEntry, ULONG cRefs,
ULONG cPrivateRefs, SECURITYBINDING *pName,
DWORD mshlflags)
{
ComDebOut((DEB_MARSHAL, "IncSrvIPIDCnt this:%x pIPID:%x cRefs:%x cPrivateRefs:%x\n",
this, pEntry, cRefs, cPrivateRefs));
Win4Assert(ServerSide());
Win4Assert(pEntry);
Win4Assert(cRefs > 0 || cPrivateRefs > 0);
ASSERT_LOCK_HELD
HRESULT hr = S_OK;
if (cPrivateRefs != 0)
{
// Add a reference.
hr = gSRFTbl.IncRef( cPrivateRefs, pEntry->ipid, pName );
if (SUCCEEDED(hr))
{
BOOL fNotify = (pEntry->cPrivateRefs == 0) ? TRUE : FALSE;
pEntry->cPrivateRefs += cPrivateRefs;
if (fNotify)
{
// this inc causes the count to go from zero to non-zero, so we
// inc the strong count on the stdid to hold it alive until this
// IPID is released.
IncStrongAndNotifyAct(pEntry, mshlflags);
}
}
}
if (SUCCEEDED(hr))
{
if (mshlflags & (MSHLFLAGS_TABLESTRONG | MSHLFLAGS_TABLEWEAK))
{
// Table Marshal Case: inc the number of table marshals.
IncTableCnt();
}
if (mshlflags & (MSHLFLAGS_WEAK | MSHLFLAGS_TABLEWEAK))
{
if (pEntry->cWeakRefs == 0)
{
// this inc causes the count to go from zero to non-zero, so we
// AddRef the stdid to hold it alive until this IPID is released.
_pStdId->AddRef();
}
pEntry->cWeakRefs += cRefs;
}
else
{
BOOL fNotify = (pEntry->cStrongRefs == 0) ? TRUE : FALSE;
pEntry->cStrongRefs += cRefs;
if (fNotify)
{
// this inc causes the count to go from zero to non-zero, so we
// inc the strong count on the stdid to hold it alive until this
// IPID is released.
IncStrongAndNotifyAct(pEntry, mshlflags);
}
}
}
ASSERT_LOCK_HELD
return hr;
}
//+-------------------------------------------------------------------
//
// Member: IncTableCnt, public
//
// Synopsis: increments the count of table marshals
//
// History: 9-Oct-96 Rickhi Created
//
//--------------------------------------------------------------------
void CStdMarshal::IncTableCnt(void)
{
ASSERT_LOCK_HELD
// If something was marshaled for a table, we have to ignore
// rundowns until a subsequent RMD is called for it, at which
// time we start paying attention to rundowns again. Since there
// can be any number of table marshals, we have to refcnt them.
_cTableRefs++;
_dwFlags |= SMFLAGS_IGNORERUNDOWN;
}
//+-------------------------------------------------------------------
//
// Member: IncStrongAndNotifyAct, private
//
// Synopsis: notifies the activation code when this interface refcnt
// goes from 0 to non-zero and the activation code asked to be
// notified, and also increments the strong refcnt.
//
// History: 21-Apr-96 Rickhi Created
//
//--------------------------------------------------------------------
void CStdMarshal::IncStrongAndNotifyAct(IPIDEntry *pEntry, DWORD mshlflags)
{
ASSERT_LOCK_HELD
// inc the strong count on the stdid to hold it alive until this
// IPIDEntry is released.
_pStdId->IncStrongCnt();
if (mshlflags & MSHLFLAGS_NOTIFYACTIVATION &&
!(pEntry->dwFlags & IPIDF_NOTIFYACT))
{
// the activation code asked to be notified when the refcnt
// on this interface goes positive, and when it reaches
// zero again. Set a flag so we remember to notify
// activation when the strong reference reference count
// goes back down to zero.
pEntry->dwFlags |= IPIDF_NOTIFYACT;
UNLOCK
ASSERT_LOCK_RELEASED
BOOL fOK = NotifyActivation(TRUE, (IUnknown *)(pEntry->pv));
ASSERT_LOCK_RELEASED
LOCK
if (!fOK)
{
// call failed, so dont bother notifying
pEntry->dwFlags &= ~IPIDF_NOTIFYACT;
}
}
}
//+-------------------------------------------------------------------
//
// Member: DecSrvIPIDCnt, protected
//
// Synopsis: decrements the refcnt on the IPID entry, and optionally
// Releases the StdId.
//
// History: 20-Feb-95 Rickhi Created
//
//--------------------------------------------------------------------
void CStdMarshal::DecSrvIPIDCnt(IPIDEntry *pEntry, ULONG cRefs,
ULONG cPrivateRefs, SECURITYBINDING *pName,
DWORD mshlflags)
{
ComDebOut((DEB_MARSHAL, "DecSrvIPIDCnt this:%x pIPID:%x cRefs:%x cPrivateRefs:%x\n",
this, pEntry, cRefs, cPrivateRefs));
Win4Assert(ServerSide());
Win4Assert(pEntry);
Win4Assert(cRefs > 0 || cPrivateRefs > 0);
ASSERT_LOCK_HELD
// Note: we dont care about holding the LOCK over the Release call since
// the guy who called us is holding a ref to the StdId, so this Release
// wont cause us to go away.
if (mshlflags & (MSHLFLAGS_TABLESTRONG | MSHLFLAGS_TABLEWEAK))
{
// Table Marshal Case: dec the number of table marshals.
DecTableCnt();
}
if (mshlflags & (MSHLFLAGS_WEAK | MSHLFLAGS_TABLEWEAK))
{
Win4Assert(pEntry->cWeakRefs >= cRefs);
pEntry->cWeakRefs -= cRefs;
if (pEntry->cWeakRefs == 0)
{
// this dec caused the count to go from non-zero to zero, so we
// Release the stdid since this IPID is no longer holding it alive.
_pStdId->Release();
}
}
else
{
// Adjust the strong reference count. Don't let the caller release
// too many times.
if (pEntry->cStrongRefs < cRefs)
{
ComDebOut((DEB_WARN,"DecSrvIPIDCnt too many releases. IPID entry: 0x%x Extra releases: 0x%x",
pEntry, cRefs-pEntry->cStrongRefs));
cRefs = pEntry->cStrongRefs;
}
pEntry->cStrongRefs -= cRefs;
if (pEntry->cStrongRefs == 0 && cRefs != 0)
{
// this dec caused the count to go from non-zero to zero, so we
// dec the strong count on the stdid since the public references
// on this IPID is no longer hold it alive.
DecStrongAndNotifyAct(pEntry, mshlflags);
}
// Adjust the secure reference count. Don't let the caller release
// too many times.
if (pName != NULL)
{
cPrivateRefs = gSRFTbl.DecRef(cPrivateRefs, pEntry->ipid, pName);
}
else
{
cPrivateRefs = 0;
}
Win4Assert( pEntry->cPrivateRefs >= cPrivateRefs );
pEntry->cPrivateRefs -= cPrivateRefs;
if (pEntry->cPrivateRefs == 0 && cPrivateRefs != 0)
{
// this dec caused the count to go from non-zero to zero, so we
// dec the strong count on the stdid since the private references
// on this IPID is no longer hold it alive.
DecStrongAndNotifyAct(pEntry, mshlflags);
}
}
ASSERT_LOCK_HELD
}
//+-------------------------------------------------------------------
//
// Member: DecTableCnt, public
//
// Synopsis: decrements the count of table marshals
//
// History: 9-Oct-96 Rickhi Created
//
//--------------------------------------------------------------------
void CStdMarshal::DecTableCnt(void)
{
ASSERT_LOCK_HELD
// If something was marshaled for a table, we have to ignore
// rundowns until a subsequent RMD is called for it, at which
// time we start paying attention to rundowns again. Since there
// can be any number of table marshals, we have to refcnt them.
// This is also used by CoLockObjectExternal.
if (--_cTableRefs == 0)
{
// this was the last table marshal, so now we have to pay
// attention to rundown from normal clients, so that if all
// clients go away we cleanup.
_dwFlags &= ~SMFLAGS_IGNORERUNDOWN;
}
}
//+-------------------------------------------------------------------
//
// Member: DecStrongAndNotifyAct, private
//
// Synopsis: notifies the activation code if this interface has
// been released and the activation code asked to be
// notified, and also decrements the strong refcnt.
//
// History: 21-Apr-96 Rickhi Created
//
//--------------------------------------------------------------------
void CStdMarshal::DecStrongAndNotifyAct(IPIDEntry *pEntry, DWORD mshlflags)
{
ASSERT_LOCK_HELD
BOOL fNotifyAct = FALSE;
if ((pEntry->dwFlags & IPIDF_NOTIFYACT) &&
pEntry->cStrongRefs == 0 &&
pEntry->cPrivateRefs == 0)
{
// the activation code asked to be notified when the refcnt
// on this interface reaches zero. Turn the flag off so we
// don't call twice.
pEntry->dwFlags &= ~IPIDF_NOTIFYACT;
fNotifyAct = TRUE;
}
UNLOCK
ASSERT_LOCK_RELEASED
if (fNotifyAct)
{
NotifyActivation(FALSE, (IUnknown *)(pEntry->pv));
}
_pStdId->DecStrongCnt(mshlflags & MSHLFLAGS_KEEPALIVE);
ASSERT_LOCK_RELEASED
LOCK
}
//+-------------------------------------------------------------------
//
// Member: AddIPIDEntry, private
//
// Synopsis: Allocates and fills in an entry in the IPID table.
// The returned entry is not yet in the IPID chain.
//
// History: 20-Feb-95 Rickhi Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::AddIPIDEntry(OXIDEntry *pOXIDEntry, IPID *pipid,
REFIID riid, CRpcChannelBuffer *pChnl, IUnknown *pUnkStub,
void *pv, IPIDEntry **ppEntry)
{
ComDebOut((DEB_MARSHAL,"AddIPIDEntry this:%x pOXID:%x iid:%I pStub:%x pv:%x\n",
this, pOXIDEntry, &riid, pUnkStub, pv));
ASSERT_LOCK_HELD
// CODEWORK: while we released the lock to create the proxy or stub,
// the same interface could have been marshaled/unmarshaled. We should
// go check for duplicates now. This is just an optimization, not a
// requirement.
// get a new entry in the IPID table.
IPIDEntry *pEntryNew = gIPIDTbl.FirstFree();
if (pEntryNew == NULL)
{
// no free slots and could not allocate more memory to grow
return E_OUTOFMEMORY;
}
if (ServerSide())
{
// create an IPID for this entry
DWORD *pdw = &pipid->Data1;
*pdw = gIPIDTbl.GetEntryIndex(pEntryNew); // IPID table index
*(pdw+1) = GetCurrentProcessId(); // current PID
*(pdw+2) = GetCurrentThreadId(); // current TID
*(pdw+3) = gIPIDSeqNum++; // process sequence #
}
*ppEntry = pEntryNew;
pEntryNew->ipid = *pipid;
pEntryNew->iid = riid;
pEntryNew->pChnl = pChnl;
pEntryNew->pStub = pUnkStub;
pEntryNew->pv = pv;
pEntryNew->dwFlags = ServerSide() ? IPIDF_SERVERENTRY :
IPIDF_DISCONNECTED | IPIDF_NOPING;
pEntryNew->cStrongRefs = 0;
pEntryNew->cWeakRefs = 0;
pEntryNew->cPrivateRefs = 0;
pEntryNew->pOXIDEntry = pOXIDEntry;
ASSERT_LOCK_HELD
ComDebOut((DEB_MARSHAL,"AddIPIDEntry this:%x pIPIDEntry:%x ipid:%I\n",
this, pEntryNew, &pEntryNew->ipid));
return S_OK;
}
//+-------------------------------------------------------------------
//
// Member: ReleaseCliIPIDs, private
//
// Synopsis: walks the IPID table releasing the proxy/stub entries
// on the IPIDEntries associated with this Object.
//
// History: 20-Feb-95 Rickhi Created
//
//--------------------------------------------------------------------
void CStdMarshal::ReleaseCliIPIDs(void)
{
Win4Assert(ClientSide());
ASSERT_LOCK_RELEASED
LOCK
// first thing we do is detach the chain of IPIDs from the CStdMarshal
// while holding the LOCK. Then release the lock and walk the chain
// releasing the proxy/stub pointers. Note there should not be any other
// pointers to any of these IPIDs, so it is OK to muck with their state.
IPIDEntry *pFirstIPID = _pFirstIPID;
_pFirstIPID = NULL;
UNLOCK
ASSERT_LOCK_RELEASED;
IPIDEntry *pLastIPID;
IPIDEntry *pEntry = pFirstIPID;
while (pEntry)
{
// mark the entry as vacant and disconnected. Note we dont put
// it back in the FreeList yet. We leave it chained to the other
// IPIDs in the list, and add the whole chain to the FreeList at
// the end.
pEntry->dwFlags |= IPIDF_VACANT | IPIDF_DISCONNECTED;
if (pEntry->pStub)
{
ComDebOut((DEB_MARSHAL,"ReleaseProxy pProxy:%x\n", pEntry->pStub));
pEntry->pStub->Release();
pEntry->pStub = NULL;
}
pLastIPID = pEntry;
pEntry = pEntry->pNextOID;
}
if (pFirstIPID != NULL)
{
// now take the LOCK again and release all the IPIDEntries back into
// the IPIDTable in one fell swoop.
ASSERT_LOCK_RELEASED
LOCK
gIPIDTbl.ReleaseEntryList(pFirstIPID, pLastIPID);
UNLOCK
ASSERT_LOCK_RELEASED
}
ASSERT_LOCK_RELEASED
}
//+------------------------------------------------------------------------
//
// Member: CStdMarshal::LockClient/UnLockClient
//
// Synopsis: Locks the client side object during outgoing calls in order
// to prevent the object going away in a nested disconnect.
//
// Notes: UnLockClient is not safe in the freethreaded model.
// Fortunately pending disconnect can only be set in the
// apartment model on the client side.
//
// History: 12-Jun-95 Rickhi Created
//
//-------------------------------------------------------------------------
ULONG CStdMarshal::LockClient(void)
{
Win4Assert(ClientSide());
InterlockedIncrement(&_cNestedCalls);
return (_pStdId->GetCtrlUnk())->AddRef();
}
ULONG CStdMarshal::UnLockClient(void)
{
Win4Assert(ClientSide());
if ((InterlockedDecrement(&_cNestedCalls) == 0) &&
(_dwFlags & SMFLAGS_PENDINGDISCONNECT))
{
Disconnect();
}
return (_pStdId->GetCtrlUnk())->Release();
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::GetSecureRemUnk, public
//
// Synopsis: If the marshaller has its own remote unknown, use it.
// Otherwise use the OXID's remote unknown.
//
// History: 2-Apr-96 AlexMit Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::GetSecureRemUnk( IRemUnknown **ppSecureRemUnk,
OXIDEntry *pOXIDEntry )
{
ComDebOut((DEB_OXID, "CStdMarshal::GetSecureRemUnk ppRemUnk:%x\n",
ppSecureRemUnk));
ASSERT_LOCK_DONTCARE
if (_pSecureRemUnk != NULL)
{
*ppSecureRemUnk = _pSecureRemUnk;
return S_OK;
}
else
{
return gOXIDTbl.GetRemUnk( pOXIDEntry, ppSecureRemUnk );
}
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::LookupStub, private
//
// Synopsis: used by the channel to acquire the stub ptr for debugging
//
// History: 12-Jun-95 Rickhi Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::LookupStub(REFIID riid, IRpcStubBuffer **ppStub)
{
AssertValid();
Win4Assert(ServerSide());
ASSERT_LOCK_RELEASED
LOCK
IPIDEntry *pEntry;
HRESULT hr = FindIPIDEntry(riid, &pEntry);
if (SUCCEEDED(hr))
{
*ppStub = (IRpcStubBuffer *)pEntry->pStub;
}
UNLOCK
ASSERT_LOCK_RELEASED
return hr;
}
#if DBG==1
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::GetOXID, private, debug
//
// Synopsis: returns the OXID for this object
//
// History: 20-Feb-95 Rickhi Created
//
//--------------------------------------------------------------------
REFMOXID CStdMarshal::GetMOXID(void)
{
ASSERT_LOCK_HELD
if (ServerSide())
{
// local to this apartment, use the local OXID
return GetLocalOXIDEntry()->moxid;
}
else
{
Win4Assert(_pChnl);
return _pChnl->GetMOXID();
}
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::DbgWalkIPIDs
//
// Synopsis: Validates that the state of all the IPIDs is consistent.
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
void CStdMarshal::DbgWalkIPIDs(void)
{
IPIDEntry *pEntry = _pFirstIPID;
while (pEntry)
{
ValidateIPIDEntry(pEntry);
pEntry = pEntry->pNextOID;
}
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::AssertValid
//
// Synopsis: Validates that the state of the object is consistent.
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
void CStdMarshal::AssertValid()
{
LOCK
Win4Assert((_dwFlags & ~(SMFLAGS_CLIENT_SIDE | SMFLAGS_REGISTEREDOID |
SMFLAGS_PENDINGDISCONNECT | SMFLAGS_DISCONNECTED |
SMFLAGS_FIRSTMARSHAL | SMFLAGS_HANDLER | SMFLAGS_WEAKCLIENT |
SMFLAGS_IGNORERUNDOWN | SMFLAGS_CLIENTMARSHALED |
SMFLAGS_NOPING | SMFLAGS_TRIEDTOCONNECT)) == 0);
Win4Assert(_pStdId != NULL);
Win4Assert(IsValidInterface(_pStdId));
if (_pChnl != NULL)
{
Win4Assert(IsValidInterface(_pChnl));
_pChnl->AssertValid(FALSE, FALSE);
}
DbgWalkIPIDs();
UNLOCK
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::AssertDisconnectPrevented, private
//
// Synopsis: Just ensures that no disconnects can/have arrived.
//
// History: 21-Sep-95 Rickhi Created
//
//+-------------------------------------------------------------------
void CStdMarshal::AssertDisconnectPrevented()
{
ASSERT_LOCK_HELD
if (ServerSide())
Win4Assert(!(_dwFlags & SMFLAGS_DISCONNECTED));
Win4Assert(_cNestedCalls > 0);
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::ValidateSTD
//
// Synopsis: Ensures that the STDOBJREF is valid
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
void CStdMarshal::ValidateSTD(STDOBJREF *pStd)
{
LOCK
// validate the flags field
Win4Assert((pStd->flags & SORF_RSRVD_MBZ) == 0);
// validate the OID
OID oid;
OIDFromMOID(_pStdId->GetOID(), &oid);
Win4Assert(pStd->oid == oid);
if (ServerSide() || _pChnl != NULL)
{
// validate the OXID
OXID oxid;
OXIDFromMOXID(GetMOXID(), &oxid);
Win4Assert(pStd->oxid == oxid );
}
UNLOCK
}
//+-------------------------------------------------------------------
//
// Function: DbgDumpSTD
//
// Synopsis: dumps a formated STDOBJREF to the debugger
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
void DbgDumpSTD(STDOBJREF *pStd)
{
ULARGE_INTEGER *puintOxid = (ULARGE_INTEGER *)&pStd->oxid;
ULARGE_INTEGER *puintOid = (ULARGE_INTEGER *)&pStd->oid;
ComDebOut((DEB_MARSHAL,
"\n\tpStd:%x flags:%08x cPublicRefs:%08x\n\toxid: %08x %08x\n\t oid: %08x %08x\n\tipid:%I\n",
pStd, pStd->flags, pStd->cPublicRefs, puintOxid->HighPart, puintOxid->LowPart,
puintOid->HighPart, puintOid->LowPart, &pStd->ipid));
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::ValidateIPIDEntry
//
// Synopsis: Ensures that the IPIDEntry is valid
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
void CStdMarshal::ValidateIPIDEntry(IPIDEntry *pEntry)
{
// ask the table to validate the IPID entry
gIPIDTbl.ValidateIPIDEntry(pEntry, ServerSide(), _pChnl);
}
//+-------------------------------------------------------------------
//
// Member: CStdMarshal::DbgDumpInterfaceList
//
// Synopsis: Prints the list of Interfaces on the object.
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
void CStdMarshal::DbgDumpInterfaceList(void)
{
ComDebOut((DEB_ERROR, "\tInterfaces left on object are:\n"));
LOCK
// walk the IPID list printing the friendly name of each interface
IPIDEntry *pEntry = _pFirstIPID;
while (pEntry)
{
WCHAR wszName[MAX_PATH];
GetInterfaceName(pEntry->iid, wszName);
ComDebOut((DEB_ERROR,"\t\t %ws\t cRefs:%x\n",wszName,pEntry->cStrongRefs));
pEntry = pEntry->pNextOID;
}
UNLOCK
}
#endif // DBG == 1
//+-------------------------------------------------------------------
//
// Function: RemoteQueryInterface, private
//
// Synopsis: call RemoteQueryInterface on remote server.
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
INTERNAL RemoteQueryInterface(IRemUnknown *pRemUnk, IPIDEntry *pIPIDEntry,
USHORT cIIDs, IID *pIIDs,
REMQIRESULT **ppQiRes, BOOL fWeakClient)
{
ComDebOut((DEB_MARSHAL,
"RemoteQueryInterface pIPIDEntry:%x cIIDs:%x, pIIDs:%x riid:%I\n",
pIPIDEntry, cIIDs, pIIDs, pIIDs));
Win4Assert(pIPIDEntry->pOXIDEntry); // must have a resolved oxid
ASSERT_LOCK_HELD
// set the IPID according to whether we want strong or weak
// references. It will only be weak if we are an OLE container
// and are talking to an embedding running on the same machine.
IPID ipid = pIPIDEntry->ipid;
if (fWeakClient)
{
ipid.Data1 |= IPIDFLAG_WEAKREF;
}
UNLOCK
ASSERT_LOCK_RELEASED
HRESULT hr = pRemUnk->RemQueryInterface(ipid, REM_ADDREF_CNT,
cIIDs, pIIDs, ppQiRes);
ASSERT_LOCK_RELEASED
LOCK
ASSERT_LOCK_HELD
ComDebOut((DEB_MARSHAL, "RemoteQueryInterface hr:%x pQIRes:%x\n",
hr, *ppQiRes));
return hr;
}
//+-------------------------------------------------------------------
//
// Function: RemoteAddRef, private
//
// Synopsis: calls the remote server to AddRef one of its interfaces
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
INTERNAL RemoteAddRef(IPIDEntry *pIPIDEntry, OXIDEntry *pOXIDEntry,
ULONG cStrongRefs, ULONG cSecureRefs)
{
ComDebOut((DEB_MARSHAL,
"RemoteAddRef cRefs:%x cSecure:%x ipid:%I\n",
cStrongRefs, cSecureRefs, &pIPIDEntry->ipid));
ASSERT_LOCK_HELD
// if the object does not require pinging, it is also ignoring
// reference counts, so there is no need to go get more, just
// pretend like we did.
if (pIPIDEntry->dwFlags & IPIDF_NOPING)
{
return S_OK;
}
// get the IRemUnknown for the remote server
IRemUnknown *pRemUnk;
HRESULT hr = gOXIDTbl.GetRemUnk(pOXIDEntry, &pRemUnk);
if (SUCCEEDED(hr))
{
// call RemAddRef on the interface
REMINTERFACEREF rifRef;
rifRef.ipid = pIPIDEntry->ipid;
rifRef.cPublicRefs = cStrongRefs;
rifRef.cPrivateRefs = cSecureRefs;
UNLOCK
ASSERT_LOCK_RELEASED
HRESULT ignore;
hr = pRemUnk->RemAddRef(1, &rifRef, &ignore);
ASSERT_LOCK_RELEASED
LOCK
}
ASSERT_LOCK_HELD
ComDebOut((DEB_MARSHAL, "RemoteAddRef hr:%x\n", hr));
return hr;
}
//+-------------------------------------------------------------------
//
// Function: RemoteReleaseRifRef
//
// Synopsis: calls the remote server to release some IPIDs
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
INTERNAL RemoteReleaseRifRef(OXIDEntry *pOXIDEntry,
USHORT cRifRef, REMINTERFACEREF *pRifRef)
{
Win4Assert(pRifRef);
ComDebOut((DEB_MARSHAL,
"RemoteRelease pOXID:%x cRifRef:%x pRifRef:%x cRefs:%x ipid:%I\n",
pOXIDEntry, cRifRef, pRifRef, pRifRef->cPublicRefs, &pRifRef->ipid));
Win4Assert(pOXIDEntry);
ASSERT_LOCK_HELD
HRESULT hr;
if (IsSTAThread() &&
FAILED(CanMakeOutCall(CALLCAT_SYNCHRONOUS, IID_IRundown)))
{
// the call control will not let this apartment model thread make
// the outgoing release call (cause we're inside an InputSync call)
// so we post ourselves a message to do it later.
hr = PostReleaseRifRef(pOXIDEntry, cRifRef, pRifRef);
}
else
{
// get the IRemUnknown for the remote server
IRemUnknown *pRemUnk;
hr = gOXIDTbl.GetRemUnk(pOXIDEntry, &pRemUnk);
if (SUCCEEDED(hr))
{
UNLOCK
ASSERT_LOCK_RELEASED
hr = pRemUnk->RemRelease(cRifRef, pRifRef);
ASSERT_LOCK_RELEASED
LOCK
}
}
ComDebOut((DEB_MARSHAL, "RemoteRelease hr:%x\n", hr));
ASSERT_LOCK_HELD
return hr;
}
//+-------------------------------------------------------------------
//
// Function: PostReleaseRifRef
//
// Synopsis: Post a message to ourself to call RemoteReleaseRifRef later.
// This is used to make a synchronous remote Release call when
// a Release is done inside of an InputSync call. The call is
// delayed until we are out of the InputSync call, since the
// call control wont allow a synch call inside an inputsync call.
//
// History: 05-Apr-96 Rickhi Created.
//
//--------------------------------------------------------------------
INTERNAL PostReleaseRifRef(OXIDEntry *pOXIDEntry,
USHORT cRifRef, REMINTERFACEREF *pRifRef)
{
Win4Assert(pRifRef);
ComDebOut((DEB_MARSHAL,
"PostRelease pOXID:%x cRifRef:%x pRifRef:%x cRefs:%x ipid:%I\n",
pOXIDEntry, cRifRef, pRifRef, pRifRef->cPublicRefs, &pRifRef->ipid));
Win4Assert(pOXIDEntry);
ASSERT_LOCK_HELD
OXIDEntry *pLocalOXIDEntry = NULL;
HRESULT hr = gOXIDTbl.GetLocalEntry(&pLocalOXIDEntry);
if (SUCCEEDED(hr))
{
// allocate a structure to hold the data and copy in the RifRef
// list, OXIDEntry, and count of entries. Inc the OXID RefCnt to
// ensure it stays alive until the posted message is processed.
hr = E_OUTOFMEMORY;
ULONG cbRifRef = cRifRef * sizeof(REMINTERFACEREF);
ULONG cbAlloc = sizeof(POSTRELRIFREF) + (cbRifRef-1);
POSTRELRIFREF *pRelRifRef = (POSTRELRIFREF *) PrivMemAlloc(cbAlloc);
if (pRelRifRef)
{
IncOXIDRefCnt(pOXIDEntry); // keep alive
pRelRifRef->pOXIDEntry = pOXIDEntry;
pRelRifRef->cRifRef = cRifRef;
memcpy(&pRelRifRef->arRifRef, pRifRef, cbRifRef);
if (!PostMessage((HWND)pLocalOXIDEntry->hServerSTA,
WM_OLE_ORPC_RELRIFREF,
GetCurrentThreadId(),
(LPARAM)pRelRifRef))
{
// Post failed, free the structure and report an error.
DecOXIDRefCnt(pOXIDEntry);
PrivMemFree(pRelRifRef);
hr = RPC_E_SYS_CALL_FAILED;
}
}
}
ComDebOut((DEB_MARSHAL, "PostRelease hr:%x\n", hr));
ASSERT_LOCK_HELD
return hr;
}
//+-------------------------------------------------------------------
//
// Function: HandlePostReleaseRifRef
//
// Synopsis: Handles the ReleaseRifRef message that was posted to the
// current thread (by the current thread) in order to do a
// delayed remote release call. See PostReleaseRifRef above.
//
// History: 05-Apr-96 Rickhi Created.
//
//--------------------------------------------------------------------
INTERNAL HandlePostReleaseRifRef(LPARAM param)
{
Win4Assert(param);
ComDebOut((DEB_MARSHAL, "HandlePostRelease pRifRef:%x\n", param));
POSTRELRIFREF *pRelRifRef = (POSTRELRIFREF *)param;
ASSERT_LOCK_RELEASED
LOCK
// simply make the real remote release call now, then release the
// reference we have on the OXIDEntry, and free the message buffer.
// If this call fails, dont try again, otherwise we could spin busy
// waiting. Instead, just let Rundown clean up the server.
RemoteReleaseRifRef(pRelRifRef->pOXIDEntry,
pRelRifRef->cRifRef,
&pRelRifRef->arRifRef);
DecOXIDRefCnt(pRelRifRef->pOXIDEntry);
UNLOCK
ASSERT_LOCK_RELEASED
PrivMemFree(pRelRifRef);
ComDebOut((DEB_MARSHAL, "HandlePostRelease hr:%x\n", S_OK));
return S_OK;
}
//+-------------------------------------------------------------------
//
// Member: RemoteChangeRef
//
// Synopsis: calls the remote server to convert interface refereces
// from strong to weak or vise versa. This behaviour is
// required to support silent updates in the OLE container /
// link / embedding scenarios.
//
// Notes: This functionality is not exposed in FreeThreaded apps
// or in remote apps. The implication being that the container
// must be on the same machine as the embedding.
//
// History: 20-Nov-95 Rickhi Created.
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::RemoteChangeRef(BOOL fLock, BOOL fLastUnlockReleases)
{
ComDebOut((DEB_MARSHAL, "RemoteChangeRef \n"));
Win4Assert(ClientSide());
Win4Assert(IsSTAThread()); // not allowed in MTA Apartment
ASSERT_LOCK_RELEASED
// must be at least 1 proxy already connected in order to be able
// to do this. We cant just ASSERT that's true because we were not
// holding the lock on entry.
LOCK
HRESULT hr = PreventDisconnect();
// A previous version of OLE set the object to weak even it it was
// currently disconnected, and it remembered that it was weak and set
// any new interfaces that it later accquired to weak. I emulate that
// behaviour here.
if (fLock)
_dwFlags &= ~SMFLAGS_WEAKCLIENT;
else
_dwFlags |= SMFLAGS_WEAKCLIENT;
if (SUCCEEDED(hr))
{
REMINTERFACEREF *pRifRefAlloc = (REMINTERFACEREF *)
_alloca(_cIPIDs * sizeof(REMINTERFACEREF));
REMINTERFACEREF *pRifRef = pRifRefAlloc;
DWORD cSecure = gCapabilities & EOAC_SECURE_REFS ? 1 : 0;
USHORT cIIDs = 0;
OXIDEntry *pOXIDEntry = NULL;
IPIDEntry *pNextIPID = _pFirstIPID;
while (pNextIPID)
{
if (!(pNextIPID->dwFlags & IPIDF_DISCONNECTED))
{
if (pOXIDEntry == NULL)
{
// This is the first connected IPID we encountered.
// Get its OXID entry and make sure it is for a server
// process on the current machine.
if (!(pNextIPID->pOXIDEntry->dwFlags &
OXIDF_MACHINE_LOCAL))
{
// OXID is for a remote process. Abandon this call.
Win4Assert(cIIDs == 0); // skip call below
Win4Assert(pOXIDEntry == NULL); // dont dec below
Win4Assert(hr == S_OK); // report success
break; // exit while loop
}
// Remember the OXID and AddRef it to keep it alive
// over the duration of the call.
pOXIDEntry = pNextIPID->pOXIDEntry;
IncOXIDRefCnt(pOXIDEntry);
}
pRifRef->ipid = pNextIPID->ipid;
if (!fLock && pNextIPID->cStrongRefs > 0)
{
pRifRef->cPublicRefs = pNextIPID->cStrongRefs;
pRifRef->cPrivateRefs = pNextIPID->cPrivateRefs;
pNextIPID->cWeakRefs += pNextIPID->cStrongRefs;
pNextIPID->cStrongRefs = 0;
pNextIPID->cPrivateRefs = 0;
pRifRef++;
cIIDs++;
}
else if (fLock && pNextIPID->cStrongRefs == 0)
{
pRifRef->cPublicRefs = pNextIPID->cWeakRefs;
pRifRef->cPrivateRefs = cSecure;
pNextIPID->cStrongRefs += pNextIPID->cWeakRefs;
pNextIPID->cWeakRefs = 0;
pNextIPID->cPrivateRefs = cSecure;
pRifRef++;
cIIDs++;
}
}
// get next IPIDentry for this object
pNextIPID = pNextIPID->pNextOID;
}
if (cIIDs != 0)
{
// we have looped filling in the IPID list, and there are
// entries in the list. go call the server now. First, set up
// the flags, then reset the RifRef pointer since we trashed
// it while walking the list above.
DWORD dwFlags = (fLock) ? IRUF_CONVERTTOSTRONG : IRUF_CONVERTTOWEAK;
if (fLastUnlockReleases)
dwFlags |= IRUF_DISCONNECTIFLASTSTRONG;
hr = RemoteChangeRifRef(pOXIDEntry, dwFlags, cIIDs, pRifRefAlloc);
}
if (pOXIDEntry)
{
// release the OXIDEntry
DecOXIDRefCnt(pOXIDEntry);
}
}
else
{
// A previous implementation of OLE returned S_OK if the object was
// disconnected. I emulate that behaviour here.
hr = S_OK;
}
DbgWalkIPIDs();
UNLOCK
ASSERT_LOCK_RELEASED
// this will handle any Disconnect that came in while we were busy.
hr = HandlePendingDisconnect(hr);
ComDebOut((DEB_MARSHAL, "RemoteChangeRef hr:%x\n", hr));
ASSERT_LOCK_RELEASED
return hr;
}
//+-------------------------------------------------------------------
//
// Function: RemoteChangeRifRef
//
// Synopsis: calls the remote server to release some IPIDs
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
INTERNAL RemoteChangeRifRef(OXIDEntry *pOXIDEntry, DWORD dwFlags,
USHORT cRifRef, REMINTERFACEREF *pRifRef)
{
Win4Assert(pRifRef);
ComDebOut((DEB_MARSHAL,
"RemoteChangeRifRef pOXID:%x cRifRef:%x pRifRef:%x cRefs:%x ipid:%I\n",
pOXIDEntry, cRifRef, pRifRef, pRifRef->cPublicRefs, &(pRifRef->ipid)));
Win4Assert(pOXIDEntry);
ASSERT_LOCK_HELD
// get the IRemUnknown for the remote server
IRemUnknown *pRemUnk;
HRESULT hr = gOXIDTbl.GetRemUnk(pOXIDEntry, &pRemUnk);
if (SUCCEEDED(hr))
{
UNLOCK
ASSERT_LOCK_RELEASED
hr = ((IRemUnknown2 *)pRemUnk)->RemChangeRef(dwFlags, cRifRef, pRifRef);
ASSERT_LOCK_RELEASED
LOCK
}
ComDebOut((DEB_MARSHAL, "RemoteChangeRifRef hr:%x\n", hr));
ASSERT_LOCK_HELD
return hr;
}
//+-------------------------------------------------------------------
//
// Function: RemoteReleaseStdObjRef
//
// Synopsis: calls the remote server to release an ObjRef
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
INTERNAL RemoteReleaseStdObjRef(STDOBJREF *pStd, OXIDEntry *pOXIDEntry)
{
ComDebOut((DEB_MARSHAL, "RemoteReleaseStdObjRef pStd:%x\n pOXIDEntry:%x",
pStd, pOXIDEntry));
ASSERT_LOCK_HELD
REMINTERFACEREF rifRef;
rifRef.ipid = pStd->ipid;
rifRef.cPublicRefs = pStd->cPublicRefs;
rifRef.cPrivateRefs = 0;
// incase we get disconnected while in the RemRelease call
// we need to extract the OXIDEntry and AddRef it.
IncOXIDRefCnt(pOXIDEntry);
RemoteReleaseRifRef(pOXIDEntry, 1, &rifRef);
DecOXIDRefCnt(pOXIDEntry);
ComDebOut((DEB_MARSHAL, "RemoteReleaseStdObjRef hr:%x\n", S_OK));
ASSERT_LOCK_HELD
return S_OK;
}
//+-------------------------------------------------------------------
//
// Function: RemoteReleaseObjRef
//
// Synopsis: calls the remote server to release an ObjRef
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
INTERNAL RemoteReleaseObjRef(OBJREF &objref)
{
return RemoteReleaseStdObjRef(&ORSTD(objref).std, GetOXIDFromObjRef(objref));
}
//+-------------------------------------------------------------------
//
// Function: GetOXIDFromObjRef, private
//
// Synopsis: extracts the OXID from the OBJREF.
//
// History: 09-Jan-96 Rickhi Created.
//
//--------------------------------------------------------------------
OXIDEntry *GetOXIDFromObjRef(OBJREF &objref)
{
// TRICK: Internally we use the saResAddr.size field as the ptr
// to the OXIDEntry. See ReadObjRef and FillObjRef.
OXIDEntry *pOXIDEntry = (objref.flags & OBJREF_STANDARD)
? *(OXIDEntry **)&ORSTD(objref).saResAddr
: *(OXIDEntry **)&ORHDL(objref).saResAddr;
Win4Assert(pOXIDEntry);
return pOXIDEntry;
}
//+-------------------------------------------------------------------
//
// Function: WriteObjRef, private
//
// Synopsis: Writes the objref into the stream
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
INTERNAL WriteObjRef(IStream *pStm, OBJREF &objref, DWORD dwDestCtx)
{
ASSERT_LOCK_RELEASED
ULONG cbToWrite = (objref.flags & OBJREF_STANDARD)
? (2*sizeof(ULONG)) + sizeof(IID) + sizeof(STDOBJREF)
: (2*sizeof(ULONG)) + sizeof(IID) + sizeof(STDOBJREF) + sizeof(CLSID);
// write the fixed-sized part of the OBJREF into the stream
HRESULT hr = pStm->Write(&objref, cbToWrite, NULL);
if (SUCCEEDED(hr))
{
// write the resolver address into the stream.
// TRICK: Internally we use the saResAddr.size field as the ptr
// to the OXIDEntry. See ReadObjRef and FillObjRef.
DUALSTRINGARRAY *psa;
OXIDEntry *pOXIDEntry = GetOXIDFromObjRef(objref);
if (pOXIDEntry->pMIDEntry != gpLocalMIDEntry ||
dwDestCtx == MSHCTX_DIFFERENTMACHINE)
{
// the interface is for a remote server, or it is going to a
// remote client, therefore, marshal the resolver strings
psa = pOXIDEntry->pMIDEntry->Node.psaKey;
Win4Assert(psa->wNumEntries != 0);
}
else
{
// the interface is for an OXID local to this machine and
// the interface is not going to a remote client, marshal an
// empty string (we pay attention to this in ReadObjRef)
psa = &saNULL;
}
// These string bindings always come from the object exporter
// who has already padded the size to 8 bytes.
hr = pStm->Write(psa, SASIZE(psa->wNumEntries), NULL);
ComDebOut((DEB_MARSHAL,"WriteObjRef psa:%x\n", psa));
}
ASSERT_LOCK_RELEASED
return hr;
}
//+-------------------------------------------------------------------
//
// Function: ReadObjRef, private
//
// Synopsis: Reads the objref from the stream
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
INTERNAL ReadObjRef(IStream *pStm, OBJREF &objref)
{
ASSERT_LOCK_RELEASED
// read the signature, flags, and iid fields of the objref so we know
// what kind of objref we are dealing with and how big it is.
HRESULT hr = StRead(pStm, &objref, 2*sizeof(ULONG) + sizeof(IID));
if (SUCCEEDED(hr))
{
if ((objref.signature != OBJREF_SIGNATURE) ||
(objref.flags & OBJREF_RSRVD_MBZ) ||
(objref.flags == 0))
{
// the objref signature is bad, or one of the reserved
// bits in the flags is set, or none of the required bits
// in the flags is set. the objref cant be interpreted so
// fail the call.
Win4Assert(!"Invalid Objref Flags");
return RPC_E_INVALID_OBJREF;
}
// compute the size of the remainder of the objref and
// include the size fields for the resolver string array
STDOBJREF *pStd = &ORSTD(objref).std;
DUALSTRINGARRAY *psa;
ULONG cbToRead;
if (objref.flags & OBJREF_STANDARD)
{
cbToRead = sizeof(STDOBJREF) + sizeof(ULONG);
psa = &ORSTD(objref).saResAddr;
}
else if (objref.flags & OBJREF_HANDLER)
{
cbToRead = sizeof(STDOBJREF) + sizeof(CLSID) + sizeof(ULONG);
psa = &ORHDL(objref).saResAddr;
}
else if (objref.flags & OBJREF_CUSTOM)
{
cbToRead = sizeof(CLSID) + 2*sizeof(DWORD); // clsid + cbExtension + size
psa = NULL;
}
// read the rest of the (fixed sized) objref from the stream
hr = StRead(pStm, pStd, cbToRead);
if (SUCCEEDED(hr))
{
if (psa != NULL)
{
// Non custom interface. Make sure the resolver string array
// has some sensible values.
if (psa->wNumEntries != 0 &&
psa->wSecurityOffset >= psa->wNumEntries)
{
hr = RPC_E_INVALID_OBJREF;
}
}
else
{
// custom marshaled interface
if (ORCST(objref).cbExtension != 0)
{
// skip past the extensions since we currently dont
// know about any extension types.
LARGE_INTEGER dlibMove;
dlibMove.LowPart = ORCST(objref).cbExtension;
dlibMove.HighPart = 0;
hr = pStm->Seek(dlibMove, STREAM_SEEK_CUR, NULL);
}
}
}
if (SUCCEEDED(hr) && psa)
{
// Non custom interface. The data that follows is a variable
// sized string array. Allocate memory for it and then read it.
DbgDumpSTD(pStd);
DUALSTRINGARRAY *psaNew;
cbToRead = psa->wNumEntries * sizeof(WCHAR);
if (cbToRead == 0)
{
// server must be local to this machine, just get the local
// resolver strings and use them to resolve the OXID
psaNew = gpsaLocalResolver;
}
else
{
// allocate space to read the strings
psaNew = (DUALSTRINGARRAY *) _alloca(cbToRead + sizeof(ULONG));
if (psaNew != NULL)
{
// update the size fields and read in the rest of the data
psaNew->wSecurityOffset = psa->wSecurityOffset;
psaNew->wNumEntries = psa->wNumEntries;
hr = StRead(pStm, psaNew->aStringArray, cbToRead);
}
else
{
psa->wNumEntries = 0;
psa->wSecurityOffset = 0;
hr = E_OUTOFMEMORY;
// seek the stream past what we should have read, ignore
// seek errors, since the OOM takes precedence.
LARGE_INTEGER libMove;
libMove.LowPart = cbToRead;
libMove.HighPart = 0;
pStm->Seek(libMove, STREAM_SEEK_CUR, 0);
}
}
// TRICK: internally we want to keep the ObjRef a fixed size
// structure, even though we have variable sized data. To do
// this i use the saResAddr.size field of the ObjRef as a ptr
// to the OXIDEntry. We pay attention to this in FillObjRef,
// WriteObjRef and FreeObjRef.
if (SUCCEEDED(hr))
{
// resolve the OXID.
ASSERT_LOCK_RELEASED
LOCK
OXIDEntry *pOXIDEntry = NULL;
hr = gResolver.ClientResolveOXID(pStd->oxid,
psaNew, &pOXIDEntry);
UNLOCK
ASSERT_LOCK_RELEASED
*((void **) psa) = pOXIDEntry;
}
else
{
*((void **) psa) = NULL;
}
}
}
ComDebOut((DEB_MARSHAL,"ReadObjRef hr:%x objref:%x\n", hr, &objref));
ASSERT_LOCK_RELEASED
return hr;
}
//+-------------------------------------------------------------------
//
// Function: FreeObjRef, private
//
// Synopsis: Releases an objref that was read in from a stream via
// ReadObjRef.
//
// History: 20-Feb-95 Rickhi Created.
//
// Notes: Anybody who calls ReadObjRef should call this guy to
// free the objref. This decrements the refcnt on the
// embedded pointer to the OXIDEntry.
//
//--------------------------------------------------------------------
INTERNAL_(void) FreeObjRef(OBJREF &objref)
{
if (objref.flags & (OBJREF_STANDARD | OBJREF_HANDLER))
{
// TRICK: Internally we use the saResAddr.size field as the ptr to
// the OXIDEntry. See ReadObjRef, WriteObjRef and FillObjRef.
OXIDEntry *pOXIDEntry = GetOXIDFromObjRef(objref);
LOCK
Win4Assert(pOXIDEntry);
DecOXIDRefCnt(pOXIDEntry);
UNLOCK
}
}
//+-------------------------------------------------------------------
//
// Function: MakeFakeObjRef, private
//
// Synopsis: Invents an OBJREF that can be unmarshaled in this process.
// The objref is partially fact (the OXIDEntry) and partially
// fiction (the OID).
//
// History: 16-Jan-96 Rickhi Created.
//
// Notes: This is used by MakeSCMProxy and GetRemUnk. Note that
// the pOXIDEntry is not AddRef'd here because the OBJREF
// created is only short-lived the callers guarantee it's
// lifetime, so FreeObjRef need not be called.
//
//--------------------------------------------------------------------
INTERNAL MakeFakeObjRef(OBJREF &objref, OXIDEntry *pOXIDEntry,
REFIPID ripid, REFIID riid)
{
// first, invent an OID since this could fail.
STDOBJREF *pStd = &ORSTD(objref).std;
HRESULT hr = gResolver.ServerGetReservedID(&pStd->oid);
if (SUCCEEDED(hr))
{
pStd->flags = SORF_NOPING | SORF_FREETHREADED;
pStd->cPublicRefs = 1;
pStd->ipid = ripid;
OXIDFromMOXID(pOXIDEntry->moxid, &pStd->oxid);
// TRICK: Internally we use the saResAddr.size field as the ptr to
// the OXIDEntry. See ReadObjRef, WriteObjRef and FillObjRef.
OXIDEntry **ppOXIDEntry = (OXIDEntry **) &ORSTD(objref).saResAddr;
*ppOXIDEntry = pOXIDEntry;
objref.signature = OBJREF_SIGNATURE;
objref.flags = OBJREF_STANDARD;
objref.iid = riid;
}
return hr;
}
//+-------------------------------------------------------------------
//
// Function: MakeCallableFromAnyApt, private
//
// Synopsis: set SORF_FREETHREADED in OBJREF so unmarshaled proxy
// can be called from any apartment.
//
// History: 16-Jan-96 Rickhi Created.
//
//--------------------------------------------------------------------
void MakeCallableFromAnyApt(OBJREF &objref)
{
STDOBJREF *pStd = &ORSTD(objref).std;
pStd->flags |= SORF_FREETHREADED;
}
//+-------------------------------------------------------------------
//
// Function: FindStdMarshal, private
//
// Synopsis: Finds the CStdMarshal for the OID read from the stream
//
// Arguements: [objref] - object reference
// [ppStdMshl] - CStdMarshal returned, AddRef'd
//
// Algorithm: Read the objref, get the OID. If we already have an identity
// for this OID, use that, otherwise either create an identity
// object, or create a handler (which in turn will create the
// identity). The identity inherits CStdMarshal.
//
// History: 20-Feb-95 Rickhi Created.
//
//--------------------------------------------------------------------
INTERNAL FindStdMarshal(OBJREF &objref, CStdMarshal **ppStdMshl)
{
ComDebOut((DEB_MARSHAL,
"FindStdMarshal objref:%x ppStdMshl:%x\n", &objref, ppStdMshl));
HRESULT hr = CO_E_OBJNOTCONNECTED;
CStdIdentity *pStdId = NULL;
if (ChkIfLocalOID(objref, &pStdId))
{
if (pStdId)
{
hr = S_OK;
}
else
{
hr = CO_E_OBJNOTCONNECTED;
}
}
else
{
STDOBJREF *pStd = &ORSTD(objref).std;
ComDebOut((DEB_MARSHAL, "poid: %x\n", &pStd->oid));
ASSERT_LOCK_RELEASED
LOCK
OXIDEntry *pOXIDEntry = GetOXIDFromObjRef(objref);
// OXID is for different apartment, check the identity table for
// an existing OID.
MOID moid;
MOIDFromOIDAndMID(pStd->oid, pOXIDEntry->pMIDEntry->mid, &moid);
hr = LookupIDFromID(moid, TRUE, &pStdId);
if (FAILED(hr))
{
CStdIdentity *pStdIdPrev = NULL;
BOOL fDuplicate = FALSE;
if (objref.flags & OBJREF_STANDARD)
{
// create an instance of the identity for this OID. We want
// to be holding the lock while we do this since it wont
// exercise any app code.
hr = CreateIdentityHandler(NULL, pStd->flags,
IID_IStdIdentity, (void **)&pStdId);
AssertOutPtrIface(hr, pStdId);
if (SUCCEEDED(hr))
{
// set the identity while holding the lock. The result is
// checked below and we release if this fails.
hr = pStdId->SetOID(moid);
Win4Assert(pStdIdPrev == NULL);
}
}
else
{
// create an instance of the handler. the handler will
// aggregate in the identity, but will pass GUID_NULL for
// the OID so that the identity is not set in the table yet.
Win4Assert(!(ORHDL(objref).std.flags & SORF_FREETHREADED));
// dont want to hold the lock while creating the handler
// since this involves running app code and calling the
// SCM etc.
UNLOCK
ASSERT_LOCK_RELEASED
hr = CoCreateInstance(ORHDL(objref).clsid, NULL,
CLSCTX_INPROC_HANDLER,
IID_IStdIdentity, (void **)&pStdId);
AssertOutPtrIface(hr, pStdId);
ASSERT_LOCK_RELEASED
LOCK
// look for the OID in the table again, since it may have
// been added while we released the lock to create the
// handler.
if (SUCCEEDED(LookupIDFromID(moid, TRUE, &pStdIdPrev)))
{
// object was unmarshaled while we released the lock
// to create the handler, so we will use the existing one.
// since we are releasing app code, we need to release the
// lock.
fDuplicate = TRUE;
}
else if (SUCCEEDED(hr))
{
// set the OID now while we are holding the lock.
hr = pStdId->SetOID(moid);
Win4Assert(pStdIdPrev == NULL);
}
}
if (pStdId && (FAILED(hr) || fDuplicate))
{
Win4Assert( (FAILED(hr) && (pStdIdPrev == NULL)) ||
(fDuplicate && (pStdIdPrev != NULL)) );
UNLOCK
ASSERT_LOCK_RELEASED
pStdId->Release();
pStdId = pStdIdPrev;
ASSERT_LOCK_RELEASED
LOCK
}
}
UNLOCK
ASSERT_LOCK_RELEASED
}
*ppStdMshl = (CStdMarshal *)pStdId;
AssertOutPtrIface(hr, *ppStdMshl);
ComDebOut((DEB_MARSHAL,
"FindStdMarshal pStdMshl:%x hr:%x\n", *ppStdMshl, hr));
return hr;
}
//+------------------------------------------------------------------------
//
// Function: CompleteObjRef, public
//
// Synopsis: Fills in the missing fields of an OBJREF from a STDOBJREF
// and resolves the OXID. Also sets fLocal to TRUE if the
// object was marshaled in this apartment.
//
// History: 22-Jan-96 Rickhi Created
//
//-------------------------------------------------------------------------
HRESULT CompleteObjRef(OBJREF &objref, OXID_INFO &oxidInfo, REFIID riid, BOOL *pfLocal)
{
// tweak the objref so we can call ReleaseMarshalObjRef or UnmarshalObjRef
objref.signature = OBJREF_SIGNATURE;
objref.flags = OBJREF_STANDARD;
objref.iid = riid;
HRESULT hr = InitChannelIfNecessary();
if (FAILED(hr))
return hr;
ASSERT_LOCK_RELEASED
LOCK
OXIDEntry *pOXIDEntry = NULL;
MIDEntry *pMIDEntry;
hr = GetLocalMIDEntry(&pMIDEntry);
if (SUCCEEDED(hr))
{
hr = FindOrCreateOXIDEntry(ORSTD(objref).std.oxid,
oxidInfo,
FOCOXID_NOREF,
gpsaLocalResolver,
gLocalMid,
pMIDEntry,
&pOXIDEntry);
}
if (SUCCEEDED(hr))
{
OXIDEntry **ppOXIDEntry = (OXIDEntry **) &ORSTD(objref).saResAddr;
*ppOXIDEntry = pOXIDEntry;
*pfLocal = (pOXIDEntry == GetLocalOXIDEntry());
}
UNLOCK
ASSERT_LOCK_RELEASED
return hr;
}