mirror of https://github.com/lianthony/NT4.0
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
2288 lines
70 KiB
2288 lines
70 KiB
//+-------------------------------------------------------------------
|
|
//
|
|
// File: stdid.cxx
|
|
//
|
|
// Contents: identity object and creation function
|
|
// identity unmarshaler (only one instance) and access function
|
|
//
|
|
// History: 1-Dec-93 CraigWi Created
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
#include <ole2int.h>
|
|
|
|
#include "..\objact\objact.hxx" // used in IProxyManager::CreateServer
|
|
|
|
// temporary until the RH interface is abstracted; once that is done,
|
|
// the creation routine will be in olerem.h
|
|
#include <remhdlr.hxx>
|
|
#include <idtable.hxx>
|
|
|
|
// CODEWORK: FAILED .vs. != NOERROR tests
|
|
|
|
INTERNAL ReadIdentityHeader(IStream *pStm, SIdentityDataHdr *pidh,
|
|
CLSID *pclsidHandler, BOOL fTransparent);
|
|
|
|
INTERNAL ScmCreateObjectInstance(
|
|
REFCLSID rclsid,
|
|
DWORD dwContext,
|
|
void * pv,
|
|
InterfaceData **ppIFD);
|
|
|
|
//+----------------------------------------------------------------
|
|
//
|
|
// Class: CStdIdentity (stdid)
|
|
//
|
|
// Purpose: To be the representative of the identity of the object
|
|
// and to coordinate marshaling
|
|
//
|
|
// Interface: IStdIdentity, IMarshal
|
|
//
|
|
// History: 11-Dec-93 CraigWi Created.
|
|
// 21-Apr-94 CraigWi Stubmgr addref's object; move strong cnt
|
|
// 10-May-94 CraigWi IEC called for strong connections
|
|
// 17-May-94 CraigWi Container weak connections
|
|
// 31-May-94 CraigWi Tell object of weak pointers
|
|
//
|
|
// Details:
|
|
//
|
|
// on server side, two main cases:
|
|
// 1. id object aggregated to server object; server must ensure that
|
|
// two threads don't try to create different notions of identity;
|
|
// follow on marshal can be std or not; app can add data or not.
|
|
// a. no data at all (rare)
|
|
// CreateStdIdentity(pUnkOuter, pUnkOuter, NULL, iid, ppv);
|
|
//
|
|
// b. std marshaling; no app data
|
|
// CreateStdIdentity(pUnkOuter, pUnkOuter,PSTDMARSHAL,iid,ppv);
|
|
//
|
|
// c. app marshaling data in addition to std marshaling data;
|
|
// app calls GetStdRemMarshaler() during marshaling.
|
|
// CreateStdIdentity(pUnkOuter, pUnkOuter, pMarshal, iid, ppv);
|
|
//
|
|
// d. app marshaling data instead of std marshaling data.
|
|
// CreateStdIdentity(pUnkOuter, pUnkOuter, pMarshal, iid, ppv);
|
|
//
|
|
// 2. id object stand alone; two threads can simultaneously create
|
|
// this identity and the first one wins;
|
|
// follow on marshal is *only* std marshaling.
|
|
// CreateStdIdentity(NULL, pUnkControl, PSTDMARSHAL, iid, ppv);
|
|
//
|
|
// on client side, we have matching cases:
|
|
// 1a. id object and app object; no app data and no std marshaling
|
|
// CreateIdentityHandler(<any>, NULL, iid, ppv);
|
|
//
|
|
// 1b. id object combined with remoting object; no app marshaling,
|
|
// allthough app may intercept IMarshal methods to know when
|
|
// unmarshal happens; most common case
|
|
// CreateIdentityHandler(<any>, PSTDMARSHAL, iid, ppv);
|
|
// pCFStdMarshal->CI(<any>, iid, ppv);
|
|
//
|
|
// 1c. id object combined with remoting object; app adds data;
|
|
// app calls GetStdRemMarshaler() during marshaling.
|
|
// CreateIdentityHandler(<any>, pMarshal, iid, ppv);
|
|
//
|
|
// 1d. id object aggregated in with app object; no std remoting
|
|
// CreateIdentityHandler(<any>, pMarshal, iid, ppv);
|
|
//
|
|
// 2. same as 1b.
|
|
//
|
|
// In all cases where the id object is aggregated, client and server, the
|
|
// external IMarshal implementation must respond with the identity clsid
|
|
// and the identity data must be first. The simplest way to do that is
|
|
// to expose the IMarshal of the identity object.
|
|
//
|
|
// the remote handler piece (separate from the identity object) is created
|
|
// under the following external conditions:
|
|
// server side (value of pMarshal parameter):
|
|
// NULL: never
|
|
// PSTDMARSHAL: on first marshal (table or normal);
|
|
// later this will be changed to occur only on the
|
|
// first normal marshal (table marshaling would be completely
|
|
// handled by the identity object).
|
|
// pMarshal: when GetStdRemMarshaler() is called.
|
|
//
|
|
// client side:
|
|
// NULL: never
|
|
// PSTDMARSHAL: on first unmarshal (internally translated into
|
|
// GetStdRemMarshaler() call).
|
|
// pMarshal: when GetStdRemMarshaler() is called.
|
|
//
|
|
// (internally, the remote handler piece is always and only created within
|
|
// GetRH(). The remote handler piece, once created, it not released
|
|
// until the identity object is released. Thus pointers to it
|
|
// are stable as long as the identity object is stable.)
|
|
//
|
|
// the identity is determined:
|
|
// server side: on creation of the identity object
|
|
// client side: on first unmarshal
|
|
//
|
|
// the clsid on the server side is (for the value of pMarshal):
|
|
// NULL: CLSID_NULL
|
|
// PSTDMARSHAL: IStdMarshalInfo::GetClassForHandler
|
|
// if not supported or returns NULL, CLSID_StdMarshal;
|
|
// this determination is made once at startup.
|
|
// pMarshal: pMarshal->GetUnmarshalClass; this determination is made
|
|
// each time the unmarshal clsid is needed.
|
|
//
|
|
// the clsid on the client side is determined by:
|
|
// NULL: CLSID_NULL
|
|
// PSTDMARSHAL: first unmarshal
|
|
// pMarshal: pMarshal->GetUnmarshalClass.
|
|
//
|
|
// NOTE: IStdMarshalInfo is not as useful as it was once thought to be. The
|
|
// problem is that handlers don't support this interface and it doesn't seem
|
|
// worth it to extend that mechanism to get per-destctx clsids. Part of the
|
|
// problem is that the documentation tells people to check the pvDestCtx
|
|
// which prevents us from using our normal dest context anyway. Thus, the
|
|
// rule is: if you want the handler clsid to different depending on context,
|
|
// you must support custom marshaling and then if you want std identity ,
|
|
// you must aggregate the std identity object; if further you want std
|
|
// remoting, you must delegate to IStdIdentity::GetStdRemMarshal().
|
|
//
|
|
// relationship to identity table:
|
|
// Creation function adds to table; pointer in table is not addref'd
|
|
// Each use of the identity (lock external, marshaling, etc.) addrefs
|
|
// Last release removes from table
|
|
//
|
|
// the IProxyManager interface is supported by the identity object because
|
|
// the RH code was too difficult to change to support this directly. In
|
|
// particular, this would have meant sometimes aggregating the RH to the
|
|
// identity object (client side) and sometimes not (server side).
|
|
//
|
|
// REF COUNTING
|
|
//
|
|
// The identity of an object is held alive by calling AddConnection on the
|
|
// the identity object or the RH. In turn, this call AddRef's the identity
|
|
// object. Each AddConnection is balanced by a ReleaseConnection (which
|
|
// Releases the identity object). The medium level events that increment
|
|
// the connection count are:
|
|
// CoMarshalInterface
|
|
// CoLockObjectExternal(..., TRUE, ...);
|
|
// rpc connect for table case
|
|
//
|
|
// The events which decrement the connection count are:
|
|
// CoReleaseMarshalData MSHLFLAGS_TABLE* (including in the normal case)
|
|
// CoLockObjectExternal(..., FALSE, ...);
|
|
// rpc disconnect all cases (including rpc rundown)
|
|
//
|
|
// The identity object contains the count of strong connections. This number is
|
|
// only incremented with AddConnection and decremented with ReleaseConnection.
|
|
// On the server side, the strong count starts out at zero and when it reaches
|
|
// zero, we disconnect (with certain restrictions). On the client side, the
|
|
// strong count starts out at 1 to represent the fact that the client connection
|
|
// is initially a strong one. The client strong connection count is buffered
|
|
// and the server channel associated with that client is only notified when the
|
|
// count was zero or becomes zero. Normally, the only way for the client count
|
|
// to change is IRunnableObject::LockRunning and
|
|
// IRunnableObject::SetContainedObject. We currently don't expose IStdIdentity
|
|
// and don't implement IExternalConnetion in the handler.
|
|
//
|
|
// If the server object supports IWeakRef, the identity object
|
|
// communicates to the object the number of pointers that are held it. As
|
|
// of this writing, there are two and only two (IUnknown *, IWR *).
|
|
//
|
|
// CoDisconnectObject forces the termination of all connections, weak or strong
|
|
// and releases all pointers on the object held by the remoting system. This
|
|
// call can be made while executing a method on the object since the object is
|
|
// temporarily held alive during the call by the dispatch code.
|
|
//
|
|
// CODEWORK: if we decide to expose the identity object, we will have to
|
|
// chose the way(s) in which it can be created. Two ways come to mind:
|
|
// CoCreateStdIdentity(...) and CoCreateIdentityHandler;
|
|
// CoCreateInstance(CLSID_StdMarshal, ...) must map to CoCreateIdentityHandler
|
|
// for compatibility with 16bit OLE2. CoGetStandardMarshal() is equalivalent
|
|
// to CoCreateStdIdentity w/o aggregation.
|
|
//--------------------------------------------------------------------
|
|
|
|
|
|
#define DECLARE_INTERNAL_UNK() \
|
|
class CInternalUnk : public IUnknown \
|
|
{ \
|
|
public: \
|
|
/* *** IUnknown methods *** */ \
|
|
STDMETHOD(QueryInterface)(REFIID riid, VOID **ppvObj); \
|
|
STDMETHOD_(ULONG,AddRef)(void) ; \
|
|
STDMETHOD_(ULONG,Release)(void); \
|
|
}; \
|
|
friend CInternalUnk; \
|
|
CInternalUnk m_InternalUnk;
|
|
|
|
|
|
typedef enum tagSTDID_FLAGS
|
|
{
|
|
STDID_SERVER = 0, // on server side
|
|
STDID_CLIENT = 1, // on client side (non-local in RH terms)
|
|
STDID_STDMARSHAL = 2, // was created with PSTDMARSHAL
|
|
STDID_HASEC = 4, // server supports IEC for connections
|
|
#if DBG == 1
|
|
STDID_INDESTRUCTOR = 256, // dtor entered; assert on AddRef and others
|
|
#endif
|
|
} STDID_FLAGS;
|
|
|
|
|
|
class CStdIdentity : public IStdIdentity, public IMarshal, public IProxyManager
|
|
{
|
|
public:
|
|
friend INTERNAL CreateStdIdentity(IUnknown *pUnkOuter,
|
|
IUnknown *pUnkControl, IMarshal *pMarshal,
|
|
REFIID riid, void **ppv);
|
|
|
|
friend INTERNAL CreateIdentityHandler(IUnknown *pUnkOuter,
|
|
IMarshal *pMarshal, REFIID riid, void **ppv);
|
|
|
|
// IUnknown
|
|
STDMETHOD(QueryInterface) (REFIID riid, LPVOID *ppvObj);
|
|
STDMETHOD_(ULONG,AddRef) (void);
|
|
STDMETHOD_(ULONG,Release) (void);
|
|
|
|
// IStdIdentity
|
|
STDMETHOD_(void, GetObjectID)(OID *pOid);
|
|
STDMETHOD_(IUnknown *, GetServer)(BOOL fAddRef);
|
|
STDMETHOD_(void, RevokeObjectID)(void);
|
|
STDMETHOD_(IMarshal *, GetStdRemMarshal)(void);
|
|
STDMETHOD(AddConnection)(DWORD extconn, DWORD reserved);
|
|
STDMETHOD(ReleaseConnection)(DWORD extconn, DWORD reserved, BOOL fLastReleaseCloses);
|
|
STDMETHOD_(ULONG,ReleaseKeepAlive)(IUnknown *pUnkToRelease, DWORD reserved);
|
|
|
|
// IMarshal
|
|
STDMETHOD(GetUnmarshalClass)(REFIID riid, LPVOID pv,
|
|
DWORD dwDestContext, LPVOID pvDestContext,
|
|
DWORD mshlflags, LPCLSID pCid);
|
|
STDMETHOD(GetMarshalSizeMax)(REFIID riid, LPVOID pv,
|
|
DWORD dwDestContext, LPVOID pvDestContext,
|
|
DWORD mshlflags, LPDWORD pSize);
|
|
STDMETHOD(MarshalInterface)(LPSTREAM pStm, REFIID riid,
|
|
LPVOID pv, DWORD dwDestContext, LPVOID pvDestContext,
|
|
DWORD mshlflags);
|
|
STDMETHOD(UnmarshalInterface)(LPSTREAM pStm, REFIID riid,
|
|
VOID **ppv);
|
|
STDMETHOD(ReleaseMarshalData)(LPSTREAM pStm);
|
|
STDMETHOD(DisconnectObject)(DWORD dwReserved);
|
|
|
|
// IProxyManager (only if client side)
|
|
STDMETHOD(CreateServer)(REFCLSID rclsid, DWORD clsctx, void *pv);
|
|
STDMETHOD_(BOOL, IsConnected)(void);
|
|
STDMETHOD(LockConnection)(BOOL fLock, BOOL fLastUnlockReleases);
|
|
STDMETHOD_(void, Disconnect)();
|
|
|
|
STDMETHOD(CreateServerWithHandler)(REFCLSID rclsid, DWORD clsctx, void *pv,
|
|
REFCLSID rclsidHandler, IID iidSrv, void **ppv,
|
|
IID iidClnt, void *pClientSiteInterface);
|
|
|
|
implementations:
|
|
CStdIdentity(DWORD flags, IUnknown *pUnkOuter,
|
|
IUnknown *pUnkControl, REFOID oid, REFCLSID clsid,
|
|
IMarshal *pMarshal, IUnknown **ppUnkInternal);
|
|
~CStdIdentity();
|
|
|
|
// internal unknown
|
|
DECLARE_INTERNAL_UNK()
|
|
|
|
// CODEWORK: DECLARE_DEBUG()
|
|
|
|
#if DBG == 1
|
|
void AssertValid();
|
|
#else
|
|
void AssertValid() { }
|
|
#endif
|
|
|
|
// private to ::CreateServer
|
|
INTERNAL_(BOOL) MiIsForClsid(InterfaceData *pIFD,
|
|
REFCLSID clsidGiven, DWORD *pcbSkipToIdenHdr);
|
|
|
|
shared_state:
|
|
// if appropriate, it is noted below if the state of a member variable
|
|
// is correllated to whether this identity (client or server) is
|
|
// connected or not.
|
|
|
|
DWORD m_refs; // number of pointer refs
|
|
IUnknown *m_pUnkOuter; // controlling unknown; set once.
|
|
|
|
CMutexSem m_mxsRelease; // controls shutdown order (::Release only)
|
|
|
|
DWORD m_flags; // see STDID_* values above; set once.
|
|
|
|
OID m_oid; // the identity
|
|
CLSID m_clsidHandler; // the clsid of the handler; see flags
|
|
// these values are NULL when disconnected
|
|
// and non-NULL if connected.
|
|
|
|
IUnknown *m_pUnkControl; // the controlling unk of the object;
|
|
// this member has three possible values:
|
|
// pUnkOuter - client side; not addref'd
|
|
// pUnkControl - server side (which may
|
|
// be pUnkOuter if aggregated); addref'd
|
|
// NULL - server side, disconnected
|
|
|
|
void *m_pWRorECServer; // server side only; set if the server
|
|
// supports IWR or IEC.
|
|
|
|
DWORD m_cStrongCnt; // number of strong connections; when
|
|
// this count goes to zero, we release
|
|
// our refs to the object; see ReleaseConnection
|
|
|
|
DWORD m_cPendingStrongRelease;// makes CoDisconnect during last strong
|
|
// release work
|
|
|
|
INTERNAL_(IMarshal *) GetMarshalNext();
|
|
IMarshal *m_pMarshalNext; // the way in which the object is marshalled;
|
|
// three cases: NULL, PSTDMARSHAL, pMarshal; see above;
|
|
// NEVER AddRef'd. NULL and PSTDMARSHAL are cases are replaced
|
|
// by a real pMarshal the first time used.
|
|
// NOTE: same value if connected or disconnected.
|
|
|
|
INTERNAL_(IRemoteHdlr *) GetRH(void);
|
|
IRemoteHdlr *m_pRH; // the RH for this identity; has ref count
|
|
// although it has our m_pUnkOuter, it is not aggregated to us.
|
|
// NOTE: value not dependent upon whether connected or disconnected.
|
|
|
|
// CODEWORK: this will change when we move to a separate strong counting
|
|
// object; see below.
|
|
|
|
BOOL IsClient() { return m_flags & STDID_CLIENT; }
|
|
BOOL StdMarshalNext() { return m_flags & STDID_STDMARSHAL; }
|
|
#if DBG == 1
|
|
void SetNowInDestructor() { m_flags |= STDID_INDESTRUCTOR; }
|
|
BOOL IsInDestructor() { return m_flags & STDID_INDESTRUCTOR; }
|
|
#else
|
|
void SetNowInDestructor() { }
|
|
#endif
|
|
// returns IExternalConnection* ptr (not addref'd);
|
|
BOOL HasEC() { return m_flags & STDID_HASEC; }
|
|
IExternalConnection *GetEC()
|
|
{ Assert(HasEC());
|
|
return (IExternalConnection *)m_pWRorECServer;
|
|
}
|
|
};
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::CStdIdentity, private
|
|
//
|
|
// Synopsis: The part of the identity object creation which cannot fail.
|
|
//
|
|
// Arguments: for all but the last param, see CreateStdIdentity and
|
|
// CreateIdentityHandler.
|
|
// [ppUnkInternal] --
|
|
// when aggregated, this the internal unknown;
|
|
// when not aggregated, this is the controlling unknown
|
|
//
|
|
// History: 15-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
CStdIdentity::CStdIdentity(DWORD flags, IUnknown *pUnkOuter,
|
|
IUnknown *pUnkControl, REFOID oid, REFCLSID clsid,
|
|
IMarshal *pMarshal, IUnknown **ppUnkInternal)
|
|
{
|
|
m_refs = 1;
|
|
m_pUnkOuter = pUnkOuter ? pUnkOuter : &m_InternalUnk;
|
|
m_flags = flags;
|
|
if (pMarshal == PSTDMARSHAL)
|
|
m_flags |= STDID_STDMARSHAL;
|
|
|
|
m_oid = oid; // NULL on client side
|
|
Assert(!!IsClient() == (IsEqualGUID(oid, OID_NULL)));
|
|
|
|
m_clsidHandler = clsid; // NULL on client side
|
|
Assert(!!IsClient() == (IsEqualGUID(clsid, CLSID_NULL)));
|
|
|
|
m_pUnkControl = pUnkControl;// NULL -> use pUnkOuter
|
|
Assert(!!IsClient() == (m_pUnkControl == NULL));
|
|
if (m_pUnkControl == NULL)
|
|
m_pUnkControl = m_pUnkOuter;
|
|
|
|
m_cPendingStrongRelease = 0;
|
|
|
|
if (IsClient())
|
|
{
|
|
m_cStrongCnt = 1; // see comments above
|
|
m_pWRorECServer = NULL;
|
|
Assert((m_flags & (STDID_HASEC)) == 0);
|
|
}
|
|
else
|
|
{
|
|
m_cStrongCnt = 0; // see comments above
|
|
m_pUnkControl->AddRef();
|
|
m_pWRorECServer = NULL; // set below if IWR or IEC
|
|
|
|
// find out of server supports IWeakRef (we prefer it over IEC)
|
|
HRESULT hresult;
|
|
// find out if server supports IExternalConnection;
|
|
// Initialize to 1 to better detect improperly written servers.
|
|
IExternalConnection FAR* pECServer = (IExternalConnection FAR*)(unsigned long)1;
|
|
hresult = m_pUnkControl->QueryInterface(IID_IExternalConnection,(LPVOID FAR*)&pECServer);
|
|
#ifndef _CAIRO_
|
|
// BUGBUG: this assert far too annoying on Cairo right now [mikese]
|
|
// remove when we convert to MIDL 2.0 for ctypes.
|
|
AssertOutPtrIface(hresult, pECServer);
|
|
#endif
|
|
if (hresult == NOERROR)
|
|
{
|
|
m_pWRorECServer = pECServer; // transfer addref
|
|
m_flags |= STDID_HASEC;
|
|
}
|
|
}
|
|
|
|
Assert(pMarshal != NULL); // don't handle NULL yet (if ever)
|
|
m_pMarshalNext = pMarshal;// NEVER addref'd
|
|
|
|
*ppUnkInternal = &m_InternalUnk; // this is what the m_refs=1 is for
|
|
|
|
m_pRH = NULL;
|
|
|
|
// AssertValid by callers
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::~CStdIdentity, private
|
|
//
|
|
// Synopsis: Final destruction of the identity object. ID has been
|
|
// revoked by now (in internal ::Release). Here we disconnect
|
|
// on server.
|
|
//
|
|
// History: 15-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
CStdIdentity::~CStdIdentity()
|
|
{
|
|
Assert(m_refs == 0);
|
|
m_refs++; // simple guard against reentry of dtor
|
|
SetNowInDestructor(); // debug flag which enables asserts to detect
|
|
|
|
// strong count can be non-zero on client dtor
|
|
Assert(IsClient() || m_cStrongCnt == 0);
|
|
Assert(m_cPendingStrongRelease == 0);
|
|
DisconnectObject(0);
|
|
|
|
RevokeObjectID();
|
|
|
|
// m_pMarshalNext - no release
|
|
|
|
if (m_pRH != NULL)
|
|
{
|
|
// client RH must go away now since it has our punkOuter.
|
|
// IRH::Lock locks _punkObj for client so that the whole aggregate
|
|
// stays alive. Server side RH can persist, but might very well be
|
|
// disconnected and live only until calls unwind.
|
|
#if DBG == 1
|
|
if (IsClient())
|
|
Assert(m_pRH->Release() == 0);
|
|
else
|
|
#endif
|
|
m_pRH->Release();
|
|
}
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::GetRH, private
|
|
//
|
|
// Synopsis: Gets the RH for this identity (there is only one); may create;
|
|
// Not addref'd.
|
|
//
|
|
// Returns: RH if successful; NULL when out of memory.
|
|
//
|
|
// History: 11-Dec-93 CraigWi Created.
|
|
// 01-Aug-94 AlexGo handle out of memory cases
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
INTERNAL_(IRemoteHdlr *)CStdIdentity::GetRH()
|
|
{
|
|
if (m_pRH == NULL && m_pUnkControl != NULL)
|
|
{
|
|
// if server, give controlling unknown of object (which when
|
|
// agregated is also our pUnkOuter). If client, m_pUnkControl
|
|
// is our m_pUnkOuter.
|
|
|
|
HRESULT hr = E_OUTOFMEMORY;
|
|
m_pRH = new CRemoteHdlr(m_pUnkControl,
|
|
this, IsClient() ? 0 /*remote*/ : RHFLAGS_LOCAL, hr);
|
|
|
|
if(hr != NOERROR)
|
|
{
|
|
delete m_pRH;
|
|
m_pRH = NULL;
|
|
}
|
|
}
|
|
|
|
return m_pRH;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::GetMarshalNext, private
|
|
//
|
|
// Synopsis: Returns the next marshaler in the chain.
|
|
//
|
|
// Effects: If the m_pMarshalNext is PSTDMARSHAL, we return the
|
|
// the IMarshal of the RH. Updates m_pMarshalNext.
|
|
//
|
|
// Returns: Marshal next if successful; NULL if out of memory. Not AddRef'd.
|
|
//
|
|
// History: 11-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
INTERNAL_(IMarshal *) CStdIdentity::GetMarshalNext()
|
|
{
|
|
Assert(m_pMarshalNext != NULL);
|
|
|
|
if (m_pMarshalNext == PSTDMARSHAL)
|
|
{
|
|
// get std remoting marshal; NULL for oom; is Addref'd.
|
|
IMarshal *pMarshalNext = GetStdRemMarshal();
|
|
if (pMarshalNext == NULL)
|
|
return NULL;
|
|
|
|
// save ptr so we don't have to do this again; lifetime is
|
|
// governed by m_pRH (which we don't release until our destructor)
|
|
// and because we don't normally have a ref on this pointer, release.
|
|
m_pMarshalNext = pMarshalNext;
|
|
m_pMarshalNext->Release();
|
|
}
|
|
|
|
return m_pMarshalNext;
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::CInternalUnk::QueryInterface, private
|
|
//
|
|
// Synopsis: internal QI for the identity object; responds to:
|
|
// IUnknown
|
|
// IMarshal
|
|
// IStdIdentity
|
|
// IProxyManager - if client
|
|
// <any supported server interface when on client side>
|
|
//
|
|
// Returns: Normal QI values.
|
|
//
|
|
// History: 15-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CStdIdentity::CInternalUnk::QueryInterface(REFIID iid, VOID **ppv)
|
|
{
|
|
CStdIdentity *pStdID = GETPPARENT(this, CStdIdentity, m_InternalUnk);
|
|
|
|
pStdID->AssertValid();
|
|
|
|
if (IsEqualGUID(iid, IID_IUnknown))
|
|
*ppv = this;
|
|
else if (IsEqualGUID(iid, IID_IMarshal))
|
|
*ppv = (IMarshal *)pStdID;
|
|
else if (IsEqualGUID(iid, IID_IStdIdentity))
|
|
*ppv = (IStdIdentity *)pStdID;
|
|
else if (pStdID->IsClient())
|
|
{
|
|
if (IsEqualGUID(iid, IID_IProxyManager))
|
|
// on client side we also support IProxyManager
|
|
*ppv = (IProxyManager *)pStdID;
|
|
|
|
// else try client-side RH
|
|
else if (pStdID->m_pRH != NULL)
|
|
return pStdID->m_pRH->QueryInterface(iid, ppv);
|
|
|
|
else
|
|
goto NoInterface;
|
|
}
|
|
else
|
|
{
|
|
NoInterface:
|
|
*ppv = NULL;
|
|
return E_NOINTERFACE;
|
|
}
|
|
|
|
((IUnknown *)*ppv)->AddRef();
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::CInternalUnk::AddRef, public
|
|
//
|
|
// Synopsis: Nothing special.
|
|
//
|
|
// History: 15-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
STDMETHODIMP_(ULONG) CStdIdentity::CInternalUnk::AddRef(void)
|
|
{
|
|
CStdIdentity *pStdID = GETPPARENT(this, CStdIdentity, m_InternalUnk);
|
|
|
|
pStdID->AssertValid();
|
|
|
|
AssertSz(!pStdID->IsInDestructor(), "CStdIdentity AddRef'd during destruction");
|
|
|
|
return InterlockedAddRef(&pStdID->m_refs);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::CInternalUnk::Release, public
|
|
//
|
|
// Synopsis: Releases the identity object. When the ref count goes
|
|
// to zero, revokes the id and destroyes the object. This
|
|
// method is thread safe (and BUGBUG: the rest code will
|
|
// be made to synchronize with it).
|
|
//
|
|
// History: 15-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
STDMETHODIMP_(ULONG) CStdIdentity::CInternalUnk::Release(void)
|
|
{
|
|
CStdIdentity *pStdID = GETPPARENT(this, CStdIdentity, m_InternalUnk);
|
|
DWORD refs;
|
|
|
|
pStdID->AssertValid();
|
|
|
|
// get sem for rel; can get addrefs and revoke id, but releases are blocked
|
|
// BUGBUG: this seems awfully expensive, but this release count might
|
|
// not change that often (release marshal data, unlock external, rundown,
|
|
// rpc disconnect, etc.).
|
|
pStdID->m_mxsRelease.Request();
|
|
|
|
// BUGBUG: probably need to use this semaphore in places where we
|
|
// don't want to do work during/after shutdown (e.g., create RH,
|
|
// marshal interface, etc.)
|
|
|
|
if ((refs = InterlockedRelease(&pStdID->m_refs)) == 0)
|
|
{
|
|
// at this moment in time, refs is 0; this triggers revoking
|
|
// the id; we may still get addrefs and revoke id.
|
|
|
|
// clear all non-ref counted pointers (that we know about);
|
|
// currently this consists of the idtable and the remote handler.
|
|
pStdID->RevokeObjectID();
|
|
|
|
// if other pointers still present, we can get addref in here.
|
|
// if no other pointers, m_refs will still be zero
|
|
|
|
if (pStdID->m_refs == 0)
|
|
{
|
|
// no one possibly pointing to this object; release sem
|
|
// and delete (which also disconnects)
|
|
pStdID->m_mxsRelease.Release();
|
|
delete pStdID;
|
|
return 0;
|
|
}
|
|
|
|
// refs not zero; other addref'd pointers still exist
|
|
refs = pStdID->m_refs;
|
|
}
|
|
|
|
pStdID->m_mxsRelease.Release();
|
|
|
|
// allow releases; which may trigger the final release of the identity
|
|
|
|
return refs;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::IUnknown methods, public
|
|
//
|
|
// Synopsis: External IUnknown methods; delegates to m_pUnkOuter.
|
|
//
|
|
// History: 15-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CStdIdentity::QueryInterface(REFIID riid, VOID **ppvObj)
|
|
{
|
|
AssertValid();
|
|
|
|
return m_pUnkOuter->QueryInterface(riid, ppvObj);
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CStdIdentity::AddRef(void)
|
|
{
|
|
AssertValid();
|
|
|
|
return m_pUnkOuter->AddRef();
|
|
}
|
|
|
|
|
|
STDMETHODIMP_(ULONG) CStdIdentity::Release(void)
|
|
{
|
|
AssertValid();
|
|
|
|
return m_pUnkOuter->Release();
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::GetObjectID, public
|
|
//
|
|
// Synopsis: Returns the GUID identity of the object; OID_NULL if revoked.
|
|
//
|
|
// Arguments: [pOid] -- The place for the ID
|
|
//
|
|
// History: 15-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
STDMETHODIMP_(void) CStdIdentity::GetObjectID(OID *pOid)
|
|
{
|
|
// can't call AssertValid() because this is used within the asserts
|
|
|
|
// NULL if not set yet (before first unmarshal in client or after revoke)
|
|
*pOid = m_oid;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::GetServer, public
|
|
//
|
|
// Synopsis: Returns a pUnk for the identified object; NULL on client side
|
|
//
|
|
// The pointer is optionally addrefed depending upon fAddRef
|
|
//
|
|
// Arguments: [fAddRef] -- whether to addref the ptr
|
|
//
|
|
// Returns: The pUnk on the object.
|
|
//
|
|
// History: 15-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
STDMETHODIMP_(IUnknown *) CStdIdentity::GetServer(BOOL fAddRef)
|
|
{
|
|
// CODEWORK: not multi-threaded; protect against simulataneous call to disconnect.
|
|
|
|
// can't call AssertValid() because the RH assert uses it
|
|
|
|
if (IsClient() || m_pUnkControl == NULL)
|
|
return NULL;
|
|
|
|
// Verify validity
|
|
Assert(IsValidInterface(m_pUnkControl));
|
|
|
|
if (fAddRef)
|
|
m_pUnkControl->AddRef();
|
|
return m_pUnkControl;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::RevokeObjectID, public
|
|
//
|
|
// Synopsis: Disassociates the OID and the object (handler or server).
|
|
// Various other methods will fail (e.g., MarshalInterface).
|
|
//
|
|
// History: 15-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
STDMETHODIMP_(void) CStdIdentity::RevokeObjectID(void)
|
|
{
|
|
AssertValid();
|
|
|
|
// no need to work about multiple threading here; ClearObjectID just
|
|
// returns an error when the id is already gone
|
|
|
|
(void)ClearObjectID(m_oid, m_pUnkControl, this);
|
|
m_oid = OID_NULL;
|
|
m_clsidHandler = CLSID_NULL;
|
|
|
|
// NOTE: we do not disconnect here. If this routine is called
|
|
// by the object, that means the identity is aggregated and
|
|
// CoDisconnectObject still works (since it QI's for IStdIdentity).
|
|
// The only other call to this routine is in shutdown
|
|
// which will eventually cause a disconnect (in the destructor).
|
|
|
|
// That is, we want to avoid a call to this method which
|
|
// will prevent the object from disconnecting successfully.
|
|
|
|
// clear id pointer in RH (easier with shared weak ref struct)
|
|
// CODEWORK: what if the RH is created during this routine?
|
|
// CODEWORK: may change if we keep a identity count separte from the ref count.
|
|
if (m_pRH != NULL)
|
|
m_pRH->ClearIdentity();
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::GetStdRemMarshal, public
|
|
//
|
|
// Synopsis: Returns an internal pointer to the std remoting marshaler
|
|
// part; this pointer cannot be passed outside the aggregate!
|
|
//
|
|
// Returns: AddRef'd pointer to std rem marshaler; NULL if out of memory.
|
|
//
|
|
// History: 14-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
STDMETHODIMP_(IMarshal *) CStdIdentity::GetStdRemMarshal(void)
|
|
{
|
|
AssertValid();
|
|
|
|
IRemoteHdlr *pRH = GetRH(); // creates if necessary; NOTE: no addref
|
|
IMarshal *pMarshal;
|
|
|
|
if (pRH != NULL &&
|
|
pRH->QueryInterface(IID_IMarshal, (void **)&pMarshal) == NOERROR)
|
|
return pMarshal;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::AddConnection, ReleaseConnection, public
|
|
//
|
|
// Synopsis: Add or release a connection (strong or weak); for now
|
|
// forwards on to RH; CODEWORK: may move counts here.
|
|
//
|
|
// The AddRef/Release is avoided on the client side because it
|
|
// messes up the client's notion of the ref count. In the
|
|
// future we may keep the weak count separate from the
|
|
// ref count and could maintain the count on both the
|
|
// client and server.
|
|
//
|
|
// Returns: S_OK if successful; error code otherwise.
|
|
// For clients, failures occurs only during the remote call.
|
|
// For server AddConnection, the only failure is disconnected.
|
|
//
|
|
// Arguments: same as IExternalConnection methods.
|
|
//
|
|
// History: 15-Dec-93 CraigWi Created.
|
|
// 16-May-94 CraigWi Added weak container connections
|
|
// 19-Jul-94 AlexT Don't pass new extconn flags to WOW
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CStdIdentity::AddConnection(DWORD extconn, DWORD reserved)
|
|
{
|
|
AssertValid();
|
|
|
|
// we only allow these connection flags at present
|
|
Assert((extconn & ~(EXTCONN_STRONG|EXTCONN_WEAK|EXTCONN_CALLABLE)) == 0);
|
|
|
|
if (m_pUnkControl == NULL)
|
|
{
|
|
CairoleDebugOut((DEB_ERROR,
|
|
"Marshaling or locking a disconnected object"));
|
|
return CO_E_OBJNOTCONNECTED;
|
|
}
|
|
|
|
AssertSz(IsValidInterface(m_pUnkControl), "Invalid interface for marshaling or locking");
|
|
|
|
if (extconn&EXTCONN_STRONG)
|
|
{
|
|
// bump strong count and weak count (ref on identity itself)
|
|
InterlockedIncrement((long *)&m_cStrongCnt);
|
|
if (!IsClient())
|
|
AddRef();
|
|
|
|
// inform object of one more strong connection; do it after the
|
|
// increment so that any renentrant calls already have the new
|
|
// count. Nothing is expected at present, but better safe than
|
|
// sorry.
|
|
if (HasEC())
|
|
{
|
|
// 16-bit OLE only knew about EXTCONN_STRONG, so if we're in
|
|
// WOW we mask off the other EXTCONN flags. This allows
|
|
// 16-bit OLE apps which were incorrectly doing binary
|
|
// compares on the first parameter to run correctly.
|
|
|
|
GetEC()->AddConnection(IsWOWThread() ? (extconn & EXTCONN_STRONG) :
|
|
extconn,
|
|
NULL);
|
|
}
|
|
|
|
|
|
if (IsClient() && m_cStrongCnt == 1)
|
|
{
|
|
Assert((m_flags & (STDID_HASEC)) == 0);
|
|
Assert(m_pWRorECServer == NULL);
|
|
// client count went from 0 to 1; lock connection on server
|
|
|
|
HRESULT hr;
|
|
IRemoteHdlr *pRH = GetRH(); // creates if necessary; NOTE: no addref
|
|
if (pRH == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
// client strong count was zero, now one: tell server
|
|
hr = pRH->LockConnection(TRUE, FALSE);
|
|
}
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
InterlockedDecrement((long *)&m_cStrongCnt);
|
|
if (!IsClient())
|
|
Release();
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
CairoleDebugOut((DEB_ITRACE, "Inc [%x] StrongCnt of ID %x\n", m_cStrongCnt, this));
|
|
Win4Assert(m_cStrongCnt > 0);
|
|
return S_OK;
|
|
}
|
|
else if (extconn&EXTCONN_WEAK)
|
|
{
|
|
// we don't keep a separate count of weak connections
|
|
|
|
CairoleDebugOut((DEB_ITRACE, "Inc WeakCnt of ID %x\n", this));
|
|
|
|
// also hold alive the identity (which holds this RH alive)
|
|
if (!IsClient())
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
}
|
|
|
|
|
|
STDMETHODIMP CStdIdentity::ReleaseConnection(DWORD extconn,
|
|
DWORD reserved, BOOL fLastReleaseCloses)
|
|
{
|
|
AssertValid();
|
|
|
|
// we only allow these connection flags at present
|
|
Assert((extconn & ~(EXTCONN_STRONG|EXTCONN_WEAK|EXTCONN_CALLABLE)) == 0);
|
|
|
|
// NOTE: it is ok to call this method for disconnected objects (to
|
|
// adjust the reg counts.
|
|
|
|
// for now, must have fLastUR with tableweak. This is because
|
|
// we can't keep the identity around and thus not release the server
|
|
// object when there are no strong and no weak connections.
|
|
Assert((extconn&EXTCONN_WEAK) == 0 || fLastReleaseCloses);
|
|
|
|
AssertSz(m_pUnkControl == NULL || IsValidInterface(m_pUnkControl),
|
|
"Invalid interface for unmarshaling or unlocking");
|
|
|
|
if (extconn&EXTCONN_STRONG)
|
|
{
|
|
Win4Assert(m_cStrongCnt > 0 && "CoLockObjectExternal(FALSE) probably called too many times");
|
|
|
|
// CODEWORK: this is not thread safe since we may get an increment
|
|
// after the decrement and decision to release and since this
|
|
// release (_punkObj) may in fact be the one to close the object
|
|
|
|
CairoleDebugOut((DEB_ITRACE, "Dec [%x] StrongCnt of ID %x\n",
|
|
m_cStrongCnt - 1, this));
|
|
|
|
// inform object of one less strong connection; do it before the
|
|
// decrement so that the count is stable during any reentrant calls.
|
|
if (m_pWRorECServer != NULL)
|
|
{
|
|
Assert((m_flags & STDID_HASEC) != 0);
|
|
DWORD cRefsBefore = m_cStrongCnt;
|
|
|
|
m_cPendingStrongRelease++; // for reentrant Disconnect
|
|
|
|
Assert(m_cPendingStrongRelease <= m_cStrongCnt);
|
|
|
|
// 16-bit OLE only knew about EXTCONN_STRONG, so if we're in
|
|
// WOW we mask off the other EXTCONN flags. This allows
|
|
// 16-bit OLE apps which were incorrectly doing binary
|
|
// compares on the first parameter to run correctly.
|
|
|
|
Verify(GetEC()->ReleaseConnection(
|
|
IsWOWThread() ?
|
|
(extconn & EXTCONN_STRONG) :
|
|
extconn,
|
|
NULL,
|
|
fLastReleaseCloses)
|
|
>= cRefsBefore-m_cPendingStrongRelease);
|
|
|
|
m_cPendingStrongRelease--; // balance above
|
|
}
|
|
|
|
// if last strong ref, on server side, still connected and allowed
|
|
// to disconnect, do so. Disconnect will release all refs we
|
|
// have to object. We include the m_pWRorECServer in the test so that
|
|
// we remain connected when IEC or IWR is supported (but not for Wow,
|
|
// since Wow apps weren't expecting this). This is important
|
|
// so that a simple addref on the object can keep it alive *AND*
|
|
// the connection from this identity to the object remain. If
|
|
// we disconnected here under all conditions, a simple addref
|
|
// would keep the object alive, but separte the identity from the
|
|
// object. By implementing IEC, an object says that it knows how to
|
|
// shutdown correctly when the strong count goes to zero.
|
|
// CODEWORK: make thread safe
|
|
LONG lRefs = InterlockedDecrement((long*)&m_cStrongCnt);
|
|
HRESULT hr = S_OK;
|
|
|
|
if (0 == lRefs && m_pUnkControl != NULL &&
|
|
(IsWOWThread() || m_pWRorECServer == NULL))
|
|
{
|
|
// client: no strong refs (other conditions always true of clients)
|
|
// server: no strong refs, still connected, IWR/IEC not supported
|
|
|
|
if (!IsClient())
|
|
{
|
|
// server
|
|
if (fLastReleaseCloses)
|
|
{
|
|
// strong count now zero; disconect server object if flag
|
|
DisconnectObject(0);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// strong ref count now zero, tell server that connection is wk;
|
|
// only tell server if we think we are connected; if we later
|
|
// connect, we will make the same call there.
|
|
if (IsConnected())
|
|
{
|
|
IRemoteHdlr *pRH = GetRH();// NOTE: no addref
|
|
if (pRH == NULL)
|
|
{
|
|
hr = E_OUTOFMEMORY;
|
|
}
|
|
else
|
|
{
|
|
// client strong count was one, now zero: tell server
|
|
hr = pRH->LockConnection(FALSE, fLastReleaseCloses);
|
|
}
|
|
}
|
|
|
|
// if failed, oh well. Connection still weak.
|
|
}
|
|
}
|
|
|
|
// release hold on identity; ID may be gone now
|
|
if (!IsClient())
|
|
Release();
|
|
|
|
return hr;
|
|
}
|
|
else if (extconn&EXTCONN_WEAK)
|
|
{
|
|
// we don't keep a separate count of weak connections
|
|
|
|
CairoleDebugOut((DEB_ITRACE, "Dec WeakCnt of ID %x\n", this));
|
|
|
|
// don't need the identity (which might release this ID)
|
|
if (!IsClient())
|
|
Release();
|
|
return S_OK;
|
|
}
|
|
else
|
|
{
|
|
return E_UNEXPECTED;
|
|
}
|
|
}
|
|
|
|
|
|
//+------------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::ReleaseKeepAlive, public
|
|
//
|
|
// Synopsis: Releases given pointer via IWeakRef if object supports it
|
|
//
|
|
// Arguments: [pUnkToRelease] -- The pUnk to release; must be on the
|
|
// object in question;
|
|
//
|
|
// History: 2-June-94 CraigWi Created
|
|
//
|
|
//-------------------------------------------------------------------------
|
|
|
|
STDMETHODIMP_(ULONG) CStdIdentity::ReleaseKeepAlive(IUnknown *pUnkToRelease, DWORD reserved)
|
|
{
|
|
// client side or server which doesn't support IWR or disconnected
|
|
return pUnkToRelease->Release();
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::GetUnmarshalClass, public
|
|
//
|
|
// Synopsis: Returns the identity unmarshal class.
|
|
//
|
|
// Returns: S_OK - class is returned
|
|
//
|
|
// BUGBUG: error for unknown context
|
|
//
|
|
// History: 15-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CStdIdentity::GetUnmarshalClass(REFIID riid, LPVOID pv,
|
|
DWORD dwDestContext, LPVOID pvDestContext,
|
|
DWORD mshlflags, LPCLSID pCid)
|
|
{
|
|
AssertValid();
|
|
|
|
// BUGBUG: better understand all contexts!
|
|
|
|
*pCid = CLSID_IdentityUnmarshal;
|
|
return NOERROR;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::GetMarshalSizeMax, public
|
|
//
|
|
// Synopsis: Returns the max marshal size for the given context. Adds
|
|
// together the identity overhead and the size of the
|
|
// follow on marshaler.
|
|
//
|
|
// Returns: S_OK - size set
|
|
//
|
|
// E_OUTOFMEMORY
|
|
//
|
|
// History: 15-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CStdIdentity::GetMarshalSizeMax(REFIID riid, LPVOID pv,
|
|
DWORD dwDestContext, LPVOID pvDestContext,
|
|
DWORD mshlflags, LPDWORD pSize)
|
|
{
|
|
AssertValid();
|
|
|
|
// size of us + follow on marshal
|
|
HRESULT hr;
|
|
IMarshal *pMarshalNext = GetMarshalNext(); // NOTE: not Addref'd
|
|
|
|
if (pMarshalNext == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
hr = pMarshalNext->GetMarshalSizeMax(riid, pv, dwDestContext,
|
|
pvDestContext, mshlflags, pSize);
|
|
|
|
// add in the size we need: header + clsid if not std marshal.
|
|
*pSize += sizeof(SIdentityDataHdr) +
|
|
(!IsEqualGUID(m_clsidHandler, CLSID_StdMarshal) ? sizeof(CLSID) : 0);
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::MarshalInterface, public
|
|
//
|
|
// Synopsis: Marshal this identity to the context given.
|
|
//
|
|
// Effects: Writes a the marshal data for the identity and then
|
|
// asks the follow on marshaler to write its data.
|
|
//
|
|
// Returns: S_OK - done
|
|
//
|
|
// E_OUTOFMEMORY
|
|
//
|
|
// IStream errors
|
|
//
|
|
// GetUnmarshalClass errors
|
|
//
|
|
// other errors in follow on marshal
|
|
//
|
|
// History: 15-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CStdIdentity::MarshalInterface(LPSTREAM pStm, REFIID riid,
|
|
LPVOID pv, DWORD dwDestContext, LPVOID pvDestContext,
|
|
DWORD mshlflags)
|
|
{
|
|
AssertValid();
|
|
|
|
HRESULT hr;
|
|
IMarshal *pMarshalNext = GetMarshalNext(); // NOTE: not Addref'd
|
|
SIdentityDataHdr idh;
|
|
CLSID clsidHandler;
|
|
|
|
if (pMarshalNext == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// CODEWORK: multi-thread: should prevent revoked and/or disconnected
|
|
// identity from being marshaled.
|
|
if (IsEqualGUID(m_oid, OID_NULL))
|
|
return CO_E_OBJNOTREG;
|
|
|
|
idh.dwflags = mshlflags & IDENFLAGS_TABLE;
|
|
|
|
// if PSTDMARSHAL, we use existing clsid (better not be NULL)
|
|
// else we get the clsid from pMarshalNext since it might be
|
|
// determined by the destcontext.
|
|
|
|
// NOTE: this call can't delegate the 'stdandard marshal' since we
|
|
// would end up in an infnite loop. If the app supplied a pMarshalNext
|
|
// (at creation time), it must use IStdIdentity::GetStdRemMarshal()
|
|
// for contexts it doesn't understand.
|
|
|
|
if (StdMarshalNext())
|
|
{
|
|
Assert(!IsEqualGUID(m_clsidHandler, CLSID_NULL));
|
|
clsidHandler = m_clsidHandler;
|
|
}
|
|
else
|
|
{
|
|
hr = pMarshalNext->GetUnmarshalClass(IID_IUnknown, NULL,
|
|
dwDestContext, pvDestContext, mshlflags, &clsidHandler);
|
|
if (FAILED(hr))
|
|
return hr;
|
|
}
|
|
|
|
if (IsEqualGUID(clsidHandler, CLSID_StdMarshal))
|
|
idh.dwflags |= IDENFLAGS_STDMARSHAL;
|
|
|
|
// store the object id in the data
|
|
memcpy(&idh.ObjectID, &m_oid, sizeof(idh.ObjectID));
|
|
|
|
// write the interface header into the stream
|
|
hr = pStm->Write(&idh, sizeof(idh), NULL);
|
|
|
|
// write clsid if not std marshal
|
|
if (SUCCEEDED(hr) && (idh.dwflags & IDENFLAGS_STDMARSHAL) == 0)
|
|
hr = pStm->Write(&clsidHandler, sizeof(clsidHandler), NULL);
|
|
|
|
if (SUCCEEDED(hr))
|
|
hr = pMarshalNext->MarshalInterface(pStm, riid, pv, dwDestContext,
|
|
pvDestContext, mshlflags);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::UnmarshalInterface, public
|
|
//
|
|
// Synopsis: Second stage in unmarshaling an identity object; first
|
|
// part is handled by the static marshaler in coapi.cxx.
|
|
//
|
|
// Effects: Reads the identity header, records the identity (if not
|
|
// done as of yet), passes the rest of the data on to
|
|
// the follow on marshaler and then returns the requested
|
|
// interface.
|
|
//
|
|
// Returns: S_OK
|
|
//
|
|
// E_OUTOFMEMORY
|
|
//
|
|
// IStream errors
|
|
//
|
|
// CO_E_OBJNOTCONNECTED - trying to unmarshal on the server
|
|
// side, but the object was disconnected.
|
|
//
|
|
// E_UNEXPECTED - at least: this object already a different
|
|
// identity.
|
|
//
|
|
// History: 15-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CStdIdentity::UnmarshalInterface(LPSTREAM pStm, REFIID riid,
|
|
VOID **ppv)
|
|
{
|
|
AssertValid();
|
|
|
|
HRESULT hr;
|
|
IMarshal *pMarshalNext = GetMarshalNext(); // NOTE: not Addref'd
|
|
SIdentityDataHdr idh;
|
|
CLSID clsid;
|
|
BOOL fSetObjectID = FALSE;
|
|
|
|
if (ppv)
|
|
*ppv = NULL;
|
|
|
|
if (pMarshalNext == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
hr = ReadIdentityHeader(pStm, &idh, &clsid, FALSE /*fTransparent*/);
|
|
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
if (!IsEqualGUID(m_oid, GUID_NULL))
|
|
{
|
|
// make sure id matches
|
|
if (!IsEqualGUID(idh.ObjectID, m_oid))
|
|
return E_UNEXPECTED;
|
|
|
|
// on the clsid: this clsid was used to create the handler which
|
|
// aggregated the std remoting stuff. if std marshaling is next,
|
|
// it should be the same as the current m_clsidHandler. otherwise
|
|
// we can't check.
|
|
|
|
if (StdMarshalNext() && !IsEqualGUID(clsid, m_clsidHandler))
|
|
return E_UNEXPECTED;
|
|
}
|
|
else
|
|
{
|
|
// become this identity; may fail because of multi-threading;
|
|
// if two threads unmarshal a pointer to the same object at the
|
|
// same time, the first one to complete SetObjectID wins. Since
|
|
// very little of the initialization has taken place, the static
|
|
// marshaler detects this and simply releases the duplicate and
|
|
// reunmarshals the data with the newly created one.
|
|
// CODEWORK: there is a possibility of an inifinite loop if the
|
|
// winning identity is released before during the subsequent lookup.
|
|
|
|
Assert(IsClient()); // can only happen on client side
|
|
|
|
m_oid = idh.ObjectID;
|
|
|
|
if (StdMarshalNext())
|
|
m_clsidHandler = clsid;
|
|
// else determined by next marshal
|
|
|
|
hr = SetObjectID(idh.ObjectID, m_pUnkOuter, this, NULL);
|
|
fSetObjectID = TRUE; // we cleanup even if this SetObjectID fails
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
// this object cannot return a new identity; we specify this
|
|
// by passing IID_NULL (and expect success and no crashes!)
|
|
hr = pMarshalNext->UnmarshalInterface(pStm, IID_NULL, NULL);
|
|
}
|
|
|
|
// ah, got the object fully (re)initialized; now get the requested iface
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (m_pUnkControl == NULL)
|
|
{
|
|
// server disconnected
|
|
hr = CO_E_OBJNOTCONNECTED;
|
|
|
|
// BUGBUG debug info from 16bit OLE
|
|
}
|
|
else if (IsEqualGUID(riid, IID_NULL))
|
|
// for CreateServer's benefit
|
|
hr = NOERROR;
|
|
else
|
|
{
|
|
// connected server or client
|
|
hr = m_pUnkControl->QueryInterface(riid, ppv);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// SetObjectID or unmarshaling failed; revoke id (no error if already
|
|
// revoked or not set).
|
|
if (fSetObjectID)
|
|
RevokeObjectID();
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::ReleaseMarshalData, public
|
|
//
|
|
// Synopsis: Releases identity marshal data.
|
|
//
|
|
// History: 15-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CStdIdentity::ReleaseMarshalData(LPSTREAM pStm)
|
|
{
|
|
AssertValid();
|
|
|
|
HRESULT hr;
|
|
IMarshal *pMarshalNext = GetMarshalNext(); // NOTE: not Addref'd
|
|
SIdentityDataHdr idh;
|
|
CLSID clsid;
|
|
|
|
if (pMarshalNext == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// BUGBUG: make sure we are not doing release table marshal on client side
|
|
|
|
hr = ReadIdentityHeader(pStm, &idh, &clsid, FALSE /*fTransparent*/);
|
|
|
|
if (FAILED(hr))
|
|
return hr;
|
|
|
|
// if we have an id, verify that it is the same as the one in the header
|
|
if (!IsEqualGUID(m_oid, OID_NULL))
|
|
{
|
|
// make sure id matches
|
|
if (!IsEqualGUID(idh.ObjectID, m_oid))
|
|
return E_UNEXPECTED;
|
|
|
|
if (StdMarshalNext() && !IsEqualGUID(clsid, m_clsidHandler))
|
|
return E_UNEXPECTED;
|
|
}
|
|
|
|
if (SUCCEEDED(hr))
|
|
// pass on; remote handler deals with table marshaling, etc.
|
|
hr = pMarshalNext->ReleaseMarshalData(pStm);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::DisconnectObject, public
|
|
//
|
|
// Synopsis: Disconnects the server object from the identity and
|
|
// remoting systems.
|
|
//
|
|
// This code is safe for reentrant calls.
|
|
//
|
|
// Effects: Further marshaling, connect, lock will result in an error.
|
|
//
|
|
// Returns: S_OK
|
|
//
|
|
// see docs.
|
|
//
|
|
// History: 15-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CStdIdentity::DisconnectObject(DWORD dwReserved)
|
|
{
|
|
AssertValid();
|
|
|
|
HRESULT hr;
|
|
// BUGBUG: prevent creation of RH if not yet created!
|
|
IMarshal *pMarshalNext = GetMarshalNext(); // NOTE: not Addref'd
|
|
|
|
if (pMarshalNext != NULL)
|
|
hr = pMarshalNext->DisconnectObject(dwReserved);
|
|
else // no RH, not connected there.
|
|
hr = NOERROR;
|
|
|
|
// since the marshal next disconnected sucessfully, disconnect us;
|
|
// for clients, revoke id too since the server may shutdown; for
|
|
// servers, release m_pUnkControl.
|
|
if (SUCCEEDED(hr))
|
|
{
|
|
if (IsClient())
|
|
// client side: remove from global id table; leave 'connected'
|
|
// since m_pUnkControl is really our pUnkOuter.
|
|
RevokeObjectID();
|
|
else
|
|
{
|
|
// server side: release m_pUnkControl;
|
|
// prevent problem on recursive disconnect
|
|
if (m_pUnkControl != NULL)
|
|
{
|
|
AssertSz(IsValidInterface(m_pUnkControl), "Invalid IUnknown interface during disconnect");
|
|
|
|
void *pWRorECSave = m_pWRorECServer;
|
|
DWORD flagsSave = m_flags;
|
|
|
|
Assert((m_pWRorECServer != NULL) ==
|
|
((m_flags & (STDID_HASEC)) != 0));
|
|
|
|
if (m_pWRorECServer != NULL)
|
|
{
|
|
// disconnect is in effect release of all external
|
|
// connections (a later connect, which isn't really
|
|
// supported now, would add back any remaining
|
|
// connections.) We need to maintain the connection
|
|
// count (i.e., not change it here) so that we can do the
|
|
// proper thing on reconnect and not assert when the app
|
|
// removes the external connections (e.g., via
|
|
// CoLockObjectExternal(FALSE)). A key point about later
|
|
// calls to lock/unlock is that we set m_pWRorECServer to
|
|
// NULL which will not communicate the lock to the
|
|
// server. CoDisconnectObject, for example, calls this
|
|
// method and then releases all lrpc connections, some of
|
|
// which may be strong and thus call ReleaseRegConn which
|
|
// we don't want to call the server.
|
|
|
|
AssertSz(IsValidInterface(m_pWRorECServer), "Invalid IWR/IEC interface during disconnect");
|
|
|
|
// clear these together so reentrant calls are simpler
|
|
m_pWRorECServer = NULL;
|
|
m_flags &= ~(STDID_HASEC);
|
|
|
|
Assert(m_cPendingStrongRelease <= m_cStrongCnt);
|
|
|
|
DWORD cRefsConn = m_cStrongCnt - m_cPendingStrongRelease;
|
|
while (cRefsConn-- != 0)
|
|
{
|
|
// CODEWORK: how many are callable??
|
|
Verify(((IExternalConnection *)pWRorECSave)->
|
|
ReleaseConnection(EXTCONN_STRONG,
|
|
NULL, FALSE) >= cRefsConn);
|
|
}
|
|
|
|
// CODEWORK: the above code needs some changes to handle
|
|
// multiple threads
|
|
}
|
|
|
|
IUnknown *pUnkControl = m_pUnkControl;
|
|
m_pUnkControl = NULL;
|
|
|
|
// CODEWORK: for thread safety, pass &m_pUnkControl to
|
|
// ClearObjectUnk and clear it while holding the semaphore
|
|
// for the table; do the same for the call to ClearObjectID;
|
|
// this prevents a race condition from triggering an
|
|
// (important) assert within those routines.
|
|
|
|
// clear id table so a reuse of pUnkControl will not find it
|
|
(void)ClearObjectUnk(m_oid, pUnkControl, this);
|
|
|
|
// have released all pointers (except perhaps some in the RH
|
|
// due to a delayed disconnect) and now we release the
|
|
// final one or two.
|
|
// IExternalConnection case or nothing
|
|
if (flagsSave & STDID_HASEC)
|
|
((IExternalConnection *)pWRorECSave)->Release();
|
|
pUnkControl->Release();
|
|
}
|
|
}
|
|
}
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::MiIsForClsid, private
|
|
//
|
|
// Synopsis: Determines if the marshaled interface uses std remoting
|
|
// and the handler for the clsid given.
|
|
//
|
|
// Arguments: [pIFD] -- The raw marshaled interface data
|
|
// [clsidGiven] -- The one to check agains
|
|
// [pcbSkipToIdenHdr] -- The amount of data before the identity hdr
|
|
//
|
|
// Returns: The result of the condition above.
|
|
//
|
|
// History: 06-Jan-94 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
INTERNAL_(BOOL) CStdIdentity::MiIsForClsid(InterfaceData *pIFD,
|
|
REFCLSID clsidGiven, DWORD *pcbSkipToIdenHdr)
|
|
{
|
|
BYTE *pb = &pIFD->abData[0];
|
|
|
|
AssertValid();
|
|
|
|
if ((((SMiApiDataHdr *)pb)->dwflags & MIAPIFLAGS_STDIDENTITY) == 0)
|
|
// not std identity
|
|
return FALSE;
|
|
|
|
// NOTE: because of the above test, there is no clsid after the data hdr
|
|
|
|
DWORD cbExtension = 0;
|
|
if (((SMiApiDataHdr *)pb)->dwflags & MIAPIFLAGS_EXTENSION)
|
|
cbExtension = sizeof(DWORD) + *(DWORD *)(pb + sizeof(SMiApiDataHdr));
|
|
|
|
*pcbSkipToIdenHdr = sizeof(SMiApiDataHdr) + cbExtension;
|
|
pb += *pcbSkipToIdenHdr;
|
|
|
|
// pb now points to identity hdr; extract clsid for handler
|
|
|
|
CLSID clsidHdlr;
|
|
if (((SIdentityDataHdr *)pb)->dwflags & IDENFLAGS_STDMARSHAL)
|
|
// uses std marshal; clsid is CLSID_StdMarshal
|
|
clsidHdlr = CLSID_StdMarshal;
|
|
else
|
|
clsidHdlr = *(CLSID *)(pb + sizeof(SIdentityDataHdr));
|
|
|
|
// allow clsidHdlr to be the stdmarshal since that is a subset of
|
|
// all handlers which use std remoting.
|
|
return IsEqualGUID(clsidHdlr, CLSID_StdMarshal) ||
|
|
IsEqualGUID(clsidGiven, clsidHdlr);
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::CreateServer, public
|
|
//
|
|
// Synopsis: Creates the server clsid in the given context and
|
|
// attaches it to this handler.
|
|
//
|
|
// History: 16-Dec-93 CraigWi Created.
|
|
//
|
|
// NOTE : remote aggregation requires that a client
|
|
// be able to dtermine the clsid up front for the handler. This
|
|
// generally means that some published clsid is used (in 16bit OLE2
|
|
// this was always the same as the object itself). In particular,
|
|
// this means that remote aggregation requires the use of std remoting;
|
|
// the exact nature of the restriction is yet to be detailed.
|
|
//
|
|
// CODEWORK: this code is not thread safe
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CStdIdentity::CreateServer(REFCLSID rclsid, DWORD clsctx, void *pv)
|
|
{
|
|
HRESULT hr;
|
|
InterfaceData *pIFD;
|
|
|
|
AssertValid();
|
|
|
|
Assert(IsClient()); // must be client side
|
|
Assert(IsValidInterface(m_pUnkControl)); // must be valid
|
|
Assert(!IsConnected());
|
|
|
|
// Loop trying to get object from the server. Because the server can be
|
|
// in the process of shutting down and respond with a marshaled interface,
|
|
// we will retry this call if unmarshaling fails assuming that the above
|
|
// is true.
|
|
const int MAX_SERVER_TRIES = 3;
|
|
|
|
for (int i = 0; i < MAX_SERVER_TRIES; i++)
|
|
{
|
|
|
|
// create object and get back marshaled pointer
|
|
if (FAILED(hr = ScmCreateObjectInstance(rclsid, clsctx, pv, &pIFD)) ||
|
|
hr > SCM_S_HANDLER)
|
|
{
|
|
// If an error occurred, return that otherwise convert a wierd
|
|
// success into E_FAIL. The point here is to return an error that
|
|
// the caller can figure out what happened.
|
|
return FAILED(hr) ? hr : E_FAIL;
|
|
}
|
|
|
|
// NOTE: this requires the ability to ReleaseMarshalData on error and
|
|
// have it work!!
|
|
CXmitRpcStream xrpc(pIFD);
|
|
LARGE_INTEGER li;
|
|
DWORD cbSkipToIdenHdr;
|
|
|
|
// marshaled interface must be std marshaling and clsid must match
|
|
if (!MiIsForClsid(pIFD, rclsid, &cbSkipToIdenHdr))
|
|
hr = E_FAIL; // BUGBUG real error
|
|
else
|
|
{
|
|
// skip SMiDataHdr + extension; length calculated above
|
|
LISet32(li, cbSkipToIdenHdr);
|
|
xrpc.Seek(li, STREAM_SEEK_SET, NULL);
|
|
|
|
// just unmarshal as normal (in case there is app data).
|
|
hr = UnmarshalInterface(&xrpc, IID_NULL, NULL);
|
|
|
|
if (FAILED(hr))
|
|
{
|
|
CairoleDebugOut((DEB_ERROR, "ScmCreateObjectInstance Result FAiled unmarshal\n"));
|
|
}
|
|
|
|
// NOTE: the above routine returns an error if, for some reason, the
|
|
// identity already exists in this process (via call to SetObjectID).
|
|
}
|
|
|
|
// rewind stream, release marshaled data and free data
|
|
LISet32(li, 0);
|
|
xrpc.Seek(li, STREAM_SEEK_SET, NULL);
|
|
CoReleaseMarshalData(&xrpc);
|
|
MIDL_user_free(pIFD);
|
|
|
|
// If either this worked or we got a packet we couldn't unmarshal
|
|
// at all we give up. Otherwise, we will hope that recontacting the
|
|
// SCM will fix things.
|
|
if (SUCCEEDED(hr) || (hr == E_FAIL))
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if no locks, tell stub mgr about weak connection; ignore errors
|
|
if (hr == NOERROR && m_cStrongCnt == 0)
|
|
{
|
|
Assert(IsConnected());
|
|
|
|
IRemoteHdlr *pRH = GetRH();// NOTE: no addref
|
|
if (pRH != NULL)
|
|
{
|
|
// client strong count is zero and we are now conected: tell server
|
|
pRH->LockConnection(FALSE, FALSE);
|
|
}
|
|
}
|
|
|
|
// CODEWORK: could fail because other thread created identity??
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::IsConnected, public
|
|
//
|
|
// Synopsis: Indicates if the client is connected to the server.
|
|
// Only the negative answer is definitive because we
|
|
// might not be able to tell if the server is connected
|
|
// and even if we could, the answer might be wrong by
|
|
// the time the caller acted on it.
|
|
//
|
|
// If we answer FALSE, we clean up the identity (part of a
|
|
// disconnect). Each level of the IsConnected calls cleans
|
|
// up similarly.
|
|
//
|
|
// Returns: TRUE if the server might be connected; FALSE if
|
|
// definitely not.
|
|
//
|
|
// History: 16-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
STDMETHODIMP_(BOOL) CStdIdentity::IsConnected(void)
|
|
{
|
|
Assert(IsClient()); // must be client side
|
|
AssertValid();
|
|
|
|
if (m_pRH && m_pRH->IsConnected())
|
|
{
|
|
return TRUE;
|
|
}
|
|
else
|
|
{
|
|
RevokeObjectID();
|
|
AssertValid();
|
|
return FALSE;
|
|
}
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::LockConnection, public
|
|
//
|
|
// Synopsis: Locks or unlocks the connection. Used only for container
|
|
// connections now. May want to change if we expose.
|
|
//
|
|
// Effects: Changes the behavior in the RH on the server side
|
|
// such that this connection, while it causes the RH
|
|
// to addref the pUnk, it does not come into play in
|
|
// the strong count.
|
|
//
|
|
// Returns:
|
|
//
|
|
// History: 16-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
STDMETHODIMP CStdIdentity::LockConnection(BOOL fLock, BOOL fLastUnlockReleases)
|
|
{
|
|
HRESULT hr;
|
|
|
|
Assert(IsClient()); // must be client side
|
|
|
|
AssertValid();
|
|
|
|
if (fLock)
|
|
hr = AddConnection(EXTCONN_STRONG, 0);
|
|
else
|
|
hr = ReleaseConnection(EXTCONN_STRONG, 0, fLastUnlockReleases);
|
|
|
|
return hr;
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::Disconnect, public
|
|
//
|
|
// Synopsis: Disconnects the client from the server.
|
|
//
|
|
// Effects: Releases the rpc channel and tells the proxy object to do so.
|
|
//
|
|
// Returns:
|
|
//
|
|
// History: 16-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
STDMETHODIMP_(void) CStdIdentity::Disconnect()
|
|
{
|
|
Assert(IsClient()); // must be client side
|
|
|
|
DisconnectObject(0);
|
|
}
|
|
|
|
//+---------------------------------------------------------------------------
|
|
//
|
|
// Method: CStdIdentity::CreateServerWithHandler
|
|
//
|
|
// Synopsis: Creates a dummy function that returns E_NOTIMPL
|
|
//
|
|
// Effects: The CreateServerWithHandler functionality is only supported
|
|
// in DCOM. DCOMREM actually implements this functionality.
|
|
//
|
|
// Arguments: [rclsid] --
|
|
// [clsctx] --
|
|
// [pv] --
|
|
// [rclsidHandler] --
|
|
// [iidSrv] --
|
|
// [ppv] --
|
|
// [iidClnt] --
|
|
// [pClientSiteInterface] --
|
|
//
|
|
//
|
|
// History: 11-29-95 kevinro Created
|
|
//
|
|
// Notes:
|
|
//
|
|
//----------------------------------------------------------------------------
|
|
STDMETHODIMP CStdIdentity::CreateServerWithHandler(REFCLSID rclsid, DWORD clsctx, void *pv,
|
|
REFCLSID rclsidHandler, IID iidSrv, void **ppv,
|
|
IID iidClnt, void *pClientSiteInterface)
|
|
{
|
|
return E_NOTIMPL;
|
|
}
|
|
|
|
|
|
|
|
#if DBG == 1
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Member: CStdIdentity::AssertValid
|
|
//
|
|
// Synopsis: Validates that the state of the object is consistent.
|
|
//
|
|
// History: 26-Jan-94 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
void CStdIdentity::AssertValid()
|
|
{
|
|
AssertSz(m_refs < 0x7fff, "Identity ref count unreasonable");
|
|
|
|
// ensure we have the controlling unknown
|
|
Assert(IsValidInterface(m_pUnkOuter)); // must be valid
|
|
|
|
// NOTE: don't carelessly AddRef/Release because of weak references
|
|
|
|
Assert((m_flags & ~(STDID_SERVER | STDID_CLIENT | STDID_STDMARSHAL |
|
|
STDID_HASEC | STDID_INDESTRUCTOR)) == 0);
|
|
|
|
Assert((m_pWRorECServer != NULL) ==
|
|
((m_flags & (STDID_HASEC)) != 0));
|
|
|
|
if (!IsEqualGUID(m_oid, OID_NULL) && StdMarshalNext())
|
|
{
|
|
Assert(!IsEqualGUID(m_clsidHandler, CLSID_NULL));
|
|
// CODEWORK: could get clsid again and check
|
|
}
|
|
|
|
if (IsEqualGUID(m_oid, OID_NULL))
|
|
Assert(IsEqualGUID(m_clsidHandler, CLSID_NULL));
|
|
else
|
|
{
|
|
IStdIdentity *pStdID;
|
|
Verify(LookupIDFromID(m_oid, FALSE /*fAddRef*/, &pStdID) == NOERROR);
|
|
Assert(pStdID == (IStdIdentity *)this);
|
|
// pStdID not addref'd
|
|
}
|
|
|
|
if (IsClient())
|
|
Assert(m_pUnkControl == m_pUnkOuter);
|
|
|
|
// must have RH tell identity when object goes away so we can NULL this
|
|
if (m_pUnkControl != NULL)
|
|
Assert(IsValidInterface(m_pUnkControl)); // must be valid
|
|
|
|
Assert(m_pMarshalNext != NULL);
|
|
if (m_pMarshalNext == PSTDMARSHAL)
|
|
Assert(StdMarshalNext());
|
|
|
|
if (StdMarshalNext())
|
|
{
|
|
if (m_pRH == NULL)
|
|
{
|
|
// haven't converted to real pointer yet
|
|
Assert(m_pMarshalNext == PSTDMARSHAL);
|
|
}
|
|
else if (m_pMarshalNext != PSTDMARSHAL)
|
|
{
|
|
// verify pointer we have is from RH
|
|
Assert(m_pMarshalNext == (IMarshal *)(CRemoteHdlr *)m_pRH);
|
|
}
|
|
}
|
|
|
|
// NOTE: if this cast is not correct, the asserts will fire wildly
|
|
// because the this pointer will be off by at least 4 bytes.
|
|
if (m_pRH)
|
|
((CRemoteHdlr *)m_pRH)->AssertValid();
|
|
}
|
|
#endif // DBG == 1
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Function: CreateStdIdentity
|
|
//
|
|
// Synopsis: Creates a new identity object, possibly aggregated.
|
|
// When not aggregated, same as CoGetStandardMarshal().
|
|
//
|
|
// Effects: The identity object is normaly the lead marshaler in a
|
|
// chain of marshalers. The pMarshal determines the next
|
|
// marshaler in the chain. The std marshaler is always
|
|
// retrievable with the GetStdRemMarshaler() method. If
|
|
// this is a new identity, we figure out the clsidHandler
|
|
// up front.
|
|
//
|
|
// Arguments: [pUnkOuter] -- The controlling unknown if aggregated
|
|
// [pUnkControl] -- Controlling unknown if not aggregated.
|
|
// These two cannot both be NULL. If they are both non-NULL,
|
|
// they must be the same.
|
|
// [pMarshal] - one of NULL, PSTDMARSHAL or real pMarshal.
|
|
// See above.
|
|
// [riid, ppv] - interface requested and place for pointer to
|
|
// that interface.
|
|
//
|
|
// Returns: S_OK - new identity created and if pUnkOuter specified,
|
|
// aggregated. If pUnkOuter == NULL, it was possible
|
|
// that another thread created the identity and that
|
|
// we simply reused it.
|
|
//
|
|
// E_NOINTERFACE - interface not supported; only returned
|
|
// when not aggregated and indicates that the identity
|
|
// object does not support the interface.
|
|
//
|
|
// E_OUTOFMEMORY -
|
|
//
|
|
// CO_E_OBJISREG - pUnkOuter was specified and another thread
|
|
// had already created the identity.
|
|
//
|
|
// BUGBUG - the identity GUID could not be created
|
|
//
|
|
// E_UNEXPECTED - pUnkOuter != NULL and riid != IID_IUnknown
|
|
//
|
|
// BUGBUG: curently we combine server side with id creation. Consider
|
|
// separting it for docfile. They can't just use CreateIdentityHandler
|
|
// always because it doesn't allocate an oid.
|
|
//
|
|
// History: 1-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
INTERNAL CreateStdIdentity(IUnknown *pUnkOuter, IUnknown *pUnkControl,
|
|
IMarshal *pMarshal, REFIID riid, void **ppv)
|
|
{
|
|
HRESULT hr;
|
|
// BUGBUG: validiate parameters more ???
|
|
|
|
// ensure initialized; checked by caller
|
|
Assert(IsApartmentInitialized());
|
|
|
|
*ppv = NULL;
|
|
|
|
if (pMarshal == NULL)
|
|
// we don't handle the NULL case yet (if we ever do, we would
|
|
// use a static null marshaler).
|
|
return E_INVALIDARG;
|
|
|
|
if (pUnkOuter == NULL)
|
|
{
|
|
// not aggregated;
|
|
if (pUnkControl == NULL)
|
|
return E_INVALIDARG;
|
|
}
|
|
else
|
|
{
|
|
// aggregated
|
|
if (!IsEqualGUID(riid, IID_IUnknown))
|
|
return E_UNEXPECTED;
|
|
|
|
Assert(pUnkControl == NULL || pUnkControl == pUnkOuter);
|
|
pUnkControl = pUnkOuter;
|
|
}
|
|
|
|
#if DBG == 1
|
|
// the caller should have a strong reference and so these tests
|
|
// should not disturb the object.
|
|
|
|
// addref/release pUnkControl; shouldn't go away (i.e.,
|
|
// should be other ref to it).
|
|
pUnkControl->AddRef();
|
|
Verify(pUnkControl->Release() != 0);
|
|
|
|
// verify that pUnkControl is in fact the controlling unknown
|
|
IUnknown *pUnkT;
|
|
Verify(pUnkControl->QueryInterface(IID_IUnknown,(void **)&pUnkT)==NOERROR);
|
|
Assert(pUnkControl == pUnkT);
|
|
Verify(pUnkT->Release() != 0);
|
|
|
|
if (pMarshal != PSTDMARSHAL)
|
|
{
|
|
// addref/release pMarshal; shouldn't go away (i.e.,
|
|
// should be other ref to it).
|
|
pMarshal->AddRef();
|
|
Verify(pMarshal->Release() != 0);
|
|
}
|
|
#endif
|
|
|
|
// create oid
|
|
OID oid;
|
|
CLSID clsidHandler;
|
|
|
|
#if defined(_CHICAGO_)
|
|
RPC_STATUS rpcStat = CoCreateAlmostGuid((UUID *)&oid);
|
|
#else
|
|
RPC_STATUS rpcStat = UuidCreate((UUID *)&oid);
|
|
#endif
|
|
|
|
// determine clsid
|
|
if (pMarshal == PSTDMARSHAL)
|
|
{
|
|
// if defined, get class from IStdMarshalInfo::GetClassForHandler;
|
|
// if not, use persistant class id; use CLSID_NULL if error.
|
|
IStdMarshalInfo FAR* pStdMarshalInfo;
|
|
if ((hr = pUnkControl->QueryInterface(IID_IStdMarshalInfo,
|
|
(LPVOID FAR*)&pStdMarshalInfo)) == NOERROR) {
|
|
// has standard marshal info; i.e., wants or needs to override
|
|
// default behavior of using persistant clsid.
|
|
hr = pStdMarshalInfo->GetClassForHandler(NULL, NULL,
|
|
&clsidHandler);
|
|
pStdMarshalInfo->Release();
|
|
|
|
// NOTE: we don't call this for each context; see note above.
|
|
}
|
|
#if DBG == 1
|
|
else {
|
|
# ifndef _CAIRO_
|
|
// BUGBUG: this assert far too annoying for Cairo right now [mikese]
|
|
// remove when we convert to MIDL 2.0 for ctypes.
|
|
|
|
AssertOutPtrFailed(pStdMarshalInfo);
|
|
# endif
|
|
}
|
|
#endif
|
|
|
|
// if queries or calls to get clsid resulted in error or
|
|
// handler NULL (compatibility with 16bit code), use
|
|
// CLSID_StdMarshal.
|
|
if (hr != NOERROR || IsEqualGUID(clsidHandler, CLSID_NULL))
|
|
clsidHandler = CLSID_StdMarshal;
|
|
}
|
|
else
|
|
{
|
|
AssertSz(FALSE, "pMarshal != PSTDMARSHAL case not tested");
|
|
|
|
// unmarshal class comes from pMarshal; retrieved at each
|
|
// marshal interface call.
|
|
clsidHandler = CLSID_NULL;
|
|
}
|
|
|
|
// create object
|
|
IStdIdentity *pStdID;
|
|
IUnknown *pUnkID;
|
|
pStdID = new CStdIdentity(STDID_SERVER, pUnkOuter, pUnkControl, oid,
|
|
clsidHandler, pMarshal, &pUnkID);
|
|
if (pStdID == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
// we now have two pointers to the object: pStdID which has no ref count
|
|
// (for the aggregation case this is necessary since it is an external
|
|
// interface); pUnkID which is the internal unknown and has the only ref
|
|
// count.
|
|
|
|
IStdIdentity *pStdIDExisting;
|
|
switch (SetObjectID(oid, pUnkControl, pStdID, &pStdIDExisting))
|
|
{
|
|
default:
|
|
Assert(FALSE);
|
|
pUnkID->Release();
|
|
return E_UNEXPECTED;
|
|
|
|
case E_OUTOFMEMORY:
|
|
Assert(pStdIDExisting == NULL);
|
|
pUnkID->Release();
|
|
return E_OUTOFMEMORY;
|
|
|
|
case CO_E_OBJISREG:
|
|
// another object got registered since we last looked; use it if
|
|
// we are not trying to aggregate this one.
|
|
|
|
pUnkID->Release();
|
|
if (pUnkOuter != NULL)
|
|
{
|
|
pStdIDExisting->Release();
|
|
return CO_E_OBJISREG;
|
|
}
|
|
|
|
// make like the success case.
|
|
pUnkID = pStdIDExisting; // this has the ref count
|
|
pStdID = pStdIDExisting; // no ref count
|
|
#if DBG == 1
|
|
pStdIDExisting = NULL;
|
|
#endif
|
|
// fall through
|
|
|
|
case S_OK:
|
|
Assert(pStdIDExisting == NULL);
|
|
|
|
((CStdIdentity *)pStdID)->AssertValid();
|
|
|
|
// NOTE: for the aggregated case, this will QI for IID_IUnknown
|
|
// which will re-retrive the internal unknown. Similarly,
|
|
// for IID_IStdIdentity, we will re-retrieve the pStdID. This
|
|
// method is less code and nearly as fast.
|
|
hr = pUnkID->QueryInterface(riid, ppv);
|
|
pUnkID->Release();
|
|
return hr;
|
|
}
|
|
}
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Function: CreateIdentityHandler, private
|
|
//
|
|
// Synopsis: Creates a client side identity object (one which is
|
|
// initialized by the first unmarshal).
|
|
//
|
|
// Arguments: [pUnkOuter] -- controlling unknown if aggregated
|
|
// [pMarshal] -- same as above.
|
|
// [riid, ppv] - interface requested and place for pointer to
|
|
// that interface.
|
|
//
|
|
// History: 16-Dec-93 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
INTERNAL CreateIdentityHandler(IUnknown *pUnkOuter, IMarshal *pMarshal,
|
|
REFIID riid, void **ppv)
|
|
{
|
|
// BUGBUG: validiate parameters more ???
|
|
|
|
// ensure initialized; checked by caller
|
|
Assert(IsApartmentInitialized());
|
|
|
|
*ppv = NULL;
|
|
|
|
if (pMarshal == NULL)
|
|
// we don't handle the NULL case yet (if we ever do, we would
|
|
// use a static null marshaler).
|
|
return E_INVALIDARG;
|
|
|
|
if (pUnkOuter != NULL && !IsEqualGUID(riid, IID_IUnknown))
|
|
return E_UNEXPECTED;
|
|
|
|
#if DBG == 1
|
|
if (pUnkOuter != NULL)
|
|
{
|
|
// addref/release pUnkOuter; shouldn't go away (i.e.,
|
|
// should be other ref to it).
|
|
pUnkOuter->AddRef();
|
|
Verify(pUnkOuter->Release() != 0);
|
|
|
|
// verify that pUnkOuter is in fact the controlling unknown
|
|
IUnknown *pUnkT;
|
|
Verify(pUnkOuter->QueryInterface(IID_IUnknown,(void**)&pUnkT)==NOERROR);
|
|
Assert(pUnkOuter == pUnkT);
|
|
Verify(pUnkT->Release() != 0);
|
|
}
|
|
|
|
if (pMarshal != PSTDMARSHAL)
|
|
{
|
|
AssertSz(FALSE, "pMarshal != PSTDMARSHAL case not tested");
|
|
|
|
// addref/release pMarshal; shouldn't go away (i.e.,
|
|
// should be other ref to it).
|
|
pMarshal->AddRef();
|
|
Verify(pMarshal->Release() != 0);
|
|
}
|
|
#endif
|
|
|
|
// create object with no identity; will get on first unmarshal
|
|
IStdIdentity *pStdID;
|
|
IUnknown *pUnkID;
|
|
pStdID = new CStdIdentity(STDID_CLIENT, pUnkOuter, NULL, OID_NULL,
|
|
CLSID_NULL, pMarshal, &pUnkID);
|
|
if (pStdID == NULL)
|
|
return E_OUTOFMEMORY;
|
|
|
|
((CStdIdentity *)pStdID)->AssertValid();
|
|
|
|
// NOTE: for the aggregated case, this will QI for IID_IUnknown
|
|
// which will re-retrive the internal unknown. Similarly,
|
|
// for IID_IStdIdentity, we will re-retrieve the pStdID. This
|
|
// method is less code and nearly as fast.
|
|
HRESULT hr = pUnkID->QueryInterface(riid, ppv);
|
|
pUnkID->Release();
|
|
|
|
CALLHOOKOBJECTCREATE(hr,CLSID_NULL,riid,(IUnknown **)ppv);
|
|
return hr;
|
|
}
|
|
|
|
|
|
|
|
//+-------------------------------------------------------------------
|
|
//
|
|
// Function: ScmCreateObjectInstance, private
|
|
//
|
|
// Synopsis: Calls the SCM to get a marshaled interfacer pointer to
|
|
// a new instance of the clsid specified.
|
|
//
|
|
// Arguments: [rclsid] -- Clsid of the server
|
|
// [dwContext] -- the context in class object should be
|
|
// located (LOCAL, REMOTE).
|
|
// [pv] -- extra info for context; LATER: machine name, etc.
|
|
// [ppIFD] -- the returned interface data
|
|
//
|
|
// Returns: S_OK
|
|
//
|
|
// History: 06-Jan-94 CraigWi Created.
|
|
//
|
|
//--------------------------------------------------------------------
|
|
|
|
INTERNAL ScmCreateObjectInstance(
|
|
REFCLSID rclsid,
|
|
DWORD dwContext,
|
|
void * pv,
|
|
InterfaceData **ppIFD)
|
|
{
|
|
// Dll ignored here since we are just doing this to get
|
|
// the remote handler.
|
|
WCHAR *pwszDllPath = NULL;
|
|
DWORD dwDllType = FreeThreading ? FREE_THREADED : APT_THREADED;
|
|
|
|
*ppIFD = NULL; // this call requires this
|
|
|
|
#ifdef DCOM
|
|
HRESULT hrinterface;
|
|
HRESULT hr = gResolver.CreateInstance( NULL, rclsid, dwContext, 1,
|
|
(IID *)&IID_IUnknown, (MInterfacePointer **)ppIFD, &hrinterface,
|
|
&dwDllType, &pwszDllPath, NULL, NULL, NULL );
|
|
#else
|
|
// The first three NULLs (pwszFrom, pstgFrom, pwszNew) trigger a
|
|
// simple creation.
|
|
HRESULT hr = gResolver.CreateObject(rclsid, dwContext, 0,
|
|
NULL, NULL, NULL, ppIFD, &dwDllType, &pwszDllPath, NULL);
|
|
#endif
|
|
|
|
if (pwszDllPath != NULL)
|
|
{
|
|
MIDL_user_free(pwszDllPath);
|
|
}
|
|
|
|
return hr;
|
|
}
|