//+-------------------------------------------------------------------
//
//  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)
{
    TRACECALL(TRACE_MARSHAL, "CoGetStandardMarshal");

    if (!IsApartmentInitialized())
        return  CO_E_NOTINITIALIZED;

    HRESULT sc = S_OK;
    IMarshal *pIM;

    pIM = FindOrCreateStdMarshal(pUnk, TRUE);
    *ppMarshal = pIM;   //  fill in the return parameters

    if (!pIM)
    {
        sc = E_OUTOFMEMORY;
    }

    CairoleDebugOut((DEB_ITRACE, "CoGetStandardMarshal: pUnk=%x pIM=%x sc=%x\n",
                                  pUnk, *ppMarshal, 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)
{
    TRACECALL(TRACE_MARSHAL, "CoGetMarshalSizeMax");

    Win4Assert(MARSHALINTERFACE_MIN >= sizeof(SMiApiDataHdr) + sizeof(CLSID));

    if (!IsApartmentInitialized())
        return  CO_E_NOTINITIALIZED;

    HRESULT sc = S_OK;
    IMarshal *pIM = NULL;

    //  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));
    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)
{
    TRACECALL(TRACE_MARSHAL, "CoMarshalInterface");

    if (!IsApartmentInitialized())
        return  CO_E_NOTINITIALIZED;

    HRESULT  sc;
    IMarshal *pIM = NULL;

    //  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;

        if (SUCCEEDED(sc))
            //  write the marshal info into the stream
            sc = pStm->Write(&ifp, sizeof(ifp), NULL);

        if (SUCCEEDED(sc) && (ifp.dwflags & MIAPIFLAGS_STDIDENTITY) == 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;
    }

    CairoleDebugOut((DEB_ITRACE, "CoMarshalInterface: pUnk=%x pIM=%x dwDest=%x pvDest=%x flags=%x sc=%x\n",
                                  pUnk, pIM, dwDestCtx, pvDestCtx, mshlflags, 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)
{
    return CoUnmarshalInterfaceEx(pStm, riid, ppv, TRUE /*fNormalDoesRelease*/);
}


// 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");

    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) == 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
        {
            //  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)
{
    TRACECALL(TRACE_MARSHAL, "CoReleaseMarshalData");

    if (!IsApartmentInitialized())
        return  CO_E_NOTINITIALIZED;

    HRESULT     sc;
    IMarshal    *pIM = NULL;
    SMiApiDataHdr ifp;
    CLSID clsid;

    //	save the current stream seek pointer

    ULARGE_INTEGER  ulSeekStart;
    LARGE_INTEGER   libMove;
    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) == 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
        {
            //  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));
    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)
{
    TRACECALL(TRACE_MARSHAL, "CoLockObjectExternal");

    //	REF COUNTING: inc or dec external ref count

    HRESULT sc;
    IStdIdentity *pStdID;
    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;
    }

    CairoleDebugOut((DEB_ITRACE, "CoLockObjectExternal pStdID=%x fLock=%x sc=%x.\n",
                                   pStdID, fLock, 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)
{
    TRACECALL(TRACE_MARSHAL, "CoDisconnectObject");

    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));

    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)
{
    IProxyManager *pPM;

    // Assume it is connected
    BOOL fResult = TRUE;

    // 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();
    }

    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?");
    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) + 512;

    // 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);
    }
}
