NT4/private/ole32/com/remote/coapi.cxx

1319 lines
41 KiB
C++
Raw Permalink Normal View History

2001-01-01 00:00:00 +01:00
//+-------------------------------------------------------------------
//
// File: coapi.cxx
//
// Contents: Public COM remote subsystem APIs
//
// Classes: CStaticMarshaler (private)
//
// Functions: CoGetStandardMarshal - returns IMarshal for given object
// CoGetMarshalSizeMax - returns max size buffer needed
// CoMarshalInterface - marshals an interface
// CoUnmarshalInterface - unmarshals an interface
// CoReleaseMarshalData - releases data from marshaled iface
// CoLockObjectExternal - keeps object alive
// CoDisconnectObject - kills sessions held by remote clients
//
// History: 23-Nov-92 Rickhi
// 11-Dec-93 CraigWi Switched to identity object
// 05-Jul-94 BruceMa Check for end of stream
//
//--------------------------------------------------------------------
#include <ole2int.h>
#include <olerem.h>
#include <iface.h>
// function prototypes
INTERNAL_(IMarshal *) FindOrCreateStdMarshal(IUnknown *pUnk, BOOL fCreate);
INTERNAL_(IMarshal *) GetStaticMarshaler(void);
INTERNAL_(void) RewriteHeader(IStream *pStm, void *pv, ULONG cb, ULARGE_INTEGER ulSeekStart);
//+-------------------------------------------------------------------
//
// Function: CoGetStandardMarshal
//
// Synopsis: Returns an instance of the standard IMarshal for the
// specifed object. See comment about aggregated id objects
// in FindOrCreateStdMarshal().
//
// Algorithm: lookup or create a remote hdlr for the object.
//
// History: 23-Nov-92 Rickhi Created
// 11-Dec-93 CraigWi Switched to identity object
//
//--------------------------------------------------------------------
STDAPI CoGetStandardMarshal(REFIID riid, IUnknown *pUnk,
DWORD dwDestContext, void *pvDestContext,
DWORD mshlflags, IMarshal **ppMarshal)
{
OLETRACEIN((API_CoGetStandardMarshal,
PARAMFMT("riid= %I, pUnk= %p, dwDestContext= %x, pvDestContext= %p, mshlflags= %x, ppMarshal= %p"),
&riid, pUnk, dwDestContext, pvDestContext, mshlflags, ppMarshal));
TRACECALL(TRACE_MARSHAL, "CoGetStandardMarshal");
HRESULT sc = S_OK;
IMarshal *pIM;
if (!IsApartmentInitialized())
{
sc = CO_E_NOTINITIALIZED;
goto errRtn;
}
CALLHOOKOBJECT(S_OK,CLSID_NULL,riid,&pUnk);
if (pUnk == NULL)
{
// this is the unmarshal side
// sc = CreateIdentityHandler(pUnk, PSTDMARSHAL, IID_IMarshal,
// (void**) &pIM);
pIM = GetStaticMarshaler();
}
else
{
// this is the marshal side
pIM = FindOrCreateStdMarshal(pUnk, TRUE);
}
*ppMarshal = pIM; // fill in the return parameters
if (SUCCEEDED(sc) && !pIM)
{
sc = E_OUTOFMEMORY;
}
CairoleDebugOut((DEB_ITRACE, "CoGetStandardMarshal: pUnk=%x pIM=%x sc=%x\n",
pUnk, *ppMarshal, sc));
errRtn:
OLETRACEOUT((API_CoGetStandardMarshal, sc));
return sc;
}
//+-------------------------------------------------------------------
//
// Function: CoGetMarshalSizeMax
//
// synopsis: returns the max size needed to marshal the specified
// interface.
//
// History: 23-Nov-92 Rickhi Created
// 11-Dec-93 CraigWi Switched to static marshaler
//
//--------------------------------------------------------------------
STDAPI CoGetMarshalSizeMax(ULONG *pulSize, REFIID riid, IUnknown *pUnk,
DWORD dwDestCtx, void *pvDestCtx,
DWORD mshlflags)
{
OLETRACEIN((API_CoGetMarshalSizeMax,
PARAMFMT("pulSize= %p, riid= %I, pUnk= %p, dwDestCtx= %x, pvDestCtx= %p, mshlflags= %x"),
pulSize, &riid, pUnk, dwDestCtx, pvDestCtx, mshlflags));
TRACECALL(TRACE_MARSHAL, "CoGetMarshalSizeMax");
Win4Assert(MARSHALINTERFACE_MIN >= sizeof(SMiApiDataHdr) + sizeof(CLSID));
HRESULT sc = S_OK;
IMarshal *pIM = NULL;
CALLHOOKOBJECT(S_OK,CLSID_NULL,riid,&pUnk);
if (!IsApartmentInitialized())
{
sc = CO_E_NOTINITIALIZED;
goto errRtn;
}
// find the IMarshal interface, or create a RH for the object
if (FAILED(pUnk->QueryInterface(IID_IMarshal, (void **)&pIM)))
{
// uses standard marshalling; cheaply get marshaler
pIM = GetStaticMarshaler();
Win4Assert(pIM);
}
if (pIM)
{
sc = pIM->GetMarshalSizeMax(riid, (void *)pUnk, dwDestCtx,
pvDestCtx, mshlflags, pulSize);
// BUGBUG: may need to release this specialy for custom marshalers
pIM->Release();
// add in the size of the stuff CoMarshalInterface will write;
// may not, in fact, write clsid, but this conservative.
(*pulSize) += sizeof(SMiApiDataHdr) + sizeof(CLSID);
}
else
{
sc = E_UNEXPECTED;
}
CairoleDebugOut((DEB_ITRACE, "CoGetMarshalSizeMax: pUnk=%x size=%x dwDest=%x pvDest=%x flags=%x sc=%x\n",
pUnk, *pulSize, dwDestCtx, pvDestCtx, mshlflags, sc));
errRtn:
OLETRACEOUT((API_CoGetMarshalSizeMax, sc));
return sc;
}
//+-------------------------------------------------------------------
//
// Function: CoMarshalInterface, public
//
// Synopsis: fill stream with marshal info for pUnk/iid;
//
// Algorithm:
//
// History: 23-Nov-92 Rickhi
// 11-Dec-93 CraigWi Switched to identity object and
// new marshaling format
//
//--------------------------------------------------------------------
STDAPI CoMarshalInterface(IStream *pStm,
REFIID riid,
IUnknown *pUnk,
DWORD dwDestCtx,
void *pvDestCtx,
DWORD mshlflags)
{
OLETRACEIN((API_CoMarshalInterface,
PARAMFMT("pStm= %p, riid= %I, pUnk= %p, dwDestCtx= %x, pvDestCtx= %p, mshlflags= %x"),
pStm, &riid, pUnk, dwDestCtx, pvDestCtx, mshlflags));
TRACECALL(TRACE_MARSHAL, "CoMarshalInterface");
HRESULT sc;
IMarshal *pIM = NULL;
if (!IsApartmentInitialized())
{
sc = CO_E_NOTINITIALIZED;
goto errRtn;
}
// Some parameter checking
if (pUnk == NULL)
{
sc = E_OUTOFMEMORY;
goto errRtn;
}
CALLHOOKOBJECT(S_OK,CLSID_NULL,riid,(IUnknown **)&pUnk);
CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IStream,(IUnknown **)&pStm);
// Make sure object supports the requested interface. This has the
// side effect that if this is a request for a marshal interface and
// the object is a proxy for which we have never gotten the interface,
// we will get it now and the marshal will work.
IUnknown *punkVerifyIf;
if (pUnk->QueryInterface(riid, (void **) &punkVerifyIf) != NOERROR)
{
sc = E_NOINTERFACE;
goto errRtn;
}
// find the IMarshal interface, or create a RH for the object
if (FAILED(pUnk->QueryInterface(IID_IMarshal, (void **)&pIM)))
{
// returns NULL if failed
pIM = FindOrCreateStdMarshal(pUnk, TRUE);
}
if (pIM)
{
// setup the marshal info structure
SMiApiDataHdr ifp;
CLSID clsid;
ifp.dwflags = mshlflags & MIAPIFLAGS_TABLE;
sc = pIM->GetUnmarshalClass(riid, pUnk, dwDestCtx, pvDestCtx,
mshlflags, &clsid);
if (IsEqualGUID(clsid, CLSID_IdentityUnmarshal))
{
ifp.dwflags |= MIAPIFLAGS_STDIDENTITY;
}
else if (IsEqualGUID(clsid, CLSID_InProcFreeMarshaler))
{
ifp.dwflags |= MIAPIFLAGS_IPFM;
}
if (SUCCEEDED(sc))
{
// write the marshal info into the stream
sc = pStm->Write(&ifp, sizeof(ifp), NULL);
}
if (SUCCEEDED(sc)
&& (ifp.dwflags & (MIAPIFLAGS_STDIDENTITY | MIAPIFLAGS_IPFM)) == 0)
{
// write non-standard unmarshaler clsid
sc = pStm->Write(&clsid, sizeof(clsid), NULL);
}
if (SUCCEEDED(sc))
{
sc = pIM->MarshalInterface(pStm, riid, pUnk, dwDestCtx,
pvDestCtx, mshlflags);
}
pIM->Release();
}
else
{
sc = E_OUTOFMEMORY;
}
// Release the interface we used to verify that the interface was supported
// here because if we succeeded we have multiple AddRefs to the interface
// and therefore the object will not go away.
punkVerifyIf->Release();
CairoleDebugOut((DEB_ITRACE, "CoMarshalInterface: pUnk=%x pIM=%x dwDest=%x pvDest=%x flags=%x sc=%x\n",
pUnk, pIM, dwDestCtx, pvDestCtx, mshlflags, sc));
errRtn:
OLETRACEOUT((API_CoMarshalInterface, sc));
return sc;
}
//+-------------------------------------------------------------------
//
// Function: CoUnMarshalInterface, public
//
// Algorithm:
//
// Notes: when a controlling unknown is supplied, it is assumed that
// the HANDLER for the class has done a CreateInstance and wants
// to aggregate just the remote handler, ie. we dont want to
// instantiate a new class handler (the default unmarshalling
// behaviour).
//
// History: 23-Nov-92 Rickhi
// 11-Dec-93 CraigWi Switched to static marshaler and
// new marshaling format
//
//--------------------------------------------------------------------
STDAPI CoUnmarshalInterfaceEx(IStream *pStm, REFIID riid, void **ppv, BOOL f);
STDAPI CoUnmarshalInterface(IStream *pStm,
REFIID riid,
void **ppv)
{
OLETRACEIN((API_CoUnmarshalInterface, PARAMFMT("pStm= %p, riid= %I, ppv= %p"), pStm, &riid, ppv));
HRESULT hr;
hr = CoUnmarshalInterfaceEx(pStm, riid, ppv, TRUE /*fNormalDoesRelease*/);
OLETRACEOUT((API_CoUnmarshalInterface, hr));
return hr;
}
// This variation allows control over whether the normal marshal case does
// the release marshal data. This is useful for nested marshaled pointers
// which need to be released once when the outer marshaling is released.
STDAPI CoUnmarshalInterfaceEx(IStream *pStm,
REFIID riid,
void **ppv,
BOOL fNormalDoesRelease)
{
TRACECALL(TRACE_MARSHAL, "CoUnmarshalInterface");
CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IStream,(IUnknown **)&pStm);
if (!IsApartmentInitialized())
return CO_E_NOTINITIALIZED;
HRESULT sc;
IMarshal *pIM = NULL;
*ppv = NULL;
SMiApiDataHdr ifp;
CLSID clsid;
sc = StRead(pStm, &ifp, sizeof(ifp));
if (SUCCEEDED(sc) && (ifp.dwflags & MIAPIFLAGS_RELEASED) != 0)
{
// already released, this is an error on the caller's part
sc = E_UNEXPECTED;
}
if (SUCCEEDED(sc)
&& (ifp.dwflags & (MIAPIFLAGS_STDIDENTITY | MIAPIFLAGS_IPFM)) == 0)
{
sc = StRead(pStm, &clsid, sizeof(clsid));
}
// deal with extension
if (SUCCEEDED(sc) && (ifp.dwflags & MIAPIFLAGS_EXTENSION) != 0)
sc = SkipMarshalExtension(pStm);
if (SUCCEEDED(sc))
{
// create an instance of the specified class and ask it to do
// the unmarshalling. note that since standard marshalling is so
// common, we cook up an instance at init time and just always
// use that guy to do the unmarshalling.
if ((ifp.dwflags & MIAPIFLAGS_STDIDENTITY) != 0)
{
// uses standard marshalling; cheaply get unmarshaler
pIM = GetStaticMarshaler();
Win4Assert(pIM);
}
else if ((ifp.dwflags & MIAPIFLAGS_IPFM) != 0)
{
// Get the in process standard free marshaler.
sc = GetInProcFreeMarshaler(&pIM);
}
else
{
// uses custom marshalling, create an instance
sc = CoCreateInstance(clsid, NULL, CLSCTX_INPROC,
IID_IMarshal, (void **)&pIM);
}
if (SUCCEEDED(sc) && pIM)
{
ULARGE_INTEGER ulSeekStart;
LARGE_INTEGER libMove;
if ((ifp.dwflags & MIAPIFLAGS_TABLE) == MSHLFLAGS_NORMAL)
{
// save current seek pointer for ReleaseMarshalData
LISet32(libMove, 0x00000000);
pStm->Seek(libMove, STREAM_SEEK_CUR, &ulSeekStart);
}
// unmarshal the interface
sc = pIM->UnmarshalInterface(pStm, riid, ppv);
// release the marshal data if we are supposed to.
// ignore errors because they cant affect that fact that
// we already successfully unmarshalled the interface.
if ((ifp.dwflags & MIAPIFLAGS_TABLE) == MSHLFLAGS_NORMAL &&
fNormalDoesRelease)
{
ULARGE_INTEGER ulSeekEnd;
pStm->Seek(libMove, STREAM_SEEK_CUR, &ulSeekEnd);
libMove.LowPart = ulSeekStart.LowPart;
libMove.HighPart = ulSeekStart.HighPart;
if (SUCCEEDED(pStm->Seek(libMove, STREAM_SEEK_SET, &ulSeekStart)))
{
pIM->ReleaseMarshalData(pStm);
}
libMove.LowPart = ulSeekEnd.LowPart;
libMove.HighPart = ulSeekEnd.HighPart;
pStm->Seek(libMove, STREAM_SEEK_SET, NULL);
}
}
}
if (pIM)
pIM->Release();
CairoleDebugOut((DEB_ITRACE, "CoUnmarshalInterface: pUnk=%x sc=%x\n",
*ppv, sc));
return sc;
}
//+-------------------------------------------------------------------
//
// Function: CoReleaseMarshalData, public
//
// Synopsis: release the reference created by CoMarshalInterface
//
// Algorithm:
//
// History: 23-Nov-92 Rickhi
// 11-Dec-93 CraigWi Switched to static marshaler and
// new marshaling format
//
//--------------------------------------------------------------------
STDAPI CoReleaseMarshalData(IStream *pStm)
{
OLETRACEIN((API_CoReleaseMarshalData, PARAMFMT("pStm= %p"), pStm));
TRACECALL(TRACE_MARSHAL, "CoReleaseMarshalData");
CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IStream,(IUnknown **)&pStm);
HRESULT sc;
IMarshal *pIM = NULL;
SMiApiDataHdr ifp;
CLSID clsid;
ULARGE_INTEGER ulSeekStart;
LARGE_INTEGER libMove;
if (!IsApartmentInitialized())
{
sc = CO_E_NOTINITIALIZED;
goto errRtn;
}
// save the current stream seek pointer
LISet32(libMove, 0x00000000);
sc = pStm->Seek(libMove, STREAM_SEEK_CUR, &ulSeekStart);
if (SUCCEEDED(sc))
sc = StRead(pStm, &ifp, sizeof(ifp));
// ensure this marshalled data has not already been released.
if (SUCCEEDED(sc) && (ifp.dwflags & MIAPIFLAGS_RELEASED) != 0)
{
// already released, this is an error. Stream in undefined
// position on errors.
sc = E_UNEXPECTED;
}
if (SUCCEEDED(sc)
&& ((ifp.dwflags & (MIAPIFLAGS_STDIDENTITY | MIAPIFLAGS_IPFM)) == 0))
sc = StRead(pStm, &clsid, sizeof(clsid));
// deal with extension
if (SUCCEEDED(sc) && (ifp.dwflags & MIAPIFLAGS_EXTENSION) != 0)
sc = SkipMarshalExtension(pStm);
if (SUCCEEDED(sc))
{
// create an instance of the specified class and ask it to do
// the unmarshalling. note that since standard marshalling is so
// common, we cook up an instance at init time and just always
// use that guy to do the unmarshalling.
if ((ifp.dwflags & MIAPIFLAGS_STDIDENTITY) != 0)
{
// uses standard marshalling; cheaply get unmarshaler
pIM = GetStaticMarshaler();
Win4Assert(pIM); // cant fail!!!
}
else if ((ifp.dwflags & MIAPIFLAGS_IPFM) != 0)
{
// Uses the free threaded marshaler. So we get it directly.
// This really can only fail with out of memory.
IUnknown *punk;
// Get the IUnknown for the free threaded marshaler
sc = CoCreateFreeThreadedMarshaler(NULL, &punk);
if (SUCCEEDED(sc))
{
// Get the IMarshal interface
sc = punk->QueryInterface(IID_IMarshal, (void **) &pIM);
// We release this no matter what since we no longer need it.
punk->Release();
}
}
else
{
// uses custom marshalling, create an instance
sc = CoCreateInstance(clsid, NULL, CLSCTX_INPROC,
IID_IMarshal, (void **)&pIM);
}
if (SUCCEEDED(sc))
{
sc = pIM->ReleaseMarshalData(pStm);
if (pIM)
pIM->Release();
}
}
if (SUCCEEDED(sc))
{
// if overall success...
// to ensure we dont allow ReleaseMarshalData multiple times we
// mark a bit in the stream to indicate we've already Released it.
// UnmarshalInterface will return an error if this bit is set.
ifp.dwflags |= MIAPIFLAGS_RELEASED;
RewriteHeader(pStm, &ifp, sizeof(ifp), ulSeekStart);
}
CairoleDebugOut((DEB_ITRACE, "CoReleaseMarshalData pIM=%x sc=%x.\n",
pIM, sc));
errRtn:
OLETRACEOUT((API_CoReleaseMarshalData, sc));
return sc;
}
//+-------------------------------------------------------------------
//
// Function: CoLockObjectExternal
//
// synopsis: adds/revokes a strong reference count to/from the
// identity for the given object.
//
// parameters: [punkObject] - IUnknown of the object
// [fLock] - lock/unlock the object
// [fLastUR] - last unlock releases.
//
// History: 23-Nov-92 Rickhi Created
// 11-Dec-93 CraigWi Switched to identity object
//
//--------------------------------------------------------------------
STDAPI CoLockObjectExternal(IUnknown *punkObject, BOOL fLock, BOOL fLastUR)
{
OLETRACEIN((API_CoLockObjectExternal, PARAMFMT("punkObject= %p, fLock= %B, fLastUR= %B"),
punkObject, fLock, fLastUR));
TRACECALL(TRACE_MARSHAL, "CoLockObjectExternal");
// REF COUNTING: inc or dec external ref count
HRESULT sc = E_INVALIDARG;
IStdIdentity *pStdID;
if (!IsValidInterface(punkObject))
goto errRtn;
CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IUnknown,(IUnknown **)&punkObject);
switch (sc = LookupIDFromUnk(punkObject, fLock, &pStdID))
{
case S_OK:
if (pStdID->GetServer(FALSE) == NULL)
{
// attempt to lock handler, return error!
// BUGBUG: debug printouts
sc = E_UNEXPECTED;
}
else if (fLock)
sc = pStdID->AddConnection(EXTCONN_STRONG, 0);
else
sc = pStdID->ReleaseConnection(EXTCONN_STRONG, 0, fLastUR);
pStdID->Release();
break;
case CO_E_OBJNOTREG:
// unlock when not registered; 16bit code returned NOERROR;
// disconnected handler goes to S_OK case above.
sc = S_OK;
break;
case E_OUTOFMEMORY:
break;
default:
sc = E_UNEXPECTED;
break;
}
errRtn:
CairoleDebugOut((DEB_ITRACE, "CoLockObjectExternal pStdID=%x fLock=%x sc=%x.\n",
pStdID, fLock, sc));
OLETRACEOUT((API_CoLockObjectExternal, sc));
return sc;
}
//+-------------------------------------------------------------------
//
// Function: CoDisconnectObject
//
// synopsis: disconnects all clients of an object by marking their
// connections as terminted abnormaly.
//
// History: 04-Oct-93 Rickhi Created
// 11-Dec-93 CraigWi Switched to identity object
//
//--------------------------------------------------------------------
STDAPI CoDisconnectObject(IUnknown *punkObject, DWORD dwReserved)
{
OLETRACEIN((API_CoDisconnectObject, PARAMFMT("punkObject= %p, dwReserved= %x"), punkObject, dwReserved));
TRACECALL(TRACE_MARSHAL, "CoDisconnectObject");
CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IUnknown,(IUnknown **)&punkObject);
HRESULT sc = S_OK;
IMarshal *pIM = NULL;
sc = punkObject->QueryInterface(IID_IMarshal, (void **)&pIM);
if (FAILED(sc))
{
// object does not support IMarshal directly. Find its standard
// marshaler if there is one, otherwise return an error.
pIM = FindOrCreateStdMarshal(punkObject, FALSE);
}
if (pIM)
{
sc = pIM->DisconnectObject(dwReserved);
pIM->Release();
}
else
{
// couldn't get std marshal; must be disconnected already
sc = NOERROR;
}
CairoleDebugOut((DEB_ITRACE, "CoDisconnectObject pIM=%x sc=%x.\n",
pIM, sc));
OLETRACEOUT((API_CoDisconnectObject, sc));
return sc;
}
//+-------------------------------------------------------------------
//
// Function: CoIsHandlerConnected
//
// Synopsis: Returns whether or not handler is connected to remote
//
// Algorithm: QueryInterface to IProxyManager. If this is supported,
// then this is a handler. We ask the handler
// for its opinion otherwise we simply return TRUE.
//
// History: 04-Oct-93 Rickhi Created
//
// Notes: The answer of this routine may be wrong by the time
// the routine returns. This is correct behavior as
// this routine is primilary to cleanup state associated
// with connections.
//
//--------------------------------------------------------------------
STDAPI_(BOOL) CoIsHandlerConnected(LPUNKNOWN pUnk)
{
OLETRACEIN((API_CoIsHandlerConnected, PARAMFMT("pUnk= %p"), pUnk));
IProxyManager *pPM;
// Assume it is connected
BOOL fResult = TRUE;
CALLHOOKOBJECT(S_OK,CLSID_NULL,IID_IUnknown,(IUnknown **)&pUnk);
// Handler should be support IProxyManager
if (SUCCEEDED(pUnk->QueryInterface(IID_IProxyManager, (void **) &pPM)))
{
// We have something that thinks its is an Ole handler so we ask
fResult = pPM->IsConnected();
// Release the interface we used
pPM->Release();
}
OLETRACEOUTEX((API_CoIsHandlerConnected, RETURNFMT("%B"), fResult));
return fResult;
}
//+-------------------------------------------------------------------
//
// Function: FindOrCreateStdMarshal, private
//
// Synopsis: looks up or creates the std identity for the object
// and returns the IMarshal aspect of it. It is assumed
// that the ID object is not aggregated to the server
// (BUGBUG: can we assert that???) because we need to QI
// for IMarshal. When the id object is aggregated, the
// caller is supposed to use IStdIdentity::GetStdRemMarshal()
// instead.
//
// Arguments: [pUnk] -- The object in question; not necessaryily the
// controlling unknown.
// [fCreate] -- TRUE -> creates identity object if it does
// not exist.
//
// Returns: NULL if out of memory; pMarshal otherwise
//
// History: 11-Dec-93 CraigWi Created.
//
//--------------------------------------------------------------------
INTERNAL_(IMarshal *)FindOrCreateStdMarshal(IUnknown *pUnk, BOOL fCreate)
{
IStdIdentity *pStdID;
if (LookupIDFromUnk(pUnk, fCreate, &pStdID) != NOERROR)
// only possible return in this case is E_OUTOFMEMORY.
return NULL;
HRESULT hr;
IMarshal *pMarshal;
hr = pStdID->QueryInterface(IID_IMarshal, (void **)&pMarshal);
pStdID->Release();
AssertSz(hr == NOERROR, "What, no IMarshal on an identity object?");
CALLHOOKOBJECTCREATE(hr,CLSID_NULL,IID_IMarshal,(IUnknown **)&pMarshal);
return (hr == NOERROR) ? pMarshal : NULL;
}
//+----------------------------------------------------------------
//
// Class: CStaticMarshaler, private
//
// Purpose: Used for three rather disjoint purposes:
// 1. unmarshaling normal identity packet
// 2. release marshal data of same and table packets
// 3. get marshal size max
//
// This class exists solely to make the code in the Co*
// APIs cleaner. It is not integrated into the real
// identity object itself to avoid confusing the two
// roles (initial unmarshaler and the real identity).
//
// Interface: IMarshal::GetMarshalSizeMax, IMarshal::UnmarshalInterface
// and IMarshal::ReleaseMarshal; other methods of IMarshal
// return E_UNEXPECTED.
//
// History: 11-Dec-93 CraigWi Created.
//
// Note: There is only one instance of this class (for speed) and
// thus many short cuts are taken.
//
//-----------------------------------------------------------------
class CStaticMarshaler : public IMarshal
{
public:
STDMETHOD(QueryInterface) (REFIID riid, LPVOID *ppvObj);
STDMETHOD_(ULONG,AddRef) (void);
STDMETHOD_(ULONG,Release) (void);
// 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);
private:
#if DBG == 1
DWORD m_refs; // for the AddRef/Release return value
#endif
} sg_StaticMarshaler;
//+-------------------------------------------------------------------
//
// Member: CStaticMarshaler::QueryInterface, public
//
// Synopsis: shouldn't be called.
//
// History: 11-Dec-93 CraigWi Created.
//
//--------------------------------------------------------------------
STDMETHODIMP CStaticMarshaler::QueryInterface(REFIID riid, VOID **ppvObj)
{
AssertSz(FALSE, "This QI should not be called");
*ppvObj = NULL;
return E_UNEXPECTED;
}
//+-------------------------------------------------------------------
//
// Member: CStaticMarshaler::AddRef, Release, public
//
// Synopsis: maintains ref count in debug version only.
//
// History: 11-Dec-93 CraigWi Created.
//
//--------------------------------------------------------------------
STDMETHODIMP_(ULONG) CStaticMarshaler::AddRef(void)
{
#if DBG == 1
return ++m_refs;
#else
return 0;
#endif
}
STDMETHODIMP_(ULONG) CStaticMarshaler::Release(void)
{
#if DBG == 1
return --m_refs;
#else
return 0;
#endif
}
//+-------------------------------------------------------------------
//
// Member: CStaticMarshaler::GetUnmarshalClass, public
//
// Synopsis: shouldn't be called.
//
// History: 11-Dec-93 CraigWi Created.
//
//--------------------------------------------------------------------
STDMETHODIMP CStaticMarshaler::GetUnmarshalClass(REFIID riid, LPVOID pv,
DWORD dwDestContext, LPVOID pvDestContext,
DWORD mshlflags, LPCLSID pCid)
{
AssertSz(FALSE, "This GetUnmarshalClass should not be called");
return E_UNEXPECTED;
}
//+-------------------------------------------------------------------
//
// Member: CStaticMarshaler::GetMarshalSizeMax, public
//
// Synopsis: Quickly gets an upper bound on the amount of data for
// a standard marshal (no app data).
//
// History: 11-Dec-93 CraigWi Created.
//
//--------------------------------------------------------------------
STDMETHODIMP CStaticMarshaler::GetMarshalSizeMax(REFIID riid, LPVOID pv,
DWORD dwDestContext, LPVOID pvDestContext,
DWORD mshlflags, LPDWORD pSize)
{
// object doesn't have own imarshal and thus it will use all
// standard marshaling (coapi, id, remhdlr, channel).
// size is: sizeof id, sizeof rh, upper bound on size of channel
// don't want to be fancy about this since we want this to be fast;
// include handler CLSID since we don't want to check if it is needed.
*pSize = sizeof(SIdentityDataHdr) + sizeof(CLSID) +
sizeof(SHandlerDataHdr) + /* CLSID_RpcChannelBuffer + */
sizeof(SChannelDataHdr)
+ 1024;
//
// BUGBUG: (RichardW, 14 Oct 94, for RickHi, bug # 25942)
//
// Someone trashed this calculation, adding this random 512 byte pad
// on the end. RickHi will apply the correct fix, but for now, for
// cairo builds, I have bumped it to 1024.
//
// CODEWORK: later when channel is indexable from the dest context,
// we use that for the last component of the size. This is also
// important to do since we don't really know an upper bound on the
// channel data size.
// When table marshaling differs from normal marshaling, this
// routine will have to encode how the size gets calculated.
// In both cases some code will be shared (procedures) with std id.
return NOERROR;
}
//+-------------------------------------------------------------------
//
// Member: CStaticMarshaler::MarshalInterface, public
//
// Synopsis: shouldn't be called.
//
// History: 11-Dec-93 CraigWi Created.
//
//--------------------------------------------------------------------
STDMETHODIMP CStaticMarshaler::MarshalInterface(LPSTREAM pStm,
REFIID riid, LPVOID pv, DWORD dwDestContext, LPVOID pvDestContext,
DWORD mshlflags)
{
AssertSz(FALSE, "This MarshalInterface should not be called");
return E_UNEXPECTED;
}
//+-------------------------------------------------------------------
//
// Function: ReadIdentityHeader, private
//
// Synopsis: Reads the identity data header and the clsid;
// if fTransparent, skips back to the beginning;
// else, skips over extention if present.
//
// History: 14-Dec-93 CraigWi Created.
//
//--------------------------------------------------------------------
INTERNAL ReadIdentityHeader(IStream *pStm, SIdentityDataHdr *pidh,
CLSID *pclsidHandler, BOOL fTransparent)
{
HRESULT hr;
ULARGE_INTEGER ulSeekStart;
LARGE_INTEGER libMove;
if (fTransparent)
{
// save current position so that we can set it back here
LISet32(libMove, 0x00000000);
hr = pStm->Seek(libMove, STREAM_SEEK_CUR, &ulSeekStart);
if (FAILED(hr))
return hr;
}
hr = StRead(pStm, pidh, sizeof(*pidh));
if (FAILED(hr))
return hr;
if ((pidh->dwflags & IDENFLAGS_STDMARSHAL) != 0)
{
// std marshal case
*pclsidHandler = CLSID_StdMarshal;
}
else
{
hr = StRead(pStm, pclsidHandler, sizeof(*pclsidHandler));
if (FAILED(hr))
return hr;
}
if (fTransparent)
{
// skip back to the beginning of what was just read
libMove.LowPart = ulSeekStart.LowPart;
libMove.HighPart = ulSeekStart.HighPart;
hr = pStm->Seek(libMove, STREAM_SEEK_SET, NULL);
}
else
{
// read extension and leave seek pointer after header
if (pidh->dwflags & IDENFLAGS_EXTENSION)
hr = SkipMarshalExtension(pStm);
}
return hr;
}
//+-------------------------------------------------------------------
//
// Member: CStaticMarshaler::UnmarshalInterface, public
//
// Synopsis: First part of unmarshaling an identity object; looks
// for an existing object and if not found creates one
// of type clsidHandler and CLSCTX_INPROC_HANDLER. In
// all cases, skips back to the beginning of the identity
// information and forwards the unmarshal call on to the
// identified object. Thus it is the responsibility of
// handlers that support identity to make sure the identity
// information is consumed first.
//
// History: 11-Dec-93 CraigWi Created.
//
//--------------------------------------------------------------------
STDMETHODIMP CStaticMarshaler::UnmarshalInterface(LPSTREAM pStm,
REFIID riid, VOID **ppv)
{
// BUGBUG PERF: can we call into identity object directly or bypass it
// by getting it's followon marshaler?
HRESULT hr;
SIdentityDataHdr idh;
CLSID clsidHandler;
// read data header and optional clsidHandler; seek back;
hr = ReadIdentityHeader(pStm, &idh, &clsidHandler, TRUE /*fTransparent*/);
if (FAILED(hr))
return hr;
// lookup id; if exists, feed data to it
// else create handler for clsidHandler and feed data to it
// works for both normal and table marshal
IStdIdentity *pStdID;
IMarshal *pMarshal;
if (LookupIDFromID(idh.ObjectID, TRUE, &pStdID) == NOERROR)
{
// identity exists; we are holding it alive
hr = pStdID->QueryInterface(IID_IMarshal, (void**)&pMarshal);
pStdID->Release();
if (hr != NOERROR)
// something which supports identity must also support custom
// marshaling.
return E_UNEXPECTED;
}
else
{
// identity doesn't not exist; create object; add to table will be in
// the unmarshal below.
// CODEWORK: can we get check for duplicate identity here so
// the contention is easier to recover from??? Possibly we
// can create a dummy id which indicates we will register one
// or remove the dummy. Other thread could wait on that object
// and if that object went away, could then create its own
// dummy id and continue. All that would happen in LookupIDFromID.
// NOTE: this CLSCTX_INPROC_HANDLER is significant since we don't
// want to confuse an inproc server with what we know must be
// a handler. Besides, the 16bit code did it this way.
// if clsidHandler == CLSID_StdMarshal: create identity handler
// BUGBUG: should make the class object code do this (it did in 16bit)
if (IsEqualGUID(clsidHandler, CLSID_StdMarshal))
{
UseStdMarshal:
hr = CreateIdentityHandler(NULL, PSTDMARSHAL,
IID_IMarshal, (void **)&pMarshal);
}
else
{
hr = CoCreateInstance(clsidHandler, NULL, CLSCTX_INPROC_HANDLER,
IID_IMarshal, (void **)&pMarshal);
// if not registered, use StdMarshal
if (hr == REGDB_E_CLASSNOTREG)
{
clsidHandler = CLSID_StdMarshal;
goto UseStdMarshal;
}
}
// NOTE: this pMarshal must consume the identity information first.
// the most common way for people to do that is to expose the IMarshal
// on the identity object as the IMarshal of the handler.
if (hr != NOERROR)
return hr;
}
hr = pMarshal->UnmarshalInterface(pStm, riid, ppv);
pMarshal->Release();
// CODEWORK: multithread issue: if already registered,skip back and try again.
return hr;
}
//+-------------------------------------------------------------------
//
// Member: CStaticMarshaler::ReleaseMarshalData, public
//
// Synopsis: First part of releasing the marshal data for an identity
// object. Very much like UnmarshalInterface above.
//
// History: 11-Dec-93 CraigWi Created.
//
//--------------------------------------------------------------------
STDMETHODIMP CStaticMarshaler::ReleaseMarshalData(LPSTREAM pStm)
{
// CODEWORK PERF: if normal, we have to re-lookup the the identity;
// an alternative is to have the ::UnmarshalInteface method above
// do the release and mark the stream as released (perhaps even
// moving the HDLRFLAGS_RELEASED bit here); we would still have to
// return an error since we cannot actually consume the data. This
// works fine for the cases that matter (Unmarshal followed by
// Release).
HRESULT hr;
SIdentityDataHdr idh;
CLSID clsidHandler;
// read data header and optional clsidHandler; seek back;
hr = ReadIdentityHeader(pStm, &idh, &clsidHandler, TRUE /*fTransparent*/);
if (FAILED(hr))
return hr;
// lookup id; if exists, feed data to it
// else error (object already released)
// works for both normal and table marshal
// CODEWORK: when table marshaling changes, this code will have to
// change too.
IStdIdentity *pStdID;
IMarshal *pMarshal;
if (LookupIDFromID(idh.ObjectID, TRUE, &pStdID) == NOERROR)
{
// identity exists; we are holding it alive
hr = pStdID->QueryInterface(IID_IMarshal, (void**)&pMarshal);
pStdID->Release();
if (hr != NOERROR)
// something which supports identity must also support custom
// marshaling.
return E_UNEXPECTED;
}
else
{
// identity doesn't not exist; error
// BUGBUG: should do better here since this may happen in the
// error case. We would create an instance of the handler
// just for the destruction of the marshal data.
// Happened in bug 13086 before CStdIdentity::IsConnected was changed.
// If we don't do this, the marshal connection will not be released
// on the server side and the object may not shutdown as it should.
return CO_E_OBJNOTREG;
}
hr = pMarshal->ReleaseMarshalData(pStm);
pMarshal->Release();
return hr;
}
//+-------------------------------------------------------------------
//
// Member: CStaticMarshaler::DisconnectObject, public
//
// Synopsis: shouldn't be called.
//
// History: 11-Dec-93 CraigWi Created.
//
//--------------------------------------------------------------------
STDMETHODIMP CStaticMarshaler::DisconnectObject(DWORD dwReserved)
{
AssertSz(FALSE, "This DisconnectObject should not be called");
return E_UNEXPECTED;
}
//+-------------------------------------------------------------------
//
// Function: GetStaticMarshaler, private
//
// Synopsis: Returns the single instance of the std identity unmarshaler.
//
// History: 11-Dec-93 CraigWi Created.
//
//--------------------------------------------------------------------
INTERNAL_(IMarshal *) GetStaticMarshaler()
{
#if DBG == 1
sg_StaticMarshaler.AddRef();
#endif
return &sg_StaticMarshaler;
}
//+-------------------------------------------------------------------
//
// Function: SkipMarshalExtension, private
//
// Synopsis: Skips the marshaling extension (DWORD cb, rgcb).
//
// Returns: stm errors if problem
//
// History: 14-Dec-93 CraigWi Created.
//
//--------------------------------------------------------------------
INTERNAL SkipMarshalExtension(IStream *pStm)
{
HRESULT hr;
DWORD cb;
hr = StRead(pStm, &cb, sizeof(cb));
if (FAILED(hr))
return hr;
LARGE_INTEGER li;
LISet32(li, cb);
return pStm->Seek(li, STREAM_SEEK_CUR, NULL);
}
//+------------------------------------------------------------------------
//
// Function: RewriteHeader, private
//
// Synopsis: Writes the given data at the offset given and restores the
// stream to the position it was on entry.
//
// Arguments: [pStm] -- The stream into which we write the data
// [pb] -- The data
// [cb] -- The amount of data
// [ulSeekStart] -- The place to write the data
//
// History: 15-May-94 CraigWi Created
//
//-------------------------------------------------------------------------
INTERNAL_(void) RewriteHeader(IStream *pStm, void *pv, ULONG cb, ULARGE_INTEGER ulSeekStart)
{
LARGE_INTEGER libMove;
ULARGE_INTEGER ulSeekEnd;
LISet32(libMove, 0x00000000);
if (SUCCEEDED(pStm->Seek(libMove, STREAM_SEEK_CUR, &ulSeekEnd)))
{
// go back to the starting position
libMove.LowPart = ulSeekStart.LowPart;
libMove.HighPart = ulSeekStart.HighPart;
if (SUCCEEDED(pStm->Seek(libMove, STREAM_SEEK_SET, NULL)))
{
// set the header bit and write it back into the stream
pStm->Write(pv, cb, NULL);
}
// regardless of whether the write worked, restore the stream
// back to the ending positon. ignore any errors generated here.
libMove.LowPart = ulSeekEnd.LowPart;
libMove.HighPart = ulSeekEnd.HighPart;
pStm->Seek(libMove, STREAM_SEEK_SET, NULL);
}
}