//+-------------------------------------------------------------------
//
//  File:       marshal.cxx
//
//  Contents:   class implementing standard COM interface marshaling
//
//  Classes:    CStdMarshal
//
//  History:    20-Feb-95   Rickhi      Created
//
//  DCOMWORK:   (maybe) implement Extended form marshal packet
//
//  PERFWORK: during unmarshal and RMD compare the MOXID in the STDOBJREF
//  to the one for the current apartment. If equal, then i know the IPID is
//  just an index into the IPID table and i can index into it, grab the
//  channel ptr and hence the stdid ptr and do very fast unmarshal or RMD
//  with no table lookup or list walking.
//
//--------------------------------------------------------------------
#include    <ole2int.h>
#include    <marshal.hxx>   // CStdMarshal
#include    <ipidtbl.hxx>   // CIPIDTable, COXIDTable, CMIDTable
#include    <riftbl.hxx>    // CRIFTable
#include    <resolver.hxx>  // CRpcResolver
#include    <stdid.hxx>     // CStdIdentity
#include    <channelb.hxx>  // CRpcChannelBuffer
#include    <callctrl.hxx>  // CAptRpcChnl, CSrvCallCtrl
#include    <scm.h>         // CLSCTX_PS_DLL
#include    <service.hxx>   // SASIZE
#include    <locks.hxx>     // LOCK/UNLOCK etc
#include    <thunkapi.hxx>  // GetAppCompatabilityFlags


#if DBG==1
// this flag and interface are used in debug to enable simpler testing
// of the esoteric NonNDR stub code feature.

BOOL gfFakeNonNDR    = FALSE;
const GUID IID_ICube =
    {0x00000139,0x0001,0x0008,{0xC0,0x00,0x00,0x00,0x00,0x00,0x00,0x46}};
#endif  // DBG


// BUGBUG: this is not quite reliable enough. Maybe best solution is
// CoGetCurrentProcessId plus sequence number.
LONG    gIPIDSeqNum = 0;

// mappings from MSHLFLAGS to STDOBJREF flags
static ULONG mapMFtoSORF[] =
{
    SORF_NULL,                  // MSHLFLAGS_NORMAL
    SORF_NULL,                  // MSHLFLAGS_TABLESTRONG
    SORF_TBLWEAK                // MSHLFLAGS_TABLEWEAK
};

// NULL resolver string array
DUALSTRINGARRAY saNULL = {0,0};

// number of remote AddRefs to acquire when we need more.
#define REM_ADDREF_CNT 5

// out internal psclass factory implementation
EXTERN_C HRESULT PrxDllGetClassObject(REFCLSID clsid, REFIID iid, void **ppv);


// structure used to post a delayed remote release call to ourself.
typedef struct tagPOSTRELRIFREF
{
    OXIDEntry      *pOXIDEntry; // server OXIDEntry
    USHORT          cRifRef;    // count of entries in arRifRef
    REMINTERFACEREF arRifRef;   // array of REMINTERFACEREFs
} POSTRELRIFREF;


//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::CStdMarshal/Init, public
//
//  Synopsis:   constructor/initializer of a standard marshaler
//
//  History:    20-Feb-95   Rickhi      Created.
//
//--------------------------------------------------------------------
CStdMarshal::CStdMarshal() : _dwFlags(0), _pChnl(NULL)
{
    // Caller must call Init before doing anything! This just makes it
    // easier for the identity object to figure out the init parameters
    // before initializing us.
}

void CStdMarshal::Init(IUnknown *punkObj, CStdIdentity *pStdId,
                       REFCLSID rclsidHandler, DWORD dwFlags)
{
    ASSERT_LOCK_DONTCARE // may be released if def handler calls CreateIdHdlr

    // server side we need to do the FirstMarshal work.
    // client side we assume disconnected until we connect the first IPIDEntry
    // and assume NOPING until we see any interface that needs pinging

    _dwFlags = dwFlags;
    _dwFlags |= (ServerSide()) ? SMFLAGS_FIRSTMARSHAL
                               : SMFLAGS_DISCONNECTED | SMFLAGS_NOPING;

    _pFirstIPID    = NULL;
    _cIPIDs        = 0;
    _pStdId        = pStdId;
    _pChnl         = NULL;
    _cNestedCalls  = 0;
    _cTableRefs    = 0;
    _dwMarshalTime = 0;
    _clsidHandler  = rclsidHandler;
    _pSecureRemUnk = NULL;

    ComDebOut((DEB_MARSHAL,"CStdMarshal %s New this:%x pStdId:%x punkObj:%x\n",
        (ClientSide()) ? "CLIENT" : "SERVER", this, pStdId, punkObj));

    AssertValid();
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::~CStdMarshal, public
//
//  Synopsis:   destructor of a standard marshaler
//
//  History:    20-Feb-95   Rickhi      Created.
//
//--------------------------------------------------------------------
CStdMarshal::~CStdMarshal()
{
    ComDebOut((DEB_MARSHAL, "CStdMarshal %s Deleted this:%x\n",
                    (ClientSide()) ? "CLIENT" : "SERVER", this));
    ASSERT_LOCK_RELEASED

    if (ClientSide())
    {
        // Due to backward compatibility, we are not allowed to release
        // interface proxies in Disconnect since the client might try to
        // reconnect later and expects the same interface pointer values.
        // Since we are going away now, we go release the proxies.

        ReleaseCliIPIDs();
        if (_pSecureRemUnk != NULL)
        {
            _pSecureRemUnk->Release();
        }
    }

    if (_pChnl)
    {
        // release the channel
        _pChnl->Release();
    }

    ASSERT_LOCK_RELEASED
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::GetUnmarshalClass, public
//
//  Synopsis:   returns the clsid of the standard marshaller
//
//  History:    20-Feb-95   Rickhi      Created.
//
//--------------------------------------------------------------------
STDMETHODIMP CStdMarshal::GetUnmarshalClass(REFIID riid, LPVOID pv,
        DWORD dwDestCtx, LPVOID pvDestCtx, DWORD mshlflags, LPCLSID pClsid)
{
    AssertValid();
    ASSERT_LOCK_RELEASED

    *pClsid = CLSID_StdMarshal;
    return S_OK;
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::GetMarshalSizeMax, public
//
//  Synopsis:   Returns an upper bound on the amount of data for
//              a standard interface marshal.
//
//  History:    20-Feb-95   Rickhi      Created.
//
//--------------------------------------------------------------------
STDMETHODIMP CStdMarshal::GetMarshalSizeMax(REFIID riid, LPVOID pv,
        DWORD dwDestCtx, LPVOID pvDestCtx, DWORD mshlflags, LPDWORD pSize)
{
    AssertValid();
    Win4Assert(gdwPsaMaxSize != 0);
    ASSERT_LOCK_RELEASED

    *pSize = sizeof(OBJREF) + gdwPsaMaxSize;
    return S_OK;
}

//+-------------------------------------------------------------------
//
//  Function:   MarshalObjRef, private
//
//  Synopsis:   Marshals interface into the objref.
//
//  Arguements: [objref]    - object reference
//              [riid]      - interface id to marshal
//              [pv]        - interface to marshal
//              [mshlflags] - marshal flags
//
//  Algorithm:  Get the correct standard identity and ask it to do
//              all the work.
//
//  History:    25-Mar-95   AlexMit     Created
//
//--------------------------------------------------------------------
INTERNAL MarshalObjRef(OBJREF &objref, REFIID riid, void *pv, DWORD mshlflags)
{
    TRACECALL(TRACE_MARSHAL, "MarshalObjRef");
    ComDebOut((DEB_MARSHAL, "MarshalObjRef: riid:%I pv:%x flags:%x\n",
        &riid, pv, mshlflags));
    ASSERT_LOCK_RELEASED

    HRESULT hr = InitChannelIfNecessary();
    if (SUCCEEDED(hr))
    {
        // Find or create the StdId for this object. We need to get a strong
        // reference to guard against an incoming last release on another
        // thread which would cause us to Disconnect this StdId.

        DWORD dwFlags = IDLF_CREATE | IDLF_STRONG;
        dwFlags |= (mshlflags & MSHLFLAGS_NOPING) ? IDLF_NOPING : 0;

        CStdIdentity *pStdID;
        hr = LookupIDFromUnk((IUnknown *)pv, dwFlags, &pStdID);

        if (hr == NOERROR)
        {
            hr = pStdID->MarshalObjRef(objref, riid, pv, mshlflags);
            pStdID->DecStrongCnt(TRUE); // fKeepAlive
        }
    }

    ASSERT_LOCK_RELEASED
    ComDebOut((DEB_MARSHAL, "MarshalObjRef: hr:%x\n", hr));
    return hr;
}

//+-------------------------------------------------------------------
//
//  Function:   MarshalInternalObjRef, private
//
//  Synopsis:   Marshals an internal interface into the objref.
//
//  Arguements: [objref]    - object reference
//              [riid]      - interface id to marshal
//              [pv]        - interface to marshal
//              [mshlflags] - marshal flags
//              [ppStdId]   - StdId to return (may be NULL)
//
//  Algorithm:  Create a StdIdentity and ask it to do the work.
//
//  Notes:      This differs from the normal MarshalObjRef in that it does
//              not look in the OID table for an already marshaled interface,
//              nor does it register the marshaled interface in the OID table.
//              This is used for internal interfaces such as the IObjServer
//              and IRemUnknown.
//
//  History:    25-Oct-95   Rickhi      Created
//
//--------------------------------------------------------------------
INTERNAL MarshalInternalObjRef(OBJREF &objref, REFIID riid, void *pv,
                               DWORD mshlflags, void **ppStdId)
{
    TRACECALL(TRACE_MARSHAL, "MarshalInternalObjRef");
    ComDebOut((DEB_MARSHAL, "MarshalInternalObjRef: riid:%I pv:%x flags:%x\n",
        &riid, pv, mshlflags));
    ASSERT_LOCK_RELEASED

    HRESULT hr = InitChannelIfNecessary();
    if (SUCCEEDED(hr))
    {
        if (!IsEqualGUID(riid, IID_IRundown))
        {
            // NOTE: make sure the local OXID is registered with the resolver.
            // See the discussion on the Chicken and Egg problem in ipidtbl.cxx
            // COXIDTable::GetLocalEntry for why this is necessary.

            LOCK
            MOID moid;
            hr = gResolver.ServerGetPreRegMOID(&moid);
            UNLOCK
        }

        if (SUCCEEDED(hr))
        {
            // Find or create the StdId for this object. We need to get a strong
            // reference to guard against an incoming last release on another
            // thread which would cause us to Disconnect this StdId.

            IUnknown *pUnkId;   // ignored
            CStdIdentity *pStdId = new CStdIdentity(STDID_SERVER, NULL,
                                                (IUnknown *)pv, &pUnkId);

            if (pStdId != NULL)
            {
                hr = pStdId->MarshalObjRef(objref, riid, pv, mshlflags);

                if (SUCCEEDED(hr) && ppStdId)
                {
                    *ppStdId = (void *)pStdId;
                }
                else
                {
                    pStdId->Release();
                }
            }
            else
            {
                hr = E_OUTOFMEMORY;
            }
        }
    }

    ASSERT_LOCK_RELEASED
    ComDebOut((DEB_MARSHAL, "MarshalInternalObjRef: hr:%x\n", hr));
    return hr;
}


//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::MarshalInterface, public
//
//  Synopsis:   marshals the interface into the stream.
//
//  History:    20-Feb-95   Rickhi      Created.
//
//--------------------------------------------------------------------
STDMETHODIMP CStdMarshal::MarshalInterface(IStream *pStm, REFIID riid,
        LPVOID pv, DWORD dwDestCtx, LPVOID pvDestCtx, DWORD mshlflags)
{
    ComDebOut((DEB_MARSHAL,
        "CStdMarshal::MarshalInterface this:%x pStm:%x riid:%I pv:%x dwCtx:%x pvCtx:%x flags:%x\n",
        this, pStm, &riid, pv, dwDestCtx, pvDestCtx, mshlflags));
    AssertValid();
    ASSERT_LOCK_RELEASED

    // Marshal the interface into an objref, then write the objref
    // into the provided stream.

    OBJREF  objref;
    HRESULT hr = MarshalObjRef(objref, riid, pv, mshlflags);

    if (SUCCEEDED(hr))
    {
        // write the objref into the stream
        hr = WriteObjRef(pStm, objref, dwDestCtx);

        if (FAILED(hr))
        {
            // undo whatever we just did, ignore error from here since
            // the stream write error supercedes any error from here.
            ReleaseMarshalObjRef(objref);
        }

        // free resources associated with the objref.
        FreeObjRef(objref);
    }

    ASSERT_LOCK_RELEASED
    ComDebOut((DEB_MARSHAL,"CStdMarshal::MarshalInterface this:%x hr:%x\n",
        this, hr));
    return hr;
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::MarshalObjRef, public
//
//  Synopsis:   marshals the interface into the objref.
//
//  History:    25-Mar-95   AlexMit     Seperated from MarshalInterface
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::MarshalObjRef(OBJREF &objref, REFIID riid,
                                   LPVOID pv, DWORD mshlflags)
{
    ComDebOut((DEB_MARSHAL,
        "CStdMarshal::MarsalObjRef this:%x riid:%I pv:%x flags:%x\n",
        this, &riid, pv, mshlflags));
    AssertValid();

    // validate the parameters. we dont allow TABLE cases if we are
    // a client side object.

    if ((mshlflags & MSHLFLAGS_TABLE) && ClientSide())
        return E_INVALIDARG;

    // count of Refs we are handing out. In the table cases we pass out
    // zero refs because we dont know how many times it will be unmarshaled
    // (and hence how many references to count). Zero refs will cause the
    // client to call back and ask for more references if it does not already
    // have any (which has the side effect of making sure the object still
    // exists, which is required by RunningObjectTable).

    ULONG cRefs = (mshlflags & MSHLFLAGS_TABLE) ? 0 :
                  (ClientSide()) ? 1 : REM_ADDREF_CNT;

    ASSERT_LOCK_RELEASED
    LOCK

    HRESULT hr = PreventDisconnect();
    if (SUCCEEDED(hr))
    {
        // The first time through we have some extra work to do so go off
        // and do that now. Next time we can just bypass all that work.

        if (_dwFlags & SMFLAGS_FIRSTMARSHAL)
        {
            hr = FirstMarshal((IUnknown *)pv, mshlflags);
        }

        if (SUCCEEDED(hr))
        {
            // Create the IPID table entry. On the server side this may
            // cause the creation of an interface stub, on the client side
            // it may just take away one of our references or it may call
            // the server to get more references for the interface being
            // marshaled.

            IPIDEntry *pIPIDEntry;
            hr = MarshalIPID(riid, cRefs, mshlflags, &pIPIDEntry);

            if (SUCCEEDED(hr))
            {
                // fill in the rest of the OBJREF
                FillObjRef(objref, cRefs, mshlflags, pIPIDEntry);
            }
        }
    }

    UNLOCK
    ASSERT_LOCK_RELEASED

    // it is now OK to allow real disconnects in.
    HRESULT hr2 = HandlePendingDisconnect(hr);
    if (FAILED(hr2) && SUCCEEDED(hr))
    {
        // a disconnect came in while marshaling. The ObjRef has a
        // reference to the OXIDEntry so go free that now.
        FreeObjRef(objref);
    }

    ComDebOut((DEB_MARSHAL, "CStdMarshal::MarshalObjRef this:%x hr:%x\n",
        this, hr2));
    return hr2;
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::FillObjRef, private
//
//  Synopsis:   Fill in the fields of an OBJREF
//
//  History:    21-Sep-95   Rickhi      Created
//
//+-------------------------------------------------------------------
void CStdMarshal::FillObjRef(OBJREF &objref, ULONG cRefs, DWORD mshlflags,
                             IPIDEntry *pIPIDEntry)
{
    ComDebOut((DEB_MARSHAL, "FillObjRef pObjRef:%x\n", &objref));
    ASSERT_LOCK_HELD
    AssertDisconnectPrevented();
    Win4Assert(pIPIDEntry);
    OXIDEntry **ppOXIDEntry;

    // first, fill in the STDOBJREF section
    STDOBJREF *pStd = &ORSTD(objref).std;
    FillSTD(pStd, cRefs, mshlflags, pIPIDEntry);

    // next fill in the rest of the OBJREF
    objref.signature = OBJREF_SIGNATURE;    // 'MEOW'
    objref.iid = pIPIDEntry->iid;           // interface iid

    if (_dwFlags & SMFLAGS_HANDLER)
    {
        // handler form, copy in the clsid
        objref.flags = OBJREF_HANDLER;
        ORHDL(objref).clsid = _clsidHandler;
        ppOXIDEntry  = (OXIDEntry **) &ORHDL(objref).saResAddr;
    }
    else
    {
        objref.flags = OBJREF_STANDARD;
        ppOXIDEntry  = (OXIDEntry **) &ORSTD(objref).saResAddr;
    }

    // TRICK: in order to keep the objref a fixed size internally,
    // we use the saResAddr.size field as a ptr to the OXIDEntry. We
    // pay attention to this in ReadObjRef, WriteObjRef, and FreeObjRef.

    *ppOXIDEntry = pIPIDEntry->pOXIDEntry;
    Win4Assert(*ppOXIDEntry != NULL);
    IncOXIDRefCnt(*ppOXIDEntry);
    ASSERT_LOCK_HELD
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::FillSTD, public
//
//  Synopsis:   Fill in the STDOBJREF fields of an OBJREF
//
//  History:    21-Sep-95   Rickhi      Created
//
//+-------------------------------------------------------------------
void CStdMarshal::FillSTD(STDOBJREF *pStd, ULONG cRefs, DWORD mshlflags,
                          IPIDEntry *pIPIDEntry)
{
    // fill in the STDOBJREF to return to the caller.
    pStd->flags  = mapMFtoSORF[mshlflags & MSHLFLAGS_TABLE];

    pStd->flags |= (pIPIDEntry->dwFlags & IPIDF_NOPING) ? SORF_NOPING : 0;
    pStd->flags |= (pIPIDEntry->dwFlags & IPIDF_NONNDRSTUB) ? SORF_NONNDR : 0;

    pStd->cPublicRefs = cRefs;

    pStd->ipid   = pIPIDEntry->ipid;

    OIDFromMOID(_pStdId->GetOID(), &pStd->oid);
    OXIDFromMOXID(pIPIDEntry->pOXIDEntry->moxid, &pStd->oxid);

    ValidateSTD(pStd);
    DbgDumpSTD(pStd);
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::FirstMarshal, private
//
//  Synopsis:   Does some first-time server side marshal stuff
//
//  Parameters: [pUnk] - interface being marshalled
//              [mshlflags] - flags for marshaling
//
//  History:    20-Feb-95   Rickhi      Created.
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::FirstMarshal(IUnknown *pUnk, DWORD mshlflags)
{
    ComDebOut((DEB_MARSHAL,
        "CStdMarshal::FirstMarshal this:%x pUnk:%x\n", this, pUnk));
    Win4Assert(ServerSide());
    Win4Assert(_dwFlags & SMFLAGS_FIRSTMARSHAL);
    Win4Assert(_pChnl == NULL);
    AssertValid();
    AssertDisconnectPrevented();
    ASSERT_LOCK_HELD

    // have now executed this code so dont do it again.
    _dwFlags &= ~SMFLAGS_FIRSTMARSHAL;

    if (mshlflags & MSHLFLAGS_NOPING)
    {
        // if the first interface is marked as NOPING, then all interfaces
        // for the object are treated as NOPING, otherwise, all interfaces
        // are marked as PING. MakeSrvIPIDEntry will look at _dwFlags to
        // determine whether to mark each IPIDEntry as NOPING or not.

        _dwFlags |= SMFLAGS_NOPING;
    }

    // get our local OXID. This should have already been created, and
    // so wont cause the LOCK to be released.

    OXIDEntry *pOXIDEntry;
    HRESULT hr = gOXIDTbl.GetLocalEntry(&pOXIDEntry);

    if (SUCCEEDED(hr))
    {
        // create a channel for this object.
        CRpcChannelBuffer *pChnl;
        hr = CreateChannel(pOXIDEntry, 0, GUID_NULL, GUID_NULL, &pChnl);
    }

    ASSERT_LOCK_HELD
    AssertDisconnectPrevented();
    ComDebOut((DEB_MARSHAL,
        "CStdMarshal::FirstMarshal this:%x hr:%x\n", this, hr));
    return hr;
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::MarshalIPID, private
//
//  Synopsis:   finds or creates an interface stub and IPID entry
//              for the given object interface.
//
//  Arguments:  [riid]   - interface to look for
//              [cRefs]  - count of references wanted
//              [mshlflags] - marshal flags
//              [ppEntry] - place to return IPIDEntry ptr
//
//  Returns:    S_OK if succeeded
//
//  History:    20-Feb-95   Rickhi        Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::MarshalIPID(REFIID riid, ULONG cRefs, DWORD mshlflags,
                                 IPIDEntry **ppIPIDEntry)
{
    TRACECALL(TRACE_MARSHAL, "CStdMarshal::MarshalIPID");
    ComDebOut((DEB_MARSHAL,
            "CStdMarshal::MarshalIPID this:%x riid:%I cRefs:%x mshlflags:%x ppEntry:%x\n",
            this, &riid, cRefs, mshlflags, ppIPIDEntry));
    AssertValid();
    AssertDisconnectPrevented();
    ASSERT_LOCK_HELD

    // validate input parms.
    Win4Assert(!(IsEqualIID(riid, IID_NULL) || IsEqualIID(riid, IID_IMarshal)));

    // look for an existing IPIDEntry for the requested interface
    IPIDEntry *pEntry;
    HRESULT hr = FindIPIDEntry(riid, &pEntry);

    if (FAILED(hr))
    {
        // no entry currently exists. on the server side we try to create one.
        // on the client side we do a remote QI for the requested interface.

        if (ServerSide())
        {
            // this call fail if we are disconnected during a yield.
            hr = MakeSrvIPIDEntry(riid, &pEntry);
        }
        else
        {
            hr = RemQIAndUnmarshal(1, (GUID *)&riid, NULL);
            if (SUCCEEDED(hr))
            {
                hr = FindIPIDEntry(riid, &pEntry);
            }
        }
    }

    if (SUCCEEDED(hr))
    {
        // REFCOUNTING:
        if (ServerSide())
        {
            // remember the latest marshal time so we can tell if the ping
            // server has run us down too early. This can happen when an
            // existing client dies and we remarshal the interface just
            // moments before the pingserver tells us the first guy is gone
            // and before the new client has had time to unmarshal and ping.

            _dwMarshalTime = GetCurrentTime();

            // inc the refcnt for the IPIDEntry and optionaly the stdid. Note
            // that for TABLE marshals cRefs is 0 (that's the number that gets
            // placed in the packet) but we do want a reference so we ask for
            // 1 here. ReleaseMarshalData will undo the 1.

            ULONG cRefs2 = (mshlflags & MSHLFLAGS_TABLE) ? 1 : cRefs;
            IncSrvIPIDCnt(pEntry, cRefs2, 0, NULL, mshlflags);
        }
        else  // client side,
        {
            // we dont support marshaling weak refs on the client side, though
            // we do support marshaling strong from a weak client by going to
            // the server and getting a strong reference.
            Win4Assert(!(mshlflags & MSHLFLAGS_WEAK));

            if (cRefs >= pEntry->cStrongRefs)
            {
                // need more references than we own, go get more from server
                // to satisfy the marshal. Get a few extra refs for ourselves
                // unless we are a weak client.

                ULONG cExtraRefs = (_dwFlags & SMFLAGS_WEAKCLIENT)
                                 ? 0 : REM_ADDREF_CNT;

                hr = RemoteAddRef(pEntry, pEntry->pOXIDEntry, cRefs + cExtraRefs, 0);

                if (SUCCEEDED(hr))
                {
                    // add in the extra references we asked for (if any).
                    pEntry->cStrongRefs += cExtraRefs;
                }
            }
            else
            {
                // we have enough references to satisfy this request (and still
                // keep some for ourselves), just subtract from the IPIDEntry
                pEntry->cStrongRefs -= cRefs;
            }

            // mark this object as having been client-side marshaled so
            // that we can tell the resolver whether or not it needs to
            // ping this object if we release it before the OID is registered.

            _dwFlags |= SMFLAGS_CLIENTMARSHALED;
        }

        // do some debug stuff
        ValidateIPIDEntry(pEntry);
        ComDebOut((DEB_MARSHAL, "pEntry:%x cRefs:%x cStdId:%x\n", pEntry,
                   pEntry->cStrongRefs, _pStdId->GetRC()));
    }

    *ppIPIDEntry = pEntry;

    ASSERT_LOCK_HELD
    AssertDisconnectPrevented();
    ComDebOut((DEB_MARSHAL, "CStdMarshal::MarshalIPID hr:%x pIPIDEntry\n", hr, *ppIPIDEntry));
    return hr;
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::UnmarshalInterface, public
//
//  Synopsis:   Unmarshals an Interface from a stream.
//
//  History:    20-Feb-95   Rickhi      Created.
//
//--------------------------------------------------------------------
STDMETHODIMP CStdMarshal::UnmarshalInterface(LPSTREAM pStm,
                                             REFIID riid, VOID **ppv)
{
    ComDebOut((DEB_MARSHAL, "CStdMarshal::UnmarsalInterface this:%x pStm:%x riid:%I\n",
                    this, pStm, &riid));
    AssertValid();
    ASSERT_LOCK_RELEASED

    // read the objref from the stream and find or create an instance
    // of CStdMarshal for its OID. Then ask that guy to do the rest of
    // the unmarshal (create the interface proxy)

    OBJREF  objref;
    HRESULT hr = ReadObjRef(pStm, objref);

    if (SUCCEEDED(hr))
    {
        // pass objref to subroutine to unmarshal the objref
        hr = ::UnmarshalObjRef(objref, ppv);

        // release the objref we read
        FreeObjRef(objref);
    }

    ASSERT_LOCK_RELEASED
    ComDebOut((DEB_MARSHAL,
        "UnmarsalInterface this:%x pv:%x hr:\n", this, *ppv, hr));
    return hr;
}

//+-------------------------------------------------------------------
//
//  Function:   UnmarshalObjRef, private
//
//  Synopsis:   UnMarshals interface from objref.
//
//  Arguements: [objref]    - object reference
//              [ppv]       - proxy
//
//  Algorithm:  Get the correct standard identity and ask it to do
//              all the work.
//
//  History:    25-Mar-95   AlexMit     Created
//
//--------------------------------------------------------------------
INTERNAL UnmarshalObjRef(OBJREF &objref, void **ppv)
{
    ASSERT_LOCK_RELEASED

    CStdMarshal *pStdMshl;
    HRESULT hr = FindStdMarshal(objref, &pStdMshl);

    if (SUCCEEDED(hr))
    {
        // pass objref to subroutine to unmarshal the objref
        hr = pStdMshl->UnmarshalObjRef(objref, ppv);
        CALLHOOKOBJECTCREATE(S_OK,ORHDL(objref).clsid,objref.iid,(IUnknown **)ppv);
        pStdMshl->Release();
    }
    else
    {
        // we could not create the indentity or handler, release the
        // marshaled objref.
        ReleaseMarshalObjRef(objref);
    }

    ASSERT_LOCK_RELEASED
    return hr;
}

//+-------------------------------------------------------------------
//
//  Function:   ChkIfLocalOID, private
//
//  Synopsis:   Helper function for UnmarshalInternalObjRef & FindStdMarshal
//
//  Arguements: [objref] - object reference
//              [ppStdMshl] - CStdMarshal returned
//
//  Algorithm:  Read the objref, get the OID. If we already have an identity
//              for this OID return it AddRefd.
//
//  History:    21-May-95   MurthyS     Created.
//
//--------------------------------------------------------------------
INTERNAL_(BOOL) ChkIfLocalOID(OBJREF &objref, CStdIdentity **ppStdId)
{
    STDOBJREF *pStd = &ORSTD(objref).std;
    BOOL flocal = FALSE;

    ComDebOut((DEB_MARSHAL, "ChkIfLocalOID (IN) poid: %x\n", &pStd->oid));
    Win4Assert((*ppStdId == NULL) && "ChkIfLocalOID: pStdId != NULL");

    ASSERT_LOCK_RELEASED
    LOCK

    OXIDEntry *pOXIDEntry = GetOXIDFromObjRef(objref);

    if (pOXIDEntry == GetLocalOXIDEntry())
    {
        flocal = TRUE;
        // OXID is for this apartment, look IPID up in the IPIDTable
        // directly, and extract the CStdMarshal from it.

        IPIDEntry *pEntry = gIPIDTbl.LookupIPID(pStd->ipid);
        if (pEntry && pEntry->pChnl)
        {
            // get the Identity
            *ppStdId = pEntry->pChnl->GetStdId();
            (*ppStdId)->AddRef();
        }
    }

    UNLOCK
    ASSERT_LOCK_RELEASED

    return flocal;
}

//+-------------------------------------------------------------------
//
//  Function:   UnmarshalInternalObjRef, private
//
//  Synopsis:   UnMarshals an internally-used interface from objref.
//
//  Arguements: [objref]    - object reference
//              [ppv]       - proxy
//
//  Algorithm:  Create a StdId and ask it to do the work.
//
//  Notes:      This differs from UnmarshalObjRef in that it does not lookup
//              or register the OID. This saves a fair amount of work and
//              avoids initializing the OID table.
//
//  History:    25-Oct-95   Rickhi      Created
//
//--------------------------------------------------------------------
INTERNAL UnmarshalInternalObjRef(OBJREF &objref, void **ppv)
{
    ASSERT_LOCK_RELEASED

    HRESULT hr = S_OK;
    CStdIdentity *pStdId = NULL;

    if (ChkIfLocalOID(objref, &pStdId))
    {
        if (pStdId)
        {
            // set OID in objref to match that in returned std identity
            OIDFromMOID(pStdId->GetOID(), &ORSTD(objref).std.oid);
        }
        else
        {
            hr = CO_E_OBJNOTCONNECTED;
        }
    }
    else
    {
        ASSERT_LOCK_RELEASED

        hr = CreateIdentityHandler(NULL, ORSTD(objref).std.flags,
                                   IID_IStdIdentity, (void **)&pStdId);
    }

    if (SUCCEEDED(hr))
    {
        // pass objref to subroutine to unmarshal the objref. tell StdId not
        // to register the OID in the OID table.

        pStdId->IgnoreOID();
        hr = pStdId->UnmarshalObjRef(objref, ppv);
        CALLHOOKOBJECTCREATE(S_OK,ORHDL(objref).clsid,objref.iid,(IUnknown **)ppv);
        pStdId->Release();
    }

    ASSERT_LOCK_RELEASED
    return hr;
}


//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::UnmarshalObjRef, private
//
//  Synopsis:   unmarshals the objref. Called by CoUnmarshalInterface,
//              UnmarshalObjRef APIs, and UnmarshalInterface method.
//
//  History:    20-Feb-95   Rickhi      Created.
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::UnmarshalObjRef(OBJREF &objref, void **ppv)
{
    ComDebOut((DEB_MARSHAL, "CStdMarshal::UnmarsalObjRef this:%x objref:%x riid:%I\n",
        this, &objref, &objref.iid));
    AssertValid();

    STDOBJREF   *pStd = &ORSTD(objref).std;
    OXIDEntry   *pOXIDEntry = GetOXIDFromObjRef(objref);
    DbgDumpSTD(pStd);

    ASSERT_LOCK_RELEASED
    LOCK

    // Prevent a disconnect from occuring while unmarshaling the
    // interface since we may have to yield the ORPC lock.

    HRESULT hr = PreventPendingDisconnect();

    if (SUCCEEDED(hr))
    {
        if (objref.flags & OBJREF_HANDLER)
        {
            // handler form, extract the handler clsid and set our flags
            _dwFlags |= SMFLAGS_HANDLER;
            _clsidHandler = ORHDL(objref).clsid;
        }

        // if no OID registered yet, do that now. only possible on client side
        // during reconnect.

        MOID moid;
        MOIDFromOIDAndMID(pStd->oid, pOXIDEntry->pMIDEntry->mid, &moid);
        hr = _pStdId->SetOID(moid);

        if (SUCCEEDED(hr))
        {
            // find or create the IPID entry for the interface. On the client
            // side this may cause the creation of an interface proxy. It will
            // also manipulate the reference counts.

            hr = UnmarshalIPID(objref.iid, pStd, pOXIDEntry, ppv);
        }
    }

    UNLOCK
    ASSERT_LOCK_RELEASED

    if (ClientSide())
    {
        if (SUCCEEDED(hr))
        {
            if (_pStdId->IsAggregated())
            {
                // we are currently holding a proxy pointer. If aggregated,
                // the controlling unknown may want to override this pointer
                // with his own version, so issue a QI to give it that chance.
                IUnknown *pUnk = (IUnknown *)*ppv;

#ifdef WX86OLE
                if (gcwx86.IsN2XProxy(pUnk))
                {
                    // Tell wx86 thunk layer to thunk as IUnknown
                    gcwx86.SetStubInvokeFlag((BOOL)1);
                }
#endif

                hr = pUnk->QueryInterface(objref.iid, ppv);
                pUnk->Release();
            }
        }
        else
        {
            // cleanup our state on failure (only meaningful on client side,
            // since if the unmarshal failed on the server side, the interface
            // is already cleaned up).
            ReleaseMarshalObjRef(objref);
        }
    }

    // now let pending disconnect through. on server-side, ignore any
    // error from HPD and pay attention only to the unmarshal result, since
    // a successful unmarshal on the server side may result in a disconnect
    // if that was the last external reference to the object.

    HRESULT hr2 = HandlePendingDisconnect(hr);

    if (FAILED(hr2) && ClientSide())
    {
        if (SUCCEEDED(hr))
        {
            // a disconnect came in while unmarshaling. ppv contains an
            // AddRef'd interface pointer so go Release that now.
            ((IUnknown *)*ppv)->Release();
        }
        hr = hr2;
    }

    ComDebOut((DEB_MARSHAL, "CStdMarshal::UnmarsalObjRef this:%x hr:%x\n",
        this, hr));
    return hr;
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::UnmarshalIPID, private
//
//  Synopsis:   finds or creates an interface proxy for the given
//              interface. may also do a remote query interface.
//
//  Arguements: [riid] - the interface to return
//              [std]  - standard objref to unmarshal from
//              [pOXIDEntry] - ptr to OXIDEntry of the server
//              [ppv]  - interface ptr of type riid returned, AddRef'd
//
//  Returns:    S_OK if succeeded
//
//  History:    20-Feb-95   Rickhi       Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::UnmarshalIPID(REFIID riid, STDOBJREF *pStd,
                                   OXIDEntry *pOXIDEntry, void **ppv)
{
    TRACECALL(TRACE_MARSHAL, "CStdMarshal::UnmarshalIPID");
    ComDebOut((DEB_MARSHAL,
            "CStdMarshal::UnmarshalIPID this:%x riid:%I pStd:%x pOXIDEntry:%x\n",
            this, &riid, pStd, pOXIDEntry));
    DbgDumpSTD(pStd);
    AssertValid();
    AssertDisconnectPrevented();
    ASSERT_LOCK_HELD

    // validate input params.
    Win4Assert(!(IsEqualIID(riid, IID_NULL) || IsEqualIID(riid, IID_IMarshal)));
    Win4Assert(pStd != NULL);
    ValidateSTD(pStd);
    Win4Assert(pOXIDEntry);


    // look for an existing IPIDEntry for the requested interface.
    IPIDEntry *pEntry;
    HRESULT hr = FindIPIDEntry(riid, &pEntry);

#ifdef WX86OLE
    BOOL fSameApt = SUCCEEDED(hr);
    PVOID pvPSThunk = NULL;
#endif


    // REFCOUNTING:
    if (ClientSide())
    {
        if (FAILED(hr))
        {
            // no IPID Entry exists yet for the requested interface. We do
            // have a STDOBJREF.  Create the interface proxy and IPIDEntry
            // now, and connect it up. If successful, the proxy will be
            // fully connected upon return, with pEntry->cStrongRefs set
            // to pStd->cPublicRefs.

            if (ppv)
                *ppv = NULL;
            hr = MakeCliIPIDEntry(riid, pStd, pOXIDEntry, &pEntry);
        }
        else if (pEntry->dwFlags & IPIDF_DISCONNECTED)
        {
            // reconnect the IPID entry to the server. this will set
            // pEntry->cStrongRefs to pStd->cPublicRefs. Even though we could
            // yield, the IPIDEntry is guarenteed connected on return
            // (cause we are holding the lock on return).

            hr = ConnectIPIDEntry(pStd, pOXIDEntry, pEntry);
        }
        else if ((pStd->flags & SORF_WEAKREF) &&
                 (pEntry->pOXIDEntry->dwFlags & OXIDF_MACHINE_LOCAL))
        {
            // add the refcnt to our weak total for this IPIDEntry
            pEntry->cWeakRefs += pStd->cPublicRefs;
        }
        else
        {
            // add the refcnt to our strong total for this IPIDEntry
            pEntry->cStrongRefs += pStd->cPublicRefs;
        }
    }
    else if (SUCCEEDED(hr))
    {
        // unmarshaling in the server apartment. If the cRefs is zero,
        // then the interface was TABLE marshalled and we dont do
        // anything to the IPID RefCnts since the object must live until
        // ReleaseMarshalData is called on it.

#ifdef WX86OLE
        pvPSThunk = gcwx86.UnmarshalledInSameApt(pEntry->pv, riid);
#endif
        if (pStd->cPublicRefs > 0)
        {
            // normal case, dec the ref counts from the IPID entry,
            // OLE always passed fLastReleaseCloses = FALSE on
            // Unmarshal and RMD so do the same here.

            DWORD mshlflags = (pStd->flags & SORF_WEAKREF)
                            ? (MSHLFLAGS_WEAK   | MSHLFLAGS_KEEPALIVE)
                            : (MSHLFLAGS_NORMAL | MSHLFLAGS_KEEPALIVE);

            DecSrvIPIDCnt(pEntry, pStd->cPublicRefs, 0, NULL, mshlflags);
        }
    }

    if (SUCCEEDED(hr) && ppv)
    {
        ValidateIPIDEntry(pEntry);

        // extract and AddRef the pointer to return to the caller.
        // Do this before releasing the lock (which we might do below
        // on the server-side in DecSrvIPIDCnt.

        // NOTE: we are calling App code while holding the lock,
        // but there is no way to avoid this.

        Win4Assert(IsValidInterface(pEntry->pv));
        *ppv = pEntry->pv;
        ((IUnknown *)*ppv)->AddRef();
        AssertOutPtrIface(hr, *ppv);
        if (_dwFlags & SMFLAGS_WEAKCLIENT && !(pStd->flags & SORF_WEAKREF))
        {
            // make the client interface weak, ignore errors.
            UNLOCK
            ASSERT_LOCK_RELEASED
            RemoteChangeRef(0,0);
            ASSERT_LOCK_RELEASED
            LOCK
        }
#ifdef WX86OLE
        // If we unmarshalled in the same apartment as the object and Wx86
        // recognized the interface then change the returned proxy to the
        // proxy created for the Wx86 PSThunk.
        if (pvPSThunk == (PVOID)-1)
        {
            // Wx86 recognized the interface, but could not establish a
            // PSThunk for it. Force an error return.
            *ppv = NULL;
            hr = E_NOINTERFACE;
        }
        else if (pvPSThunk != NULL)
        {
            // Wx86 recognized the interface and did establish a PSThunk
            // for it. Force a successful return with Wx86 proxy interface.
            *ppv = pvPSThunk;
        }
#endif
    }

    ComDebOut((DEB_MARSHAL, "pEntry:%x cRefs:%x cStdId:%x\n", pEntry,
        (SUCCEEDED(hr)) ? pEntry->cStrongRefs : 0, _pStdId->GetRC()));
    ASSERT_LOCK_HELD
    AssertDisconnectPrevented();
    ComDebOut((DEB_MARSHAL, "CStdMarshal::UnmarshalIPID hr:%x\n", hr));
    return hr;
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::PrivateCopyProxy, internal
//
//  Synopsis:   Creates a copy of a proxy and IPID entry.
//
//  Arguements: [pProxy]   - Proxy to copy
//              [ppProxy]  - return copy here.
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::PrivateCopyProxy( IUnknown *pv, IUnknown **ppv )
{
    TRACECALL(TRACE_MARSHAL, "CStdMarshal::PrivateCopyProxy");
    ComDebOut((DEB_MARSHAL, "CStdMarshal::PrivateCopyProxy this:%x pv:%x\n",
            this, pv));

    // Don't copy stubs.
    if (ServerSide())
        return E_INVALIDARG;

    ASSERT_LOCK_RELEASED
    LOCK

    // Prevent a disconnect from occuring while unmarshaling the
    // interface since we may have to yield the ORPC lock.

    HRESULT hr = PreventPendingDisconnect();

    if (SUCCEEDED(hr))
    {
        // Find the proxy to copy.
        IPIDEntry *pEntry;
        hr = FindIPIDEntryByInterface(pv, &pEntry);
        if (SUCCEEDED(hr))
        {
            // Don't copy disconnected proxies.
            if (pEntry->dwFlags & IPIDF_DISCONNECTED)
                hr = RPC_E_DISCONNECTED;

            // IUnknown can't be copied.
            else if (IsEqualGUID( pEntry->iid, IID_IUnknown ))
                hr = E_INVALIDARG;

            else
            {
                BOOL fNonNDRProxy;
                IRpcProxyBuffer *pProxy;
                hr = CreateProxy(pEntry->iid, &pProxy, (void **)ppv,
                                 &fNonNDRProxy);

                if (SUCCEEDED(hr))
                {
                    IPIDEntry *pIpidCopy;

                    // add a disconnected IPID entry to the table.
                    hr = AddIPIDEntry(NULL, &pEntry->ipid, pEntry->iid, NULL,
                                      pProxy, *ppv, &pIpidCopy);

                    if (SUCCEEDED(hr))
                    {
                        // mark this IPID as a copy so we dont free it during
                        // ReleaseIPIDs.
                        pIpidCopy->dwFlags |= IPIDF_COPY;

                        // connect the IPIDEntry before adding it to the table so
                        // that we dont have to worry about races between Unmarshal,
                        // Disconnect, and ReconnectProxies.

                        // Make up an objref. Mark it as NOPING since we dont
                        // really have any references and we dont really need
                        // any because if we ever try to marshal it we will
                        // find the original IPIDEntry and use that. NOPING
                        // also lets us skip this IPID in DisconnectCliIPIDs.

                        STDOBJREF std;
                        OXIDFromMOXID(pEntry->pOXIDEntry->moxid, &std.oxid);
                        std.ipid        = pEntry->ipid;
                        std.cPublicRefs = 1;
                        std.flags       = SORF_NOPING;

                        hr = ConnectIPIDEntry(&std, pEntry->pOXIDEntry, pIpidCopy);

                        // Add this IPID entry after the original.
                        pIpidCopy->pNextOID = pEntry->pNextOID;
                        pEntry->pNextOID    = pIpidCopy;
                        _cIPIDs++;
                    }
                    else
                    {
                        // could not get an IPIDEntry, release the proxy, need to
                        // release the lock to do this.

                        UNLOCK
                        ASSERT_LOCK_RELEASED

                        pProxy->Release();
                        ((IUnknown *)*ppv)->Release();

                        ASSERT_LOCK_RELEASED
                        LOCK
                    }
                }
            }
        }

        if (SUCCEEDED(hr))
        {
            ValidateIPIDEntry(pEntry);
            AssertOutPtrIface(hr, *ppv);
        }
        AssertDisconnectPrevented();
    }
    ASSERT_LOCK_HELD
    UNLOCK
    ASSERT_LOCK_RELEASED

    // Now let pending disconnect through.
    HRESULT hr2 = HandlePendingDisconnect(hr);
    if (FAILED(hr2) && SUCCEEDED(hr))
    {
        // a disconnect came in while creating the proxy. ppv contains
        // an AddRef'd interface pointer so go Release that now.
        ((IUnknown *)*ppv)->Release();
    }

    ComDebOut((DEB_MARSHAL, "CStdMarshal::PrivateCopyProxy hr:%x\n", hr2));
    return hr2;
}

//+-------------------------------------------------------------------
//
//  Member:     MakeSrvIPIDEntry, private
//
//  Synopsis:   creates a server side IPID table entry
//
//  Arguements: [riid] - the interface to return
//              [ppEntry] - IPIDEntry returned
//
//  History:    20-Feb-95   Rickhi       Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::MakeSrvIPIDEntry(REFIID riid, IPIDEntry **ppEntry)
{
    Win4Assert(ServerSide());
    AssertValid();
    AssertDisconnectPrevented();
    ASSERT_LOCK_HELD

    BOOL fNonNDRStub;
    void *pv;
    IRpcStubBuffer *pStub;
    HRESULT hr = CreateStub(riid, &pStub, &pv, &fNonNDRStub);

    if (SUCCEEDED(hr))
    {
        OXIDEntry *pOXIDEntry = _pChnl->GetOXIDEntry();

        IPID ipidDummy;
        hr = AddIPIDEntry(pOXIDEntry, &ipidDummy, riid, _pChnl, pStub, pv,
                          ppEntry);

        if (SUCCEEDED(hr))
        {
            if (_dwFlags & SMFLAGS_NOPING)
            {
                // object does no need pinging, turn on NOPING
                (*ppEntry)->dwFlags |= IPIDF_NOPING;
            }

            if (fNonNDRStub)
            {
                // the stub was a custom 16bit one requested by WOW, mark the
                // IPIDEntry as holding a non-NDR stub so we know to set the
                // SORF_NONNDR flag in the StdObjRef when marshaling. This
                // tells local clients whether to create a MIDL generated
                // proxy or custom proxy. Functionality to support OLE
                // Automation on DCOM.

                (*ppEntry)->dwFlags |= IPIDF_NONNDRSTUB;
            }

            // increment the OXIDEntry ref count so that it stays
            // around as long as the IPIDEntry points to it. It gets
            // decremented when we disconnect the IPIDEntry.

            IncOXIDRefCnt(pOXIDEntry);

            // chain the IPIDEntries for this OID together

            (*ppEntry)->pNextOID = _pFirstIPID;
            _pFirstIPID = *ppEntry;
        }
        else
        {
            // release the stub. we need to release the lock to do this.
            UNLOCK
            ASSERT_LOCK_RELEASED

            pStub->Release();

            ASSERT_LOCK_RELEASED
            LOCK
        }
    }

    ASSERT_LOCK_HELD
    AssertDisconnectPrevented();
    return hr;
}

//+-------------------------------------------------------------------
//
//  Member:     MakeCliIPIDEntry, private
//
//  Synopsis:   creates a client side IPID table entry
//
//  Arguements: [riid] - the interface to return
//              [pStd]  - standard objref
//              [pOXIDEntry] - OXIDEntry of the server
//              [ppEntry] - IPIDEntry returned
//
//  History:    20-Feb-95   Rickhi       Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::MakeCliIPIDEntry(REFIID riid, STDOBJREF *pStd,
                                      OXIDEntry *pOXIDEntry,
                                      IPIDEntry **ppEntry)
{
    Win4Assert(ClientSide());
    AssertValid();
    AssertDisconnectPrevented();
    Win4Assert(pOXIDEntry);
    ASSERT_LOCK_HELD

    BOOL fNonNDRProxy;
    void *pv;
    IRpcProxyBuffer *pProxy;
    HRESULT hr = CreateProxy(riid, &pProxy, &pv, &fNonNDRProxy);

    if (SUCCEEDED(hr))
    {
        // add a disconnected IPID entry to the table.
        hr = AddIPIDEntry(NULL, &pStd->ipid, riid, NULL, pProxy, pv, ppEntry);

        if (pv)
        {
            // throw away our reference here, we will get it back later
            // in UnmarshalIPID
            ((IUnknown *)pv)->Release();
        }

        if (SUCCEEDED(hr))
        {
            if (fNonNDRProxy)
            {
                // the proxy is a custom 16bit one requested by WOW, mark the
                // IPIDEntry as holding a non-NDR proxy so we know to set the
                // LOCALF_NOTNDR flag in the local header when we call on it
                // (see CRpcChannelBuffer::ClientGetBuffer). Functionality to
                // support OLE Automation on DCOM.

                (*ppEntry)->dwFlags |= IPIDF_NONNDRPROXY;
            }

            if (pStd->flags & SORF_NONNDR)
            {
                // need to remember this flag so we can tell other
                // unmarshalers if we remarshal it.

                (*ppEntry)->dwFlags |= IPIDF_NONNDRSTUB;
            }

            // connect the IPIDEntry before adding it to the table so
            // that we dont have to worry about races between Unmarshal,
            // Disconnect, and ReconnectProxies.

            hr = ConnectIPIDEntry(pStd, pOXIDEntry, *ppEntry);

            // chain the IPIDEntries for this OID together. On client side
            // always add the entry to the list regardless of whether connect
            // succeeded.

            (*ppEntry)->pNextOID = _pFirstIPID;
            _pFirstIPID = *ppEntry;

            _cIPIDs++;
        }
        else
        {
            // could not get an IPIDEntry, release the proxy, need to
            // release the lock to do this.

            UNLOCK
            ASSERT_LOCK_RELEASED

            pProxy->Release();

            ASSERT_LOCK_RELEASED
            LOCK
        }
    }

    ASSERT_LOCK_HELD
    AssertDisconnectPrevented();
    return hr;
}

//+-------------------------------------------------------------------
//
//  Member:     ConnectIPIDEntry, private
//
//  Synopsis:   connects a client side IPID table entry to the server
//
//  Arguments:  [pStd] - standard objref
//              [pOXIDEntry] - OXIDEntry for the server
//              [pEntry] - IPIDEntry to connect, already has a proxy
//                         and the IID filled in.
//
//  Notes:      This routine is re-entrant, it may be called multiple
//              times for the same IPIDEntry, with part of the work done
//              in one call and part in another. Only if the entry is
//              fully set up will it return S_OK and mark the entry as
//              connected. DisconnectCliIPIDs handles cleanup of partial
//              connections.
//
//  History:    20-Feb-95   Rickhi       Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::ConnectIPIDEntry(STDOBJREF *pStd,
                                      OXIDEntry *pOXIDEntry,
                                      IPIDEntry *pEntry)
{
    ComDebOut((DEB_MARSHAL,
        "CStdMarshal::ConnectIPIDEntry this:%x ipid:%I pOXIDEntry:%x pIPIDEntry:%x\n",
         this, &pStd->ipid, pOXIDEntry, pEntry));
    Win4Assert(ClientSide());
    AssertDisconnectPrevented();
    AssertValid();
    Win4Assert(pOXIDEntry);
    ASSERT_LOCK_HELD
    HRESULT hr = S_OK;

    // mark the object as having attempted to connect an IPIDEntry so that
    // if we fail somewhere in this routine and dont mark the whole object
    // as connected, Disconnect will still try to clean things up.

    _dwFlags |= SMFLAGS_TRIEDTOCONNECT;

    if (!(pStd->flags & SORF_NOPING))
    {
        // this interface requires pinging, turn off NOPING for this object
        // and this IPIDEntry.
        _dwFlags        &= ~SMFLAGS_NOPING;
        pEntry->dwFlags &= ~IPIDF_NOPING;
    }

    if (!(_dwFlags & (SMFLAGS_REGISTEREDOID | SMFLAGS_NOPING)))
    {
        // register the OID with the ping server so it will get pinged
        hr = gResolver.ClientRegisterOIDWithPingServer(pStd->oid, pOXIDEntry);
        if (FAILED(hr))
        {
            return hr;
        }

        _dwFlags |= SMFLAGS_REGISTEREDOID;
    }

    // Go get any references we need that are not already included in the
    // STDOBJREF. These references will have been added to the counts in
    // the IPIDEntry upon return. Any references in the STDOBJREF will be
    // added to the IPIDEntry count only if the connect succeeds, otherwise
    // ReleaseMarshalObjRef (which will clean up STDOBJREF references) will
    // get called by higher level code.

    hr = GetNeededRefs(pStd, pOXIDEntry, pEntry);
    if (FAILED(hr))
    {
        return hr;
    }

    if (pEntry->pChnl == NULL)
    {
        // create a channel for this oxid/ipid pair. On the client side we
        // create one channel per proxy (and hence per IPID).

        hr = CreateChannel(pOXIDEntry, pStd->flags, pStd->ipid,
                           pEntry->iid, &pEntry->pChnl);

        if (SUCCEEDED(hr))
        {
            // update this IPID table entry. must update ipid too since
            // on reconnect it differs from the old value.

            IncOXIDRefCnt(pOXIDEntry);
            pEntry->pOXIDEntry  = pOXIDEntry;
            pEntry->ipid        = pStd->ipid;
            pEntry->pChnl->SetIPIDEntry(pEntry);
        }
    }

    if (SUCCEEDED(hr))
    {
        // Release the lock while we connect the proxy. We have to do
        // this because the IDispatch proxy makes an Rpc call during
        // Connect (Yuk!), which causes the channel to assert that the
        // lock is released. The proxy MUST be able to handle multiple
        // simultaneous or nested connects to the same channel ptr, since
        // it is possible when we yield the lock for another thread to
        // come in here and try a connect.

        void *pv = NULL;
        IRpcProxyBuffer * pProxy = (IRpcProxyBuffer *)(pEntry->pStub);

        if (pProxy)
        {
            // HACKALERT: OleAutomation returns NULL pv in CreateProxy
            // in cases where they dont know whether to return an NDR
            // proxy or a custom-format proxy. So we have to go connect
            // the proxy first then Query for the real interface once that
            // is done.

            BOOL fGetpv = (pEntry->pv) ? FALSE : TRUE;

            UNLOCK
            ASSERT_LOCK_RELEASED

            hr = pProxy->Connect(pEntry->pChnl);
            if (fGetpv && SUCCEEDED(hr))
            {
#ifdef WX86OLE
                if (gcwx86.IsN2XProxy(pProxy))
                {
                    // If we are creating a proxy for an object that is
                    // living on the x86 side then we need to set the
                    // StubInvoke flag to allow QI to thunk the
                    // custom interface QI.
                    gcwx86.SetStubInvokeFlag((BOOL)2);
                }
#endif
                hr = pProxy->QueryInterface(pEntry->iid, &pv);
                AssertOutPtrIface(hr, pv);

                if(SUCCEEDED(hr))
                {
#ifdef WX86OLE
                    // Call whole32 thunk layer to play with the ref count
                    // and aggregate the proxy to the controlling unknown.
                    gcwx86.AggregateProxy(_pStdId->GetCtrlUnk(),
                                          (IUnknown *)pv);
#endif
                    // Release our reference here.
                    // We keep a weak reference to pv.
                    ((IUnknown *)pv)->Release();
                }
            }

            ASSERT_LOCK_RELEASED
            LOCK
        }

        // Regardless of errors from Connect and QI we wont try to cleanup
        // any of the work we have done so far in this routine. The routine
        // is reentrant (by the same thread or by different threads) and
        // those calls could be using some of resources we have already
        // allocated. Instead, we rely on DisconnectCliIPIDs to cleanup
        // the partial allocation of resources.

        if (pEntry->dwFlags & IPIDF_DISCONNECTED)
        {
            // Mark the IPIDEntry as connected so we dont try to connect
            // again. Also, as long as there is one IPID connected, the
            // whole object is considered connected. This allows disconnect
            // to find the newly connected IPID and disconnect it later.
            // Infact, DisconnectCliIPIDs relies on there being at least
            // one IPID with a non-NULL OXIDEntry. It is safe to set this
            // now because Disconnects have been temporarily turned off.

            if (SUCCEEDED(hr))
            {
                if (pv)
                {
                    // assign the interface pointer
                    pEntry->pv = pv;
                }

                AssertDisconnectPrevented();
                pEntry->dwFlags &= ~IPIDF_DISCONNECTED;
                _dwFlags &= ~SMFLAGS_DISCONNECTED;
            }
        }
        else
        {
            // while the lock was released, the IPIDEntry got connected
            // by another thread (or by a nested call on this thread).
            // Ignore any errors from Connect or QI since apparently
            // things are connected now.

            hr = S_OK;
        }

        if (SUCCEEDED(hr))
        {
            // Add in any references we were given. If we were given 0 refs
            // and the interface is noping, then pretend like we got 1 ref.

            ULONG cRefs = ((pStd->cPublicRefs == 0) && (pStd->flags & SORF_NOPING))
                          ? 1 : pStd->cPublicRefs;

            // figure out if we have weak or strong references. To be weak
            // they must be local to this machine and the SORF flag set.
            BOOL fWeak = ((pStd->flags & SORF_WEAKREF) &&
                         (pOXIDEntry->dwFlags & OXIDF_MACHINE_LOCAL));

            if (fWeak)
                pEntry->cWeakRefs += cRefs;
            else
                pEntry->cStrongRefs += cRefs;
        }

        // in debug build, ensure that we did not screw up
        ValidateIPIDEntry(pEntry);
    }

    ASSERT_LOCK_HELD
    AssertDisconnectPrevented();
    ComDebOut((DEB_MARSHAL,
        "CStdMarshal::ConnectIPIDEntry this:%x pOXIDEntry:%x pChnl:%x hr:%x\n",
         this, pEntry->pOXIDEntry, pEntry->pChnl, hr));
    return hr;
}

//+-------------------------------------------------------------------
//
//  Member:     GetNeededRefs, private
//
//  Synopsis:   Figures out if any references are needed and goes and gets
//              them from the server.
//
//  Arguments:  [pStd] - standard objref
//              [pOXIDEntry] - OXIDEntry for the server
//              [pEntry] - IPIDEntry to connect, already has a proxy
//                         and the IID filled in.
//
//  History:    20-Feb-95   Rickhi       Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::GetNeededRefs(STDOBJREF *pStd, OXIDEntry *pOXIDEntry,
                                   IPIDEntry *pEntry)
{
    HRESULT hr  = S_OK;

    if ((pStd->flags & (SORF_NOPING | SORF_WEAKREF)) == 0)
    {
        // if we dont have any and weren't given any strong refs, go get some.
        ULONG cNeedStrong = ((pEntry->cStrongRefs + pStd->cPublicRefs) == 0)
                            ? REM_ADDREF_CNT : 0;

        // if we are using secure refs and we dont have any, go get some.
        ULONG cNeedSecure = ((gCapabilities & EOAC_SECURE_REFS) &&
                            (pEntry->cPrivateRefs == 0)) ? 1 : 0;

        if (cNeedStrong || cNeedSecure)
        {
            // Need to go get some references from the remote server. Note
            // that we will yield here but we dont have to worry about it because
            // the IPIDEntry is still marked as disconnected.

            hr = RemoteAddRef(pEntry, pOXIDEntry, cNeedStrong, cNeedSecure);

            if (SUCCEEDED(hr))
            {
                pEntry->cStrongRefs  += cNeedStrong;
                pEntry->cPrivateRefs += cNeedSecure;
            }
        }
    }

    return hr;
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::ReconnectProxies
//
//  Synopsis:   Reconnects the proxies to a new server (functionality
//              used by the OLE default handler).
//
//  History:    20-Feb-95   Rickhi  Created.
//
//  CODEWORK:   CreateServer should just ask for all these interfaces
//              during the create.
//
//  BUGBUG: fail this call if freethreaded
//
//--------------------------------------------------------------------
void CStdMarshal::ReconnectProxies()
{
    ComDebOut((DEB_MARSHAL,"CStdMarshal::ReconnectProxies this:%x pFirst:%x\n",
               this, _pFirstIPID));
    AssertValid();
    Win4Assert(ClientSide());
    ASSERT_LOCK_RELEASED
    LOCK

    // must be at least 1 proxy already connected in order to be able
    // to reconnect the other proxies. We cant just ASSERT that's true
    // because we were not holding the lock on entry.

    HRESULT hr = PreventDisconnect();

    if (SUCCEEDED(hr))
    {
        // allocate a stack buffer to hold the IPIDs
        IID *pIIDsAlloc = (IID *) _alloca(_cIPIDs * sizeof(IID));
        IID    *pIIDs = pIIDsAlloc;
        USHORT  cIIDs = 0;

        IPIDEntry *pNextIPID = _pFirstIPID;

        while (pNextIPID)
        {
            // Don't allow reconnection for fancy new servers or with
            // secure proxies.
            if (pNextIPID->dwFlags & IPIDF_COPY)
            {
                hr = E_FAIL;
                break;
            }
            if ((pNextIPID->dwFlags & IPIDF_DISCONNECTED))
            {
                // not connected, add it to the list to be connected.
                *pIIDs = pNextIPID->iid;
                pIIDs++;
                cIIDs++;
            }

            pNextIPID = pNextIPID->pNextOID;
        }

        if (cIIDs != 0 && SUCCEEDED(hr))
        {
            // we have looped filling in the IID list, and there are
            // entries int he list. go call QI on server now and
            // unmarshal the results.

            hr = RemQIAndUnmarshal(cIIDs, pIIDsAlloc, NULL);
        }
    }

    DbgWalkIPIDs();
    UNLOCK
    ASSERT_LOCK_RELEASED

    // this will handle any Disconnect that came in while we were busy.
    hr = HandlePendingDisconnect(hr);

    ComDebOut((DEB_MARSHAL,"CStdMarshal::ReconnectProxies [OUT] this:%x\n", this));
    return;
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::ReleaseMarshalData, public
//
//  Synopsis:   Releases the references added by MarshalInterface
//
//  History:    20-Feb-95   Rickhi      Created.
//
//--------------------------------------------------------------------
STDMETHODIMP CStdMarshal::ReleaseMarshalData(LPSTREAM pStm)
{
    ComDebOut((DEB_MARSHAL,
        "CStdMarshal::ReleaseMarshalData this:%x pStm:%x\n", this, pStm));
    AssertValid();
    ASSERT_LOCK_RELEASED

    OBJREF  objref;
    HRESULT hr = ReadObjRef(pStm, objref);

    if (SUCCEEDED(hr))
    {
        // call worker API to do the rest of the work
        hr = ::ReleaseMarshalObjRef(objref);

        // deallocate the objref we read
        FreeObjRef(objref);
    }

    ASSERT_LOCK_RELEASED
    ComDebOut((DEB_MARSHAL,
        "CStdMarshal::ReleaseMarshalData this:%x hr:%x\n", this, hr));
    return hr;
}

//+-------------------------------------------------------------------
//
//  Function:   ReleaseMarshalObjRef, private
//
//  Synopsis:   Releases the references added by MarshalObjRef
//
//  Arguements: [objref] - object reference
//
//  Algorithm:  Get the correct standard identity and ask it to do
//              a ReleaseMarshalData.
//
//  History:    19-Jun-95   Rickhi      Created
//
//--------------------------------------------------------------------
INTERNAL ReleaseMarshalObjRef(OBJREF &objref)
{
    ComDebOut((DEB_MARSHAL, "ReleaseMarshalObjRef objref:%x\n", &objref));
    ASSERT_LOCK_RELEASED

    HRESULT hr = InitChannelIfNecessary();
    if (SUCCEEDED(hr))
    {
        CStdMarshal *pStdMshl;
        hr = FindStdMarshal(objref, &pStdMshl);

        if (SUCCEEDED(hr))
        {
            // only do the RMD if on the server side.
            if (pStdMshl->ServerSide())
            {
                // pass objref to subroutine to Release the marshaled data
                hr = pStdMshl->ReleaseMarshalObjRef(objref);
            }
            pStdMshl->Release();
        }
        else
        {
            // we could not find or create an identity. If the server is
            // outside this apartment, try to issue a remote release on
            // the interface. if the OXID is local and we could not find
            // the identity, there is nothing left to cleanup.

            LOCK
            OXIDEntry *pOXIDEntry = GetOXIDFromObjRef(objref);
            if (pOXIDEntry != GetLocalOXIDEntry())
            {
                        // make a remote release call
                        RemoteReleaseObjRef(objref);
            }
            UNLOCK
        }
    }

    ASSERT_LOCK_RELEASED
    ComDebOut((DEB_MARSHAL, "ReleaseMarshalObjRef hr:%x\n", hr));
    return hr;
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::ReleaseMarshalObjRef, public
//
//  Synopsis:   Releases the references added by MarshalObjRef
//
//  History:    20-Feb-95   Rickhi      Created.
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::ReleaseMarshalObjRef(OBJREF &objref)
{
    ComDebOut((DEB_MARSHAL,
        "CStdMarshal::ReleaseMarshalObjRef this:%x objref:%x\n", this, &objref));
    AssertValid();

    HRESULT    hr = S_OK;
    STDOBJREF *pStd = &ORSTD(objref).std;
    ValidateSTD(pStd);

    ASSERT_LOCK_RELEASED
    LOCK

    // REFCOUNTING:
    if (ServerSide())
    {
        // look for an existing IPIDEntry for the given IPID
        IPIDEntry *pEntry;
        hr = FindIPIDEntryByIPID(pStd->ipid, &pEntry);

        if (SUCCEEDED(hr) && !(pEntry->dwFlags & IPIDF_DISCONNECTED))
        {
            // subtract the ref count from the IPIDEntry, may Release the
            // StdId if this was the last reference for this IPIDEntry.

            // we need to figure out how it was marshalled, strong/weak etc
            // in order to set the flags and cRefs correctly to pass to
            // DecSrvIPIDCnt.

            if (pStd->cPublicRefs == 0)
            {
                // table case
                DWORD mshlflags = (pStd->flags & SORF_TBLWEAK)
                                ? MSHLFLAGS_TABLEWEAK : MSHLFLAGS_TABLESTRONG;
                DecSrvIPIDCnt(pEntry, 1, 0, NULL, mshlflags);
            }
            else
            {
                // normal or weak case
                DWORD mshlflags = (pStd->flags & SORF_WEAKREF)
                                ? MSHLFLAGS_WEAK : MSHLFLAGS_NORMAL;
                DecSrvIPIDCnt(pEntry, pStd->cPublicRefs, 0, NULL, mshlflags);
            }
        }
    }
    else  // client side
    {
        if ((pStd->cPublicRefs == 0) || (pStd->flags & SORF_NOPING))
        {
            // there are no references, or this interface does not
            // need pinging, so there is nothing to do.
            ;
        }
        else
        {
            // look for an existing IPIDEntry for the given IPID
            IPIDEntry *pEntry;
            hr = FindIPIDEntryByIPID(pStd->ipid, &pEntry);

            if (SUCCEEDED(hr) && !(pEntry->dwFlags & IPIDF_DISCONNECTED))
            {
                // add these to the cRefs of this entry, they will get freed
                // when we do the remote release.  Saves an Rpc call now.

                if ((pStd->flags & SORF_WEAKREF) &&
                    (pEntry->pOXIDEntry->dwFlags & OXIDF_MACHINE_LOCAL))
                    pEntry->cWeakRefs   += pStd->cPublicRefs;
                else
                    pEntry->cStrongRefs += pStd->cPublicRefs;
            }
            else
            {
                // client side, no matching IPIDEntry so just contact the remote
                // server to remove the reference. ignore errors since there is
                // nothing we can do about them anyway.
                RemoteReleaseObjRef(objref);
                hr = S_OK;
            }
        }
    }

    UNLOCK
    ASSERT_LOCK_RELEASED
    ComDebOut((DEB_MARSHAL,
        "CStdMarshal::ReleaseMarshalObjRef this:%x hr:%x\n", this, hr));
    return hr;
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::PreventDisconnect, public
//
//  Synopsis:   Prevents a Disconnect from occurring until a matching
//              HandlePendingDisconnect is called.
//
//  History:    21-Sep-95   Rickhi      Created
//
//  The ORPC LOCK is yielded at many places in order to make calls on
//  application interfaces (server-side objects, stubs, proxies,
//  handlers, remote objects, resolver, etc). In order to keep the
//  code (reasonably?) simple, disconnects are prevented from occuring
//  while in the middle of (potentially) complex operations, and while
//  there are outstanding calls on interfaces to this object.
//
//  To accomplish this, a counter (_cNestedCalls) is atomically incremented.
//  When _cNestedCalls != 0 and a Disconnect arrives, the object is flagged
//  as PendingDisconnect. When HandlePendingDisconnect is called, it
//  decrements the _cNestedCalls. If the _cNestedCalls == 0 and there is
//  a pending disconnect, the real Disconnect is done.
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::PreventDisconnect()
{
    ASSERT_LOCK_HELD

    // treat this as a nested call so that if we yield, a real
    // disconnect wont come through, instead it will be treated
    // as pending. That allows us to avoid checking our state
    // for Disconnected every time we yield the ORPC LOCK.

    InterlockedIncrement(&_cNestedCalls);

    if (_dwFlags & (SMFLAGS_DISCONNECTED | SMFLAGS_PENDINGDISCONNECT))
        return CO_E_OBJNOTCONNECTED;

    return S_OK;
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::PreventPendingDisconnect, public
//
//  Synopsis:   similar to PreventDisconnect but special case for use
//              in UnmarshalObjRef (since the client side starts out
//              in the Disconnected state until the first unmarshal is done).
//
//  History:    21-Sep-95   Rickhi      Created
//
//+-------------------------------------------------------------------
HRESULT CStdMarshal::PreventPendingDisconnect()
{
    ASSERT_LOCK_HELD
    InterlockedIncrement(&_cNestedCalls);

    if (_dwFlags &
          (ClientSide() ? SMFLAGS_PENDINGDISCONNECT
                        : SMFLAGS_PENDINGDISCONNECT | SMFLAGS_DISCONNECTED))
        return CO_E_OBJNOTCONNECTED;

    return S_OK;
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::HandlePendingDisconnect, public
//
//  Synopsis:   Reverses a call to PreventDisconnect and lets a
//              pending disconnect through.
//
//  History:    21-Sep-95   Rickhi      Created
//
//+-------------------------------------------------------------------
HRESULT CStdMarshal::HandlePendingDisconnect(HRESULT hr)
{
    ASSERT_LOCK_RELEASED

    // treat this as a nested call so that if we yield, a real
    // disconnect wont come through, instead it will be treated
    // as pending. That allows us to avoid checking our state
    // for Disconnected every time we yield the ORPC LOCK.

    if (InterlockedDecrement(&_cNestedCalls) == 0 &&
        (_dwFlags & SMFLAGS_PENDINGDISCONNECT))
    {
        Disconnect();
        hr = FAILED(hr) ? hr : CO_E_OBJNOTCONNECTED;
    }

    return hr;
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::DisconnectObject, public
//
//  Synopsis:   part of IMarshal interface, this is legal only on the
//              server side.
//
//  History:    20-Feb-95   Rickhi      Created.
//
//--------------------------------------------------------------------
STDMETHODIMP CStdMarshal::DisconnectObject(DWORD dwReserved)
{
    ComDebOut((DEB_MARSHAL,
        "CStdMarshal::DisconnectObject this:%x dwRes:%x\n", this, dwReserved));
    AssertValid();
    ASSERT_LOCK_RELEASED

    // this operation is not legal from the client side (although
    // IProxyManager::Disconnect is), but we still have to return S_OK
    // in either case for backward compatibility.

    if (ServerSide())
    {
        Disconnect();
    }

    ASSERT_LOCK_RELEASED
    return S_OK;
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::Disconnect, public
//
//  Synopsis:   client side - disconnects proxies from the channel.
//              server side - disconnects stubs from the server object.
//
//  History:    20-Feb-95   Rickhi      Created.
//
//--------------------------------------------------------------------
void CStdMarshal::Disconnect(void)
{
    ComDebOut((DEB_MARSHAL, "CStdMarshal::Disconnect this:%x\n", this));
    AssertValid();

    ASSERT_LOCK_RELEASED
    LOCK

    if ((_dwFlags & SMFLAGS_DISCONNECTED) &&
       !(_dwFlags & SMFLAGS_TRIEDTOCONNECT))
    {
        // already disconnected, no partial connects, nothing to do
        ComDebOut((DEB_MARSHAL,"CStdMarshal::Disconnect [already done]:%x\n",this));
        UNLOCK
        ASSERT_LOCK_RELEASED
        return;
    }

    // Revoke ID from the ID table if registered. This prevents other
    // marshals/unmarshals from finding this identity that is about to
    // be disconnected. This is the ONLY state that should change, since
    // we dont want to screw up any work-in-progress on other threads
    // or in calls higher up the stack.

    _pStdId->RevokeOID();

    if (_cNestedCalls != 0)
    {
        // we dont allow disconnect to occur inside a nested call since we
        // dont want state to vanish in the middle of a call, but we do
        // remember that we want to disconnect and will do it when the
        // stack unwinds (or other threads complete).

        _dwFlags |= SMFLAGS_PENDINGDISCONNECT;

        ComDebOut((DEB_MARSHAL,"CStdMarshal::Disconnect [pending]:%x\n",this));
        UNLOCK;
        ASSERT_LOCK_RELEASED
        return;
    }


    // No calls in progress and not already disconnected, OK to really
    // disconnect now. First mark ourself as disconnected incase we
    // get reentered while releasing a stub pointer.

    _dwFlags |= SMFLAGS_DISCONNECTED;           // turn on disconnected
    _dwFlags &= ~(SMFLAGS_PENDINGDISCONNECT |   // turn off pending disconnect
                  SMFLAGS_TRIEDTOCONNECT);      // turn off tried to connect

    // disconnect all our IPIDs
    if (ServerSide())
        DisconnectSrvIPIDs();
    else
        DisconnectCliIPIDs();

    UNLOCK
    ASSERT_LOCK_RELEASED

    if (ServerSide())
    {
        // HACK - 16 and 32 bit Word 6.0 crash if you release all the objects
        // it left lying around at CoUninitialize.  Leak them.
        COleTls tls;
        // If we are not uninitializing, then call the release.
        if ((tls->dwFlags & OLETLS_THREADUNINITIALIZING) == 0 ||

        // If we are in WOW and the app is not word, then call the release.
           (IsWOWThread() &&
            (g_pOleThunkWOW->GetAppCompatibilityFlags() & OACF_NO_UNINIT_CLEANUP) == 0) ||

        // If the app is not 32 bit word, then call the release.
           !IsTaskName( L"winword.exe" ))
        {
            // on the server side, we have to tell the stdid to release his
            // controlling unknown of the real object.
            _pStdId->ReleaseCtrlUnk();
        }
    }

    ASSERT_LOCK_RELEASED
    ComDebOut((DEB_MARSHAL,"CStdMarshal::Disconnect [complete]:%x\n",this));
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::DisconnectCliIPIDs
//
//  Synopsis:   disconnects client side IPIDs for this object.
//
//  History:    20-Feb-95   Rickhi  Created.
//
//--------------------------------------------------------------------
void CStdMarshal::DisconnectCliIPIDs()
{
    ComDebOut((DEB_MARSHAL,"CStdMarshal::DisconnectCliIPIDs this:%x pFirst:%x\n",
               this, _pFirstIPID));
    Win4Assert(ClientSide());
    Win4Assert(_dwFlags & SMFLAGS_DISCONNECTED);

    // YIELD WARNING: Do not yield between here and the matching comment
    // below, since we are mucking with internal state that could get
    // messed up if a reconnect (or unmarshal) is done.

    ASSERT_LOCK_HELD

    // on client side, we cant actually release the proxies until the
    // object goes away (backward compatibility), so we just release
    // our references to the remote guy, disconnect the proxies, and
    // delete the channels, but hold on to the IPIDEntries.

    REMINTERFACEREF *pRifRefAlloc = (REMINTERFACEREF *)
                            _alloca(_cIPIDs * 2 * sizeof(REMINTERFACEREF));
    REMINTERFACEREF *pRifRef = pRifRefAlloc;


    OXIDEntry  *pOXID = NULL;
    USHORT    cRifRef = 0;
    IPIDEntry *pEntry = _pFirstIPID;

    while (pEntry)
    {
        // we have to handle the case where ConnectIPIDEntry partially (but
        // not completely) set up the IPIDEntry, hence we cant just check
        // for the IPIDF_DISCONNECTED flag.

        ValidateIPIDEntry(pEntry);

        // NOTE: we are calling Proxy code here while holding the ORPC LOCK.
        // There is no way to get around this without introducing race
        // conditions.  We cant just disconnect the channel and leave the
        // proxy connected cause some proxies (like IDispatch) do weird shit,
        // like keeping separate pointers to the server.

        if (pEntry->pStub)      // NULL for IUnknown IPID
        {
            ComDebOut((DEB_MARSHAL, "Disconnect pProxy:%x\n", pEntry->pStub));
            ((IRpcProxyBuffer *)pEntry->pStub)->Disconnect();
            pEntry->pv = NULL;
        }

        if (!(pEntry->dwFlags & IPIDF_NOPING))
        {
            // the object pays attention to pings (and hence refcounts)

            if (pEntry->cStrongRefs > 0 || pEntry->cPrivateRefs > 0)
            {
                // we own some strong references on this interface, fill
                // in an interfaceref so we release them.

                pRifRef->cPublicRefs  = pEntry->cStrongRefs;
                pRifRef->cPrivateRefs = pEntry->cPrivateRefs;
                pRifRef->ipid         = pEntry->ipid;
                pRifRef++;
                cRifRef++;
            }

            if (pEntry->cWeakRefs > 0)
            {
                // we own some weak references on this interface, fill
                // in an interfaceref so we release them.

                pRifRef->cPublicRefs  = pEntry->cWeakRefs;
                pRifRef->cPrivateRefs = 0;
                pRifRef->ipid         = pEntry->ipid;

                // mark the IPID as weak so that RemRelease on the server
                // knows to release weak references instead of strong refs.

                pRifRef->ipid.Data1 |= IPIDFLAG_WEAKREF;
                pRifRef++;
                cRifRef++;
            }
        }

        pEntry->cStrongRefs  = 0;
        pEntry->cWeakRefs    = 0;
        pEntry->cPrivateRefs = 0;
        pEntry->dwFlags     |= IPIDF_DISCONNECTED | IPIDF_NOPING;

        if (pEntry->pChnl)
        {
            // release the channel for this IPID
            pEntry->pChnl->Release();
            pEntry->pChnl = NULL;
        }

        if (pEntry->pOXIDEntry)
        {
            // We will be decrementing the OXID refcnt as we release IPIDEntries
            // but we dont want the OXIDEntry to go away until after we make the
            // RemoteRelease call below, so we hold on to it here.

            if (pOXID == NULL)
            {
                pOXID = pEntry->pOXIDEntry;
                IncOXIDRefCnt(pOXID);
            }

            // If we ever go to a model where different IPIDEntries on the
            // same object can point to different OXIDEntires, then we need
            // to re-write this code to batch the releases by OXID.
            Win4Assert(pOXID == pEntry->pOXIDEntry);

            // release the RefCnt on the OXIDEntry
            DecOXIDRefCnt(pEntry->pOXIDEntry);
            pEntry->pOXIDEntry = NULL;
        }

        // get next IPID in chain for this object
        pEntry = pEntry->pNextOID;
    }

    if (_pChnl)
    {
        // release the last client side channel
        _pChnl->Release();
        _pChnl = NULL;
    }

    if (_dwFlags & SMFLAGS_REGISTEREDOID)
    {
        // Tell the resolver to stop pinging the OID. The OID is only
        // registered on the client side.

        Win4Assert(ClientSide());
        gResolver.ClientDeRegisterOIDFromPingServer(_pStdId->GetOID(),
                                          _dwFlags & SMFLAGS_CLIENTMARSHALED);

    }

    // turn these flags off so re-connect (with new OID) will behave properly.
    _dwFlags &= ~(SMFLAGS_CLIENTMARSHALED | SMFLAGS_REGISTEREDOID |
                  SMFLAGS_NOPING);


    // YIELD WARNING: Up this this point we have been mucking with our
    // internal state. We cant yield before this point or a reconnect
    // proxies could get all screwed up. It is OK to yield after this point
    // because all internal state changes are now complete. The function
    // to release the remote references yield.

    if (cRifRef != 0)
    {
        // we have looped filling in the RifRef and entries exist in the
        // array. go call the server now to release the IPIDs.

        Win4Assert(pOXID);  // must have been at least one
        RemoteReleaseRifRef(pOXID, cRifRef, pRifRefAlloc);
    }

    if (pOXID)
    {
        // Now release the refcnt (if any) we put on the OXIDEntry above
        // to hold it
        DecOXIDRefCnt(pOXID);
    }

    ASSERT_LOCK_HELD
    DbgWalkIPIDs();
    ComDebOut((DEB_MARSHAL, "CStdMarshal::DisconnectCliIPIDs this:%x\n",this));
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::DisconnectSrvIPIDs
//
//  Synopsis:   disconnects the server side IPIDs for this object.
//
//  History:    20-Feb-95   Rickhi  Created.
//
//--------------------------------------------------------------------
void CStdMarshal::DisconnectSrvIPIDs()
{
    ComDebOut((DEB_MARSHAL,
        "CStdMarshal::DisconnectSrvIPIDs this:%x pFirst:%x\n",this, _pFirstIPID));
    Win4Assert(ServerSide());

    // there should be no other threads looking at these IPIDs at this time,
    // since Marshal, Unmarshal, and Dispatch all call PreventDisconnect,
    // Disconnect checks the disconnected flag directly, RMD holds the
    // lock over it's whole execution, RemAddRef and RemRelease hold the
    // lock and check the disconnected flag of the IPIDEntry, and
    // RemQueryInterface calls PreventDisconnect.

    Win4Assert(_dwFlags & SMFLAGS_DISCONNECTED);
    Win4Assert(_cNestedCalls == 0);
    ASSERT_LOCK_HELD


    // while holding the lock, flag each IPID as disconnected so that no
    // more incoming calls are dispatched to this object. We also unchain
    // the IPIDs to ensure that no other threads are pointing at them.

    IPIDEntry *pFirstIPID = _pFirstIPID;
    _pFirstIPID = NULL;

    IPIDEntry *pEntry = pFirstIPID;
    while (pEntry)
    {
        pEntry->dwFlags |= IPIDF_VACANT | IPIDF_DISCONNECTED;

        // release the refcnt on the OXIDEntry and NULL it
        DecOXIDRefCnt(pEntry->pOXIDEntry);
        pEntry->pOXIDEntry = NULL;

        pEntry = pEntry->pNextOID;
    }


    // now release the LOCK since we will be calling into app code to
    // disconnect the stubs, and to release the external connection counts.
    // There should be no other pointers to these IPIDEntries now, so it
    // is safe to muck with their fields (except the dwFlags which is looked
    // at by Dispatch and was already set above).

    UNLOCK
    ASSERT_LOCK_RELEASED

    IPIDEntry *pLastIPID;
    pEntry = pFirstIPID;

    while (pEntry)
    {
        if (pEntry->dwFlags & IPIDF_NOTIFYACT)
        {
            // the activation code asked to be notified when the refcnt
            // on this interface reaches zero. Turn the flag off so we
            // don't call twice.
            pEntry->dwFlags &= ~IPIDF_NOTIFYACT;
            NotifyActivation(FALSE, (IUnknown *)(pEntry->pv));
        }

        if (pEntry->pStub)      // pStub is NULL for IUnknown IPID
        {
            ComDebOut((DEB_MARSHAL, "Disconnect pStub:%x\n", pEntry->pStub));
            ((IUnknown *)pEntry->pv)->Release();
            ((IRpcStubBuffer *)pEntry->pStub)->Disconnect();
            pEntry->pStub->Release();
            pEntry->pStub = NULL;
            pEntry->pv = NULL;
        }

        if (pEntry->cWeakRefs > 0)
        {
            // Release weak references on the StdId.
            pEntry->cWeakRefs = 0;
            _pStdId->Release();
        }

        if (pEntry->cStrongRefs > 0)
        {
            // Release strong references on the StdId. Note that 16bit
            // 16bit OLE always passed fLastReleaseCloses = FALSE in
            // DisconnectObject so we do the same here.

            pEntry->cStrongRefs = 0;
            _pStdId->DecStrongCnt(TRUE);    // fKeepAlive
        }

        if (pEntry->cPrivateRefs > 0)
        {
            // Release private references on the StdId. Note that 16bit
            // 16bit OLE always passed fLastReleaseCloses = FALSE in
            // DisconnectObject so we do the same here.

            pEntry->cPrivateRefs = 0;
            _pStdId->DecStrongCnt(TRUE);    // fKeepAlive
        }
        pLastIPID = pEntry;
        pEntry = pEntry->pNextOID;
    }

    ASSERT_LOCK_RELEASED
    LOCK

    if (pFirstIPID)
    {
        // now we release all entries.
        gIPIDTbl.ReleaseEntryList(pFirstIPID, pLastIPID);
    }

    ASSERT_LOCK_HELD
    DbgWalkIPIDs();
    ComDebOut((DEB_MARSHAL,
        "CStdMarshal::DisconnectSrvIPIDs [OUT] this:%x\n",this));
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::InstantiatedProxy, public
//
//  Synopsis:   return requested interfaces to the caller if instantiated
//
//  History:    20-Feb-95   Rickhi      Created.
//
//--------------------------------------------------------------------
BOOL CStdMarshal::InstantiatedProxy(REFIID riid, void **ppv, HRESULT *phr)
{
    ComDebOut((DEB_MARSHAL,
           "CStdMarshal::InstantiatedProxy this:%x riid:%I ppv:%x\n",
            this, &riid, ppv));
    AssertValid();
    Win4Assert(ClientSide());
    Win4Assert(*ppv == NULL);
    Win4Assert(*phr == S_OK);

    BOOL fRet = FALSE;

    ASSERT_LOCK_RELEASED
    LOCK

    // look for an existing IPIDEntry for the requested interface
    IPIDEntry *pEntry;
    HRESULT hr = FindIPIDEntry(riid, &pEntry);

    if (SUCCEEDED(hr) && pEntry->pv)
    {
        // found the ipid entry, now extract the interface
        // pointer to return to the caller.

        Win4Assert(IsValidInterface(pEntry->pv));
        *ppv = pEntry->pv;
        fRet = TRUE;
    }
    else if (_cIPIDs == 0)
    {
        // no IPIDEntry for the requested interface, and we have never
        // been connected to the server. Return E_NOINTERFACE in this
        // case. This is different from having been connected then
        // disconnected, where we return CO_E_OBJNOTCONNECTED.

        *phr = E_NOINTERFACE;
        Win4Assert(fRet == FALSE);
    }
    else if (_dwFlags & SMFLAGS_PENDINGDISCONNECT)
    {
        // no IPIDEntry for the requested interface and disconnect is
        // pending, so return an error.

        *phr = CO_E_OBJNOTCONNECTED;
        Win4Assert(fRet == FALSE);
    }
    else
    {
        // no IPIDEntry, we are not disconnected, and we do have other
        // instantiated proxies. QueryMultipleInterfaces expects
        // *phr == S_OK and FALSE returned.

        Win4Assert(*phr == S_OK);
        Win4Assert(fRet == FALSE);
    }

    UNLOCK
    ASSERT_LOCK_RELEASED
    ComDebOut((DEB_MARSHAL,
      "CStdMarshal::InstantiatedProxy hr:%x pv:%x fRet:%x\n", *phr, *ppv, fRet));
    return fRet;
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::QueryRemoteInterfaces, public
//
//  Synopsis:   return requested interfaces to the caller if supported
//
//  History:    20-Feb-95   Rickhi      Created.
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::QueryRemoteInterfaces(USHORT cIIDs, IID *pIIDs, SQIResult *pQIRes)
{
    ComDebOut((DEB_MARSHAL,
           "CStdMarshal::QueryRemoteInterfaces this:%x pIIDs:%x pQIRes:%x\n",
            this, pIIDs, pQIRes));
    AssertValid();
    Win4Assert(ClientSide());
    Win4Assert(cIIDs > 0);

    ASSERT_LOCK_RELEASED
    LOCK

    HRESULT hr = PreventDisconnect();

    if (SUCCEEDED(hr))
    {
        // call QI on the remote guy and unmarshal the results
        hr = RemQIAndUnmarshal(cIIDs, pIIDs, pQIRes);
    }
    else
    {
        // cant call out because we're disconnected so return error for
        // each requested interface.
        for (USHORT i=0; i<cIIDs; i++, pQIRes++)
        {
            pQIRes->hr = hr;
        }
    }

    UNLOCK
    ASSERT_LOCK_RELEASED

    // if the object was disconnected while in the middle of the call,
    // then we still return SUCCESS for any interfaces we acquired. The
    // reason is that we do have the proxies, and this matches the
    // behaviour of a QI for an instantiated proxy on a disconnected
    // object.

    hr = HandlePendingDisconnect(hr);

    ComDebOut((DEB_MARSHAL,
       "CStdMarshal::QueryRemoteInterfaces this:%x hr:%x\n", this, hr));
    return hr;
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::RemQIAndUnmarshal, private
//
//  Synopsis:   call QI on remote guy, then unmarshal the STDOBJREF
//              to create the IPID, and return the interface ptr.
//
//  History:    20-Feb-95   Rickhi      Created.
//
//  Notes:      Caller must guarantee at least one IPIDEntry is connected.
//              This function does a sparse fill of the result array.
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::RemQIAndUnmarshal(USHORT cIIDs, IID *pIIDs,
                                       SQIResult *pQIRes)
{
    ComDebOut((DEB_MARSHAL,
        "CStdMarshal::RemQIAndUnmarshal this:%x cIIDs:%x pIIDs:%x pQIRes:%x\n",
            this, cIIDs, pIIDs, pQIRes));
    AssertValid();
    AssertDisconnectPrevented();
    Win4Assert(_pFirstIPID);    // must be at least 1 IPIDEntry
    ASSERT_LOCK_HELD

    // we need an IPID to call RemoteQueryInterface with, any one will
    // do so long as it is connected (in the reconnect case there may be
    // only one connected IPID) so we pick the first one in the chain that
    // is connected.

    IPIDEntry *pIPIDEntry = GetConnectedIPID();

    // remember what type of reference to get since we yield the lock
    // and cant rely on _dwFlags later.
    BOOL fWeakClient = (_dwFlags & SMFLAGS_WEAKCLIENT);

    // call the remote guy
    REMQIRESULT *pRemQiRes = NULL;
    IRemUnknown *pRemUnk;
    HRESULT  hr = GetSecureRemUnk( &pRemUnk, pIPIDEntry->pOXIDEntry );
    if (SUCCEEDED(hr))
    {
        hr = RemoteQueryInterface(pRemUnk, pIPIDEntry, cIIDs, pIIDs, &pRemQiRes,
                                  fWeakClient);
    }

    // need to remember the result ptr so we can free it.
    REMQIRESULT *pRemQiResNext = pRemQiRes;

    // unmarshal each STDOBJREF returned. Note that while we did the
    // RemoteQI we could have yielded (or nested) and did another
    // RemoteQI for the same interfaces, so we have to call UnmarshalIPID
    // which will find any existing IPIDEntry and bump its refcnt.

    HRESULT   hr2;
    HRESULT  *phr = &hr2;
    void     *pv;
    void     **ppv = &pv;

    for (USHORT i=0; i<cIIDs; i++)
    {
        if (pQIRes)
        {
            // caller wants the pointers returned, set ppv and phr.
            ppv = &pQIRes->pv;
            phr = &pQIRes->hr;
            pQIRes++;
        }

        if (SUCCEEDED(hr))
        {
            if (SUCCEEDED(pRemQiResNext->hResult))
            {
                if (fWeakClient)
                {
                    // mark the std objref with the weak reference flag so
                    // that UnmarshalIPID adds the references to the correct
                    // count.
                    pRemQiResNext->std.flags |= SORF_WEAKREF;
                }

                *phr = UnmarshalIPID(*pIIDs, &pRemQiResNext->std,
                                      pIPIDEntry->pOXIDEntry,
                                      (pQIRes) ? ppv : NULL);

                if (FAILED(*phr))
                {
                    // could not unmarshal, release the resources with the
                    // server.
                    RemoteReleaseStdObjRef(&pRemQiResNext->std,
                                           pIPIDEntry->pOXIDEntry);
                }
            }
            else if (pQIRes)
            {
                // the requested interface was not returned so set the
                // return code and interface ptr.
                *phr = pRemQiResNext->hResult;
                *ppv = NULL;
            }

            pIIDs++;
            pRemQiResNext++;
        }
        else
        {
            // the whole call failed so return the error for each
            // requested interface.
            *phr = hr;
            *ppv = NULL;
        }

        // make sure the ptr value is NULL on failure. It may be NULL or
        // non-NULL on success. (ReconnectProxies wants NULL).
        Win4Assert(SUCCEEDED(*phr) || *ppv == NULL);
    }

    // free the result buffer
    CoTaskMemFree(pRemQiRes);

    ASSERT_LOCK_HELD
    AssertDisconnectPrevented();
    ComDebOut((DEB_MARSHAL,
        "CStdMarshal::RemQIAndUnmarshal this:%x hr:%x\n", this, hr));
    return hr;
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::RemIsConnected, private
//
//  Synopsis:   Returns TRUE if most likely connected, FALSE if definitely
//              not connected or pending disconnect.
//
//  History:    20-Feb-95   Rickhi      Created.
//
//--------------------------------------------------------------------
BOOL CStdMarshal::RemIsConnected(void)
{
    AssertValid();
    Assert(ClientSide());

    // the default link depends on us returning FALSE if we are either
    // disconnected or just pending disconnect, in order that they avoid
    // running their cleanup code twice.

    BOOL fRes = (_dwFlags & (SMFLAGS_DISCONNECTED | SMFLAGS_PENDINGDISCONNECT))
              ? FALSE : TRUE;

    ComDebOut((DEB_MARSHAL,
        "CStdMarshal::RemIsConnected this:%x fResult:%x\n", this, fRes));
    return fRes;
}

//+-------------------------------------------------------------------
//
//  Member:     CreateChannel, private
//
//  Synopsis:   Creates an instance of the Rpc Channel.
//
//  History:    20-Feb-95   Rickhi        Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::CreateChannel(OXIDEntry *pOXIDEntry, DWORD dwFlags,
                REFIPID ripid, REFIID riid, CRpcChannelBuffer **ppChnl)
{
    ASSERT_LOCK_HELD
    HRESULT hr = S_OK;

    if (_pChnl == NULL)
    {
        DWORD cState = ServerSide() ? server_cs : client_cs;
        cState |= (dwFlags & SORF_FREETHREADED) ? freethreaded_cs : 0;

        // make a channel. We dont need the call control stuff so just
        // create the base class.

        _pChnl = new CRpcChannelBuffer(_pStdId, pOXIDEntry, cState);

        if (_pChnl == NULL)
        {
            hr = E_OUTOFMEMORY;
        }
    }

    if (SUCCEEDED(hr) && ClientSide())
    {
        *ppChnl = _pChnl->Copy(pOXIDEntry, ripid, riid);
        if (*ppChnl == NULL)
        {
            hr = E_OUTOFMEMORY;
        }
    }
    else
    {
        *ppChnl = _pChnl;
    }

    ASSERT_LOCK_HELD
    return hr;
}

//+-------------------------------------------------------------------
//
//  Member:     GetPSFactory, private
//
//  Synopsis:   loads the proxy/stub factory for given IID
//
//  History:    20-Feb-95   Rickhi        Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::GetPSFactory(REFIID riid, IUnknown *pUnkWow, BOOL fServer,
                                  IPSFactoryBuffer **ppIPSF, BOOL *pfNonNDR)
{
    ComDebOut((DEB_MARSHAL,
        "CStdMarshal::GetPSFactory this:%x riid:%I pUnkWow:%x\n",
         this, &riid, pUnkWow));
    ASSERT_LOCK_RELEASED

    // map iid to classid
    CLSID clsid;
    HRESULT hr = gRIFTbl.RegisterInterface(riid, fServer, &clsid);
#ifdef WX86OLE
    BOOL fWx86 = FALSE;
#endif

    if (SUCCEEDED(hr))
    {
        BOOL fWow = FALSE;

        if (IsWOWThread())
        {
            // figure out if this is a custom interface from a 16bit
            // app, since we have to load the 16bit proxy code if so.

            IThunkManager *pThkMgr;
            g_pOleThunkWOW->GetThunkManager(&pThkMgr);
            Win4Assert(pThkMgr && "pUnk in WOW does not support IThunkManager.");

            if (pUnkWow)
                fWow = pThkMgr->IsCustom3216Proxy(pUnkWow, riid);
            else
                fWow = pThkMgr->IsIIDRequested(riid);

            pThkMgr->Release();
        }

#ifdef WX86OLE
        // If we are in a Wx86 process then we need to determine if the
        // PSFactory needs to be an x86 or native one.
        else if (gcwx86.IsWx86Enabled())
        {
            // Callout to wx86 to ask it to determine if an x86 PS factory
            // is required. Whole32 can tell if the stub needs to be x86
            // by determining if pUnkWow is a custom interface proxy or not.
            // Whole32 can determine if a x86 proxy is required by checking
            // if the riid is one for a custom interface that is expected
            // to be returned.
            fWx86 = gcwx86.NeedX86PSFactory(pUnkWow, riid);
        }
#endif

        // if we are loading a 16bit custom proxy then mark it as non NDR
        *pfNonNDR = (fWow) ? TRUE : FALSE;

        if (IsEqualGUID(clsid, CLSID_PSOlePrx32))
        {
            // its our internal CLSID so go straight to our class factory.
            hr = PrxDllGetClassObject(clsid, IID_IPSFactoryBuffer,
                                      (void **)ppIPSF);
        }
        else
        {
#ifdef WX86OLE
            DWORD dwContext = fWow ? CLSCTX_INPROC_SERVER16
                                   : (fWx86 ? CLSCTX_INPROC_SERVERX86 :
                                              CLSCTX_INPROC_SERVER)
                                                              | CLSCTX_PS_DLL;
#else
            DWORD dwContext = fWow ? CLSCTX_INPROC_SERVER16
                                   : CLSCTX_INPROC_SERVER | CLSCTX_PS_DLL;
#endif

            // load the dll and get the PS class object
            hr = ICoGetClassObject(clsid, dwContext, NULL, IID_IPSFactoryBuffer,
                              (void **)ppIPSF);
#ifdef WX86OLE
            if (fWx86 && FAILED(hr))
            {
                // if we are looking for an x86 PSFactory and we didn't find
                // one on InprocServerX86 key then we need to check
                // InprocServer32 key as well.
                hr = ICoGetClassObject(clsid,
                                      CLSCTX_INPROC_SERVER | CLSCTX_PS_DLL,
                                      NULL, IID_IPSFactoryBuffer,
                                      (void **)ppIPSF);

                if (SUCCEEDED(hr) &&
                    (! gcwx86.IsN2XProxy((IUnknown *)*ppIPSF)))
                {
                    ((IUnknown *)*ppIPSF)->Release();
                    hr = REGDB_E_CLASSNOTREG;
                }
            }
#endif
            AssertOutPtrIface(hr, *ppIPSF);
        }
    }

#if DBG==1
    // if the fake NonNDR flag is set and its the test interface, then
    // trick the code into thinking this is a nonNDR proxy. This is to
    // enable simpler testing of an esoteric feature.

    if (gfFakeNonNDR && IsEqualIID(riid, IID_ICube))
    {
        *pfNonNDR = TRUE;
    }
#endif

    ComDebOut((DEB_MARSHAL,
        "CStdMarshal::GetPSFactory this:%x pIPSF:%x fNonNDR:%x hr:%x\n",
         this, *ppIPSF, *pfNonNDR, hr));

    ASSERT_LOCK_RELEASED
    return hr;
}

//+-------------------------------------------------------------------
//
//  Member:     CreateProxy, private
//
//  Synopsis:   creates an interface proxy for the given interface
//
//  Returns:    [ppv] - interface of type riid, AddRef'd
//
//  History:    20-Feb-95   Rickhi        Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::CreateProxy(REFIID riid, IRpcProxyBuffer **ppProxy,
                                 void **ppv, BOOL *pfNonNDR)
{
    TRACECALL(TRACE_MARSHAL, "CreateProxy");
    ComDebOut((DEB_MARSHAL,
        "CStdMarshal::CreateProxy this:%x riid:%I\n", this, &riid));
    AssertValid();
    Win4Assert(ClientSide());
    Win4Assert(ppProxy != NULL);
    ASSERT_LOCK_HELD

    // get the controlling IUnknown of this object
    IUnknown *punkCtrl = _pStdId->GetCtrlUnk();
    Win4Assert(punkCtrl != NULL);

    if (InlineIsEqualGUID(riid, IID_IUnknown))
    {
        // there is no proxy for IUnknown so we handle that case here
        punkCtrl->AddRef();
        *ppv      = (void **)punkCtrl;
        *ppProxy  = NULL;
        *pfNonNDR = FALSE;
        return S_OK;
    }

    UNLOCK
    ASSERT_LOCK_RELEASED

    // now construct the proxy for the interface
    IPSFactoryBuffer *pIPSF = NULL;
    HRESULT hr = GetPSFactory(riid, NULL, FALSE, &pIPSF, pfNonNDR);

    if (SUCCEEDED(hr))
    {
        // got the class factory, now create an instance
        hr = pIPSF->CreateProxy(punkCtrl, riid, ppProxy, ppv);
        AssertOutPtrIface(hr, *ppProxy);
        pIPSF->Release();
    }

    ASSERT_LOCK_RELEASED
    LOCK

    ComDebOut((DEB_MARSHAL,
        "CStdMarshal::CreateProxy this:%x pProxy:%x pv:%x fNonNDR:%x hr:%x\n",
         this, *ppProxy, *ppv, *pfNonNDR, hr));
    return  hr;
}

//+-------------------------------------------------------------------
//
//  Member:     CreateStub, private
//
//  Synopsis:   creates an interface stub and adds it to the IPID table
//
//  History:    20-Feb-95   Rickhi      Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::CreateStub(REFIID riid, IRpcStubBuffer **ppStub,
                                void **ppv, BOOL *pfNonNDR)
{
    TRACECALL(TRACE_MARSHAL, "CreateStub");
    ComDebOut((DEB_MARSHAL,
        "CStdMarshal::CreateStub this:%x riid:%I\n", this, &riid));
    AssertValid();
    Win4Assert(ServerSide());
    Win4Assert(ppStub != NULL);
    ASSERT_LOCK_HELD

    // get the IUnknown of the object
    IUnknown *punkObj = _pStdId->GetServer();
    Win4Assert(punkObj != NULL);

    if (InlineIsEqualGUID(riid, IID_IUnknown))
    {
        // there is no stub for IUnknown so we handle that here
        *ppv      = (void *)punkObj;
        *ppStub   = NULL;
        *pfNonNDR = FALSE;
        return S_OK;
    }

    UNLOCK
    ASSERT_LOCK_RELEASED

    // make sure the object supports the given interface, so we dont
    // waste a bunch of effort creating a stub if the interface is
    // not supported.

    IUnknown *pUnkIf = NULL;
    HRESULT hr;
#ifdef WX86OLE
    if (gcwx86.IsN2XProxy(punkObj))
    {
        // If we are creating a stub for an object that is living on the
        // x86 side then we need to set the StubInvoke flag to allow QI
        // to thunk the custom interface QI.
        gcwx86.SetStubInvokeFlag((BOOL)1);
    }
#endif
    hr = punkObj->QueryInterface(riid, (void **)&pUnkIf);
    AssertOutPtrIface(hr, pUnkIf);

    if (SUCCEEDED(hr))
    {
        // now construct the stub for the interface
        IPSFactoryBuffer *pIPSF = NULL;
        hr = GetPSFactory(riid, pUnkIf, TRUE, &pIPSF, pfNonNDR);

        if (SUCCEEDED(hr))
        {
            // got the class factory, now create an instance
            hr = pIPSF->CreateStub(riid, punkObj, ppStub);
            AssertOutPtrIface(hr, *ppStub);
            pIPSF->Release();
        }

        if (SUCCEEDED(hr))
        {
            // remember the interface pointer
            *ppv = (void *)pUnkIf;
        }
        else
        {
            // error, release the interface and return NULL
            pUnkIf->Release();
            *ppv = NULL;
        }
    }

    ASSERT_LOCK_RELEASED
    LOCK

    ComDebOut((DEB_MARSHAL,
        "CStdMarshal::CreateStub this:%x pStub:%x pv:%x fNonNDR:%x hr:%x\n",
         this, *ppStub, *ppv, *pfNonNDR, hr));
    return  hr;
}

//+-------------------------------------------------------------------
//
//  Member:     FindIPIDEntry, private
//
//  Synopsis:   Finds an IPIDEntry, chained off this object, with the
//              given riid.
//
//  History:    20-Feb-95   Rickhi  Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::FindIPIDEntry(REFIID riid, IPIDEntry **ppEntry)
{
    ComDebOut((DEB_OXID,"CStdMarshal::FindIPIDEntry ppEntry:%x riid:%I\n",
        ppEntry, &riid));
    ASSERT_LOCK_HELD

    IPIDEntry *pEntry = _pFirstIPID;
    while (pEntry)
    {
        if (InlineIsEqualGUID(riid, pEntry->iid))
        {
            *ppEntry = pEntry;
            return S_OK;
        }

        pEntry = pEntry->pNextOID;      // get next entry in object chain
    }

    ASSERT_LOCK_HELD
    return E_NOINTERFACE;
}

//+-------------------------------------------------------------------
//
//  Member:     FindIPIDEntryByIPID, private
//
//  Synopsis:   returns the IPIDEntry ptr for the given IPID
//
//  History:    20-Feb-95   Rickhi  Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::FindIPIDEntryByIPID(REFIPID ripid, IPIDEntry **ppEntry)
{
    ASSERT_LOCK_HELD

    IPIDEntry *pEntry = _pFirstIPID;
    while (pEntry)
    {
        if (InlineIsEqualGUID(pEntry->ipid, ripid))
        {
            *ppEntry = pEntry;
            return S_OK;
        }

        pEntry = pEntry->pNextOID;      // get next entry in object chain
    }

    ASSERT_LOCK_HELD
    return E_NOINTERFACE;
}

//+-------------------------------------------------------------------
//
//  Member:     FindIPIDEntryByInterface, internal
//
//  Synopsis:   returns the IPIDEntry ptr for the given proxy
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::FindIPIDEntryByInterface(void *pProxy, IPIDEntry **ppEntry)
{
    ASSERT_LOCK_HELD

    IPIDEntry *pEntry = _pFirstIPID;
    *ppEntry          = NULL;
    while (pEntry)
    {
        if (pEntry->pv == pProxy)
        {
            *ppEntry = pEntry;
            break;
        }

        pEntry = pEntry->pNextOID;
    }

    if (*ppEntry != NULL)
        return S_OK;
    else
        return E_NOINTERFACE;
}

//+-------------------------------------------------------------------
//
//  Member:     IncSrvIPIDCnt, protected
//
//  Synopsis:   increments the refcnt on the IPID entry, and optionally
//              AddRefs the StdId.
//
//  History:    20-Feb-95   Rickhi  Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::IncSrvIPIDCnt(IPIDEntry *pEntry, ULONG cRefs,
                                   ULONG cPrivateRefs, SECURITYBINDING *pName,
                                   DWORD mshlflags)
{
    ComDebOut((DEB_MARSHAL, "IncSrvIPIDCnt this:%x pIPID:%x cRefs:%x cPrivateRefs:%x\n",
        this, pEntry, cRefs, cPrivateRefs));
    Win4Assert(ServerSide());
    Win4Assert(pEntry);
    Win4Assert(cRefs > 0 || cPrivateRefs > 0);
    ASSERT_LOCK_HELD

    HRESULT hr = S_OK;

    if (cPrivateRefs != 0)
    {
        // Add a reference.
        hr = gSRFTbl.IncRef( cPrivateRefs, pEntry->ipid, pName );

        if (SUCCEEDED(hr))
        {
            BOOL fNotify = (pEntry->cPrivateRefs == 0) ? TRUE : FALSE;
            pEntry->cPrivateRefs += cPrivateRefs;
            if (fNotify)
            {
                // this inc causes the count to go from zero to non-zero, so we
                // inc the strong count on the stdid to hold it alive until this
                // IPID is released.
                IncStrongAndNotifyAct(pEntry, mshlflags);
            }
        }
    }

    if (SUCCEEDED(hr))
    {
        if (mshlflags & (MSHLFLAGS_TABLESTRONG | MSHLFLAGS_TABLEWEAK))
        {
            // Table Marshal Case: inc the number of table marshals.
            IncTableCnt();
        }

        if (mshlflags & (MSHLFLAGS_WEAK | MSHLFLAGS_TABLEWEAK))
        {
            if (pEntry->cWeakRefs == 0)
            {
                // this inc causes the count to go from zero to non-zero, so we
                // AddRef the stdid to hold it alive until this IPID is released.

                _pStdId->AddRef();
            }
            pEntry->cWeakRefs += cRefs;
        }
        else
        {
            BOOL fNotify = (pEntry->cStrongRefs == 0) ? TRUE : FALSE;
            pEntry->cStrongRefs += cRefs;
            if (fNotify)
            {
                // this inc causes the count to go from zero to non-zero, so we
                // inc the strong count on the stdid to hold it alive until this
                // IPID is released.
                IncStrongAndNotifyAct(pEntry, mshlflags);
            }
        }
    }

    ASSERT_LOCK_HELD
    return hr;
}

//+-------------------------------------------------------------------
//
//  Member:     IncTableCnt, public
//
//  Synopsis:   increments the count of table marshals
//
//  History:    9-Oct-96   Rickhi  Created
//
//--------------------------------------------------------------------
void CStdMarshal::IncTableCnt(void)
{
    ASSERT_LOCK_HELD

    // If something was marshaled for a table, we have to ignore
    // rundowns until a subsequent RMD is called for it, at which
    // time we start paying attention to rundowns again. Since there
    // can be any number of table marshals, we have to refcnt them.

    _cTableRefs++;
    _dwFlags |= SMFLAGS_IGNORERUNDOWN;
}

//+-------------------------------------------------------------------
//
//  Member:     IncStrongAndNotifyAct, private
//
//  Synopsis:   notifies the activation code when this interface refcnt
//              goes from 0 to non-zero and the activation code asked to be
//              notified, and also increments the strong refcnt.
//
//  History:    21-Apr-96   Rickhi  Created
//
//--------------------------------------------------------------------
void CStdMarshal::IncStrongAndNotifyAct(IPIDEntry *pEntry, DWORD mshlflags)
{
    ASSERT_LOCK_HELD

    // inc the strong count on the stdid to hold it alive until this
    // IPIDEntry is released.

    _pStdId->IncStrongCnt();
    if (mshlflags & MSHLFLAGS_NOTIFYACTIVATION &&
        !(pEntry->dwFlags & IPIDF_NOTIFYACT))
    {
        // the activation code asked to be notified when the refcnt
        // on this interface goes positive, and when it reaches
        // zero again. Set a flag so we remember to notify
        // activation when the strong reference reference count
        // goes back down to zero.
        pEntry->dwFlags |= IPIDF_NOTIFYACT;

        UNLOCK
        ASSERT_LOCK_RELEASED
        BOOL fOK = NotifyActivation(TRUE, (IUnknown *)(pEntry->pv));
        ASSERT_LOCK_RELEASED
        LOCK

        if (!fOK)
        {
            // call failed, so dont bother notifying
            pEntry->dwFlags &= ~IPIDF_NOTIFYACT;
        }
    }
}

//+-------------------------------------------------------------------
//
//  Member:     DecSrvIPIDCnt, protected
//
//  Synopsis:   decrements the refcnt on the IPID entry, and optionally
//              Releases the StdId.
//
//  History:    20-Feb-95   Rickhi  Created
//
//--------------------------------------------------------------------
void CStdMarshal::DecSrvIPIDCnt(IPIDEntry *pEntry, ULONG cRefs,
                                ULONG cPrivateRefs, SECURITYBINDING *pName,
                                DWORD mshlflags)
{
    ComDebOut((DEB_MARSHAL, "DecSrvIPIDCnt this:%x pIPID:%x cRefs:%x cPrivateRefs:%x\n",
        this, pEntry, cRefs, cPrivateRefs));
    Win4Assert(ServerSide());
    Win4Assert(pEntry);
    Win4Assert(cRefs > 0 || cPrivateRefs > 0);
    ASSERT_LOCK_HELD

    // Note: we dont care about holding the LOCK over the Release call since
    // the guy who called us is holding a ref to the StdId, so this Release
    // wont cause us to go away.

    if (mshlflags & (MSHLFLAGS_TABLESTRONG | MSHLFLAGS_TABLEWEAK))
    {
        // Table Marshal Case: dec the number of table marshals.
        DecTableCnt();
    }

    if (mshlflags & (MSHLFLAGS_WEAK | MSHLFLAGS_TABLEWEAK))
    {
        Win4Assert(pEntry->cWeakRefs >= cRefs);
        pEntry->cWeakRefs -= cRefs;

        if (pEntry->cWeakRefs == 0)
        {
            // this dec caused the count to go from non-zero to zero, so we
            // Release the stdid since this IPID is no longer holding it alive.
            _pStdId->Release();
        }
    }
    else
    {
        // Adjust the strong reference count.  Don't let the caller release
        // too many times.

        if (pEntry->cStrongRefs < cRefs)
        {
            ComDebOut((DEB_WARN,"DecSrvIPIDCnt too many releases. IPID entry: 0x%x   Extra releases: 0x%x",
                       pEntry, cRefs-pEntry->cStrongRefs));
            cRefs = pEntry->cStrongRefs;
        }
        pEntry->cStrongRefs -= cRefs;

        if (pEntry->cStrongRefs == 0 && cRefs != 0)
        {
            // this dec caused the count to go from non-zero to zero, so we
            // dec the strong count on the stdid since the public references
            // on this IPID is no longer hold it alive.

            DecStrongAndNotifyAct(pEntry, mshlflags);
        }

        // Adjust the secure reference count.  Don't let the caller release
        // too many times.

        if (pName != NULL)
        {
            cPrivateRefs = gSRFTbl.DecRef(cPrivateRefs, pEntry->ipid, pName);
        }
        else
        {
            cPrivateRefs = 0;
        }

        Win4Assert( pEntry->cPrivateRefs >= cPrivateRefs );
        pEntry->cPrivateRefs -= cPrivateRefs;

        if (pEntry->cPrivateRefs == 0 && cPrivateRefs != 0)
        {
            // this dec caused the count to go from non-zero to zero, so we
            // dec the strong count on the stdid since the private references
            // on this IPID is no longer hold it alive.

            DecStrongAndNotifyAct(pEntry, mshlflags);
        }
    }

    ASSERT_LOCK_HELD
}

//+-------------------------------------------------------------------
//
//  Member:     DecTableCnt, public
//
//  Synopsis:   decrements the count of table marshals
//
//  History:    9-Oct-96   Rickhi  Created
//
//--------------------------------------------------------------------
void CStdMarshal::DecTableCnt(void)
{
    ASSERT_LOCK_HELD

    // If something was marshaled for a table, we have to ignore
    // rundowns until a subsequent RMD is called for it, at which
    // time we start paying attention to rundowns again. Since there
    // can be any number of table marshals, we have to refcnt them.
    // This is also used by CoLockObjectExternal.

    if (--_cTableRefs == 0)
    {
        // this was the last table marshal, so now we have to pay
        // attention to rundown from normal clients, so that if all
        // clients go away we cleanup.

        _dwFlags &= ~SMFLAGS_IGNORERUNDOWN;
    }
}

//+-------------------------------------------------------------------
//
//  Member:     DecStrongAndNotifyAct, private
//
//  Synopsis:   notifies the activation code if this interface has
//              been released and the activation code asked to be
//              notified, and also decrements the strong refcnt.
//
//  History:    21-Apr-96   Rickhi  Created
//
//--------------------------------------------------------------------
void CStdMarshal::DecStrongAndNotifyAct(IPIDEntry *pEntry, DWORD mshlflags)
{
    ASSERT_LOCK_HELD
    BOOL fNotifyAct = FALSE;

    if ((pEntry->dwFlags & IPIDF_NOTIFYACT) &&
         pEntry->cStrongRefs == 0  &&
         pEntry->cPrivateRefs == 0)
    {
        // the activation code asked to be notified when the refcnt
        // on this interface reaches zero. Turn the flag off so we
        // don't call twice.
        pEntry->dwFlags &= ~IPIDF_NOTIFYACT;
        fNotifyAct = TRUE;
    }

    UNLOCK
    ASSERT_LOCK_RELEASED

    if (fNotifyAct)
    {
        NotifyActivation(FALSE, (IUnknown *)(pEntry->pv));
    }

    _pStdId->DecStrongCnt(mshlflags & MSHLFLAGS_KEEPALIVE);

    ASSERT_LOCK_RELEASED
    LOCK
}

//+-------------------------------------------------------------------
//
//  Member:     AddIPIDEntry, private
//
//  Synopsis:   Allocates and fills in an entry in the IPID table.
//              The returned entry is not yet in the IPID chain.
//
//  History:    20-Feb-95   Rickhi  Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::AddIPIDEntry(OXIDEntry *pOXIDEntry, IPID *pipid,
               REFIID riid, CRpcChannelBuffer *pChnl, IUnknown *pUnkStub,
               void *pv, IPIDEntry **ppEntry)
{
    ComDebOut((DEB_MARSHAL,"AddIPIDEntry this:%x pOXID:%x iid:%I pStub:%x pv:%x\n",
        this, pOXIDEntry, &riid, pUnkStub, pv));
    ASSERT_LOCK_HELD

    // CODEWORK: while we released the lock to create the proxy or stub,
    // the same interface could have been marshaled/unmarshaled. We should
    // go check for duplicates now. This is just an optimization, not a
    // requirement.

    // get a new entry in the IPID table.
    IPIDEntry *pEntryNew = gIPIDTbl.FirstFree();

    if (pEntryNew == NULL)
    {
        // no free slots and could not allocate more memory to grow
        return E_OUTOFMEMORY;
    }

    if (ServerSide())
    {
        // create an IPID for this entry
        DWORD *pdw = &pipid->Data1;
        *pdw     = gIPIDTbl.GetEntryIndex(pEntryNew);   // IPID table index
        *(pdw+1) = GetCurrentProcessId();               // current PID
        *(pdw+2) = GetCurrentThreadId();                // current TID
        *(pdw+3) = gIPIDSeqNum++;                       // process sequence #
    }

    *ppEntry = pEntryNew;

    pEntryNew->ipid     = *pipid;
    pEntryNew->iid      = riid;
    pEntryNew->pChnl    = pChnl;
    pEntryNew->pStub    = pUnkStub;
    pEntryNew->pv       = pv;
    pEntryNew->dwFlags  = ServerSide() ? IPIDF_SERVERENTRY :
                                         IPIDF_DISCONNECTED | IPIDF_NOPING;
    pEntryNew->cStrongRefs  = 0;
    pEntryNew->cWeakRefs    = 0;
    pEntryNew->cPrivateRefs = 0;
    pEntryNew->pOXIDEntry   = pOXIDEntry;

    ASSERT_LOCK_HELD
    ComDebOut((DEB_MARSHAL,"AddIPIDEntry this:%x pIPIDEntry:%x ipid:%I\n",
        this, pEntryNew, &pEntryNew->ipid));
    return S_OK;
}

//+-------------------------------------------------------------------
//
//  Member:     ReleaseCliIPIDs, private
//
//  Synopsis:   walks the IPID table releasing the proxy/stub entries
//              on the IPIDEntries associated with this Object.
//
//  History:    20-Feb-95   Rickhi  Created
//
//--------------------------------------------------------------------
void CStdMarshal::ReleaseCliIPIDs(void)
{
    Win4Assert(ClientSide());

    ASSERT_LOCK_RELEASED
    LOCK

    // first thing we do is detach the chain of IPIDs from the CStdMarshal
    // while holding the LOCK. Then release the lock and walk the chain
    // releasing the proxy/stub pointers. Note there should not be any other
    // pointers to any of these IPIDs, so it is OK to muck with their state.

    IPIDEntry *pFirstIPID = _pFirstIPID;
    _pFirstIPID = NULL;

    UNLOCK
    ASSERT_LOCK_RELEASED;

    IPIDEntry *pLastIPID;
    IPIDEntry *pEntry = pFirstIPID;

    while (pEntry)
    {
        // mark the entry as vacant and disconnected. Note we dont put
        // it back in the FreeList yet. We leave it chained to the other
        // IPIDs in the list, and add the whole chain to the FreeList at
        // the end.

        pEntry->dwFlags |= IPIDF_VACANT | IPIDF_DISCONNECTED;

        if (pEntry->pStub)
        {
            ComDebOut((DEB_MARSHAL,"ReleaseProxy pProxy:%x\n", pEntry->pStub));
            pEntry->pStub->Release();
            pEntry->pStub = NULL;
        }

        pLastIPID = pEntry;
        pEntry = pEntry->pNextOID;
    }


    if (pFirstIPID != NULL)
    {
        // now take the LOCK again and release all the IPIDEntries back into
        // the IPIDTable in one fell swoop.

        ASSERT_LOCK_RELEASED
        LOCK

        gIPIDTbl.ReleaseEntryList(pFirstIPID, pLastIPID);

        UNLOCK
        ASSERT_LOCK_RELEASED
    }

    ASSERT_LOCK_RELEASED
}

//+------------------------------------------------------------------------
//
//  Member:     CStdMarshal::LockClient/UnLockClient
//
//  Synopsis:   Locks the client side object during outgoing calls in order
//              to prevent the object going away in a nested disconnect.
//
//  Notes:      UnLockClient is not safe in the freethreaded model.
//              Fortunately pending disconnect can only be set in the
//              apartment model on the client side.
//
//  History:    12-Jun-95   Rickhi  Created
//
//-------------------------------------------------------------------------
ULONG CStdMarshal::LockClient(void)
{
    Win4Assert(ClientSide());
    InterlockedIncrement(&_cNestedCalls);
    return (_pStdId->GetCtrlUnk())->AddRef();
}

ULONG CStdMarshal::UnLockClient(void)
{
    Win4Assert(ClientSide());
    if ((InterlockedDecrement(&_cNestedCalls) == 0) &&
        (_dwFlags & SMFLAGS_PENDINGDISCONNECT))
    {
        Disconnect();
    }
    return (_pStdId->GetCtrlUnk())->Release();
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::GetSecureRemUnk, public
//
//  Synopsis:   If the marshaller has its own remote unknown, use it.
//              Otherwise use the OXID's remote unknown.
//
//  History:    2-Apr-96   AlexMit     Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::GetSecureRemUnk( IRemUnknown **ppSecureRemUnk,
                                      OXIDEntry *pOXIDEntry )
{
    ComDebOut((DEB_OXID, "CStdMarshal::GetSecureRemUnk ppRemUnk:%x\n",
               ppSecureRemUnk));

    ASSERT_LOCK_DONTCARE

    if (_pSecureRemUnk != NULL)
    {
        *ppSecureRemUnk = _pSecureRemUnk;
        return S_OK;
    }
    else
    {
        return gOXIDTbl.GetRemUnk( pOXIDEntry, ppSecureRemUnk );
    }
}



//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::LookupStub, private
//
//  Synopsis:   used by the channel to acquire the stub ptr for debugging
//
//  History:    12-Jun-95   Rickhi  Created
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::LookupStub(REFIID riid, IRpcStubBuffer **ppStub)
{
    AssertValid();
    Win4Assert(ServerSide());

    ASSERT_LOCK_RELEASED
    LOCK

    IPIDEntry *pEntry;
    HRESULT hr = FindIPIDEntry(riid, &pEntry);

    if (SUCCEEDED(hr))
    {
        *ppStub = (IRpcStubBuffer *)pEntry->pStub;
    }

    UNLOCK
    ASSERT_LOCK_RELEASED
    return hr;
}


#if DBG==1
//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::GetOXID, private, debug
//
//  Synopsis:   returns the OXID for this object
//
//  History:    20-Feb-95   Rickhi  Created
//
//--------------------------------------------------------------------
REFMOXID CStdMarshal::GetMOXID(void)
{
    ASSERT_LOCK_HELD

    if (ServerSide())
    {
        // local to this apartment, use the local OXID
        return GetLocalOXIDEntry()->moxid;
    }
    else
    {
        Win4Assert(_pChnl);
        return _pChnl->GetMOXID();
    }
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::DbgWalkIPIDs
//
//  Synopsis:   Validates that the state of all the IPIDs is consistent.
//
//  History:    20-Feb-95   Rickhi  Created.
//
//--------------------------------------------------------------------
void CStdMarshal::DbgWalkIPIDs(void)
{
    IPIDEntry *pEntry = _pFirstIPID;
    while (pEntry)
    {
        ValidateIPIDEntry(pEntry);
        pEntry = pEntry->pNextOID;
    }
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::AssertValid
//
//  Synopsis:   Validates that the state of the object is consistent.
//
//  History:    20-Feb-95   Rickhi  Created.
//
//--------------------------------------------------------------------
void CStdMarshal::AssertValid()
{
    LOCK
    Win4Assert((_dwFlags & ~(SMFLAGS_CLIENT_SIDE | SMFLAGS_REGISTEREDOID |
                    SMFLAGS_PENDINGDISCONNECT | SMFLAGS_DISCONNECTED |
                    SMFLAGS_FIRSTMARSHAL | SMFLAGS_HANDLER | SMFLAGS_WEAKCLIENT |
                    SMFLAGS_IGNORERUNDOWN | SMFLAGS_CLIENTMARSHALED |
                    SMFLAGS_NOPING | SMFLAGS_TRIEDTOCONNECT)) == 0);

    Win4Assert(_pStdId  != NULL);
    Win4Assert(IsValidInterface(_pStdId));

    if (_pChnl != NULL)
    {
        Win4Assert(IsValidInterface(_pChnl));
        _pChnl->AssertValid(FALSE, FALSE);
    }

    DbgWalkIPIDs();
    UNLOCK
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::AssertDisconnectPrevented, private
//
//  Synopsis:   Just ensures that no disconnects can/have arrived.
//
//  History:    21-Sep-95   Rickhi      Created
//
//+-------------------------------------------------------------------
void CStdMarshal::AssertDisconnectPrevented()
{
    ASSERT_LOCK_HELD
    if (ServerSide())
        Win4Assert(!(_dwFlags & SMFLAGS_DISCONNECTED));
    Win4Assert(_cNestedCalls > 0);
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::ValidateSTD
//
//  Synopsis:   Ensures that the STDOBJREF is valid
//
//  History:    20-Feb-95   Rickhi  Created.
//
//--------------------------------------------------------------------
void CStdMarshal::ValidateSTD(STDOBJREF *pStd)
{
    LOCK

    // validate the flags field
    Win4Assert((pStd->flags & SORF_RSRVD_MBZ) == 0);

    // validate the OID
    OID oid;
    OIDFromMOID(_pStdId->GetOID(), &oid);
    Win4Assert(pStd->oid == oid);

    if (ServerSide() || _pChnl != NULL)
    {
        // validate the OXID
        OXID oxid;
        OXIDFromMOXID(GetMOXID(), &oxid);
        Win4Assert(pStd->oxid == oxid );
    }

    UNLOCK
}

//+-------------------------------------------------------------------
//
//  Function:   DbgDumpSTD
//
//  Synopsis:   dumps a formated STDOBJREF to the debugger
//
//  History:    20-Feb-95   Rickhi  Created.
//
//--------------------------------------------------------------------
void DbgDumpSTD(STDOBJREF *pStd)
{
    ULARGE_INTEGER *puintOxid = (ULARGE_INTEGER *)&pStd->oxid;
    ULARGE_INTEGER *puintOid  = (ULARGE_INTEGER *)&pStd->oid;

    ComDebOut((DEB_MARSHAL,
        "\n\tpStd:%x   flags:%08x   cPublicRefs:%08x\n\toxid: %08x %08x\n\t oid: %08x %08x\n\tipid:%I\n",
        pStd, pStd->flags, pStd->cPublicRefs, puintOxid->HighPart, puintOxid->LowPart,
        puintOid->HighPart, puintOid->LowPart, &pStd->ipid));
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::ValidateIPIDEntry
//
//  Synopsis:   Ensures that the IPIDEntry is valid
//
//  History:    20-Feb-95   Rickhi  Created.
//
//--------------------------------------------------------------------
void CStdMarshal::ValidateIPIDEntry(IPIDEntry *pEntry)
{
    // ask the table to validate the IPID entry
    gIPIDTbl.ValidateIPIDEntry(pEntry, ServerSide(), _pChnl);
}

//+-------------------------------------------------------------------
//
//  Member:     CStdMarshal::DbgDumpInterfaceList
//
//  Synopsis:   Prints the list of Interfaces on the object.
//
//  History:    20-Feb-95   Rickhi  Created.
//
//--------------------------------------------------------------------
void CStdMarshal::DbgDumpInterfaceList(void)
{
    ComDebOut((DEB_ERROR, "\tInterfaces left on object are:\n"));
    LOCK

    // walk the IPID list printing the friendly name of each interface
    IPIDEntry *pEntry = _pFirstIPID;
    while (pEntry)
    {
        WCHAR wszName[MAX_PATH];
        GetInterfaceName(pEntry->iid, wszName);
        ComDebOut((DEB_ERROR,"\t\t %ws\t cRefs:%x\n",wszName,pEntry->cStrongRefs));
        pEntry = pEntry->pNextOID;
    }

    UNLOCK
}
#endif // DBG == 1

//+-------------------------------------------------------------------
//
//  Function:   RemoteQueryInterface, private
//
//  Synopsis:   call RemoteQueryInterface on remote server.
//
//  History:    20-Feb-95   Rickhi      Created.
//
//--------------------------------------------------------------------
INTERNAL RemoteQueryInterface(IRemUnknown *pRemUnk, IPIDEntry *pIPIDEntry,
                              USHORT cIIDs, IID *pIIDs,
                              REMQIRESULT **ppQiRes, BOOL fWeakClient)
{
    ComDebOut((DEB_MARSHAL,
        "RemoteQueryInterface pIPIDEntry:%x cIIDs:%x, pIIDs:%x riid:%I\n",
         pIPIDEntry, cIIDs, pIIDs, pIIDs));
    Win4Assert(pIPIDEntry->pOXIDEntry);     // must have a resolved oxid
    ASSERT_LOCK_HELD

    // set the IPID according to whether we want strong or weak
    // references. It will only be weak if we are an OLE container
    // and are talking to an embedding running on the same machine.

    IPID ipid = pIPIDEntry->ipid;
    if (fWeakClient)
    {
        ipid.Data1 |= IPIDFLAG_WEAKREF;
    }

    UNLOCK
    ASSERT_LOCK_RELEASED

    HRESULT hr = pRemUnk->RemQueryInterface(ipid, REM_ADDREF_CNT,
                                            cIIDs, pIIDs, ppQiRes);
    ASSERT_LOCK_RELEASED
    LOCK

    ASSERT_LOCK_HELD
    ComDebOut((DEB_MARSHAL, "RemoteQueryInterface hr:%x pQIRes:%x\n",
               hr, *ppQiRes));
    return hr;
}

//+-------------------------------------------------------------------
//
//  Function:   RemoteAddRef, private
//
//  Synopsis:   calls the remote server to AddRef one of its interfaces
//
//  History:    20-Feb-95   Rickhi      Created.
//
//--------------------------------------------------------------------
INTERNAL RemoteAddRef(IPIDEntry *pIPIDEntry, OXIDEntry *pOXIDEntry,
                      ULONG cStrongRefs, ULONG cSecureRefs)
{
    ComDebOut((DEB_MARSHAL,
        "RemoteAddRef cRefs:%x cSecure:%x ipid:%I\n",
        cStrongRefs, cSecureRefs, &pIPIDEntry->ipid));
    ASSERT_LOCK_HELD

    // if the object does not require pinging, it is also ignoring
    // reference counts, so there is no need to go get more, just
    // pretend like we did.

    if (pIPIDEntry->dwFlags & IPIDF_NOPING)
    {
        return S_OK;
    }

    // get the IRemUnknown for the remote server
    IRemUnknown *pRemUnk;
    HRESULT hr = gOXIDTbl.GetRemUnk(pOXIDEntry, &pRemUnk);

    if (SUCCEEDED(hr))
    {
        // call RemAddRef on the interface
        REMINTERFACEREF rifRef;
        rifRef.ipid         = pIPIDEntry->ipid;
        rifRef.cPublicRefs  = cStrongRefs;
        rifRef.cPrivateRefs = cSecureRefs;

        UNLOCK
        ASSERT_LOCK_RELEASED

        HRESULT ignore;
        hr = pRemUnk->RemAddRef(1, &rifRef, &ignore);

        ASSERT_LOCK_RELEASED
        LOCK
    }

    ASSERT_LOCK_HELD
    ComDebOut((DEB_MARSHAL, "RemoteAddRef hr:%x\n", hr));
    return hr;
}

//+-------------------------------------------------------------------
//
//  Function:   RemoteReleaseRifRef
//
//  Synopsis:   calls the remote server to release some IPIDs
//
//  History:    20-Feb-95   Rickhi      Created.
//
//--------------------------------------------------------------------
INTERNAL RemoteReleaseRifRef(OXIDEntry *pOXIDEntry,
                            USHORT cRifRef, REMINTERFACEREF *pRifRef)
{
    Win4Assert(pRifRef);
    ComDebOut((DEB_MARSHAL,
        "RemoteRelease pOXID:%x cRifRef:%x pRifRef:%x cRefs:%x ipid:%I\n",
         pOXIDEntry, cRifRef, pRifRef, pRifRef->cPublicRefs, &pRifRef->ipid));
    Win4Assert(pOXIDEntry);
    ASSERT_LOCK_HELD

    HRESULT hr;

    if (IsSTAThread() &&
        FAILED(CanMakeOutCall(CALLCAT_SYNCHRONOUS, IID_IRundown)))
    {
        // the call control will not let this apartment model thread make
        // the outgoing release call (cause we're inside an InputSync call)
        // so we post ourselves a message to do it later.

        hr = PostReleaseRifRef(pOXIDEntry, cRifRef, pRifRef);
    }
    else
    {
        // get the IRemUnknown for the remote server
        IRemUnknown *pRemUnk;
        hr = gOXIDTbl.GetRemUnk(pOXIDEntry, &pRemUnk);

        if (SUCCEEDED(hr))
        {
            UNLOCK
            ASSERT_LOCK_RELEASED
            hr = pRemUnk->RemRelease(cRifRef, pRifRef);
            ASSERT_LOCK_RELEASED
            LOCK
        }
    }

    ComDebOut((DEB_MARSHAL, "RemoteRelease hr:%x\n", hr));
    ASSERT_LOCK_HELD
    return hr;
}

//+-------------------------------------------------------------------
//
//  Function:   PostReleaseRifRef
//
//  Synopsis:   Post a message to ourself to call RemoteReleaseRifRef later.
//              This is used to make a synchronous remote Release call when
//              a Release is done inside of an InputSync call. The call is
//              delayed until we are out of the InputSync call, since the
//              call control wont allow a synch call inside an inputsync call.
//
//  History:    05-Apr-96   Rickhi      Created.
//
//--------------------------------------------------------------------
INTERNAL PostReleaseRifRef(OXIDEntry *pOXIDEntry,
                           USHORT cRifRef, REMINTERFACEREF *pRifRef)
{
    Win4Assert(pRifRef);
    ComDebOut((DEB_MARSHAL,
        "PostRelease pOXID:%x cRifRef:%x pRifRef:%x cRefs:%x ipid:%I\n",
         pOXIDEntry, cRifRef, pRifRef, pRifRef->cPublicRefs, &pRifRef->ipid));
    Win4Assert(pOXIDEntry);
    ASSERT_LOCK_HELD

    OXIDEntry *pLocalOXIDEntry = NULL;
    HRESULT hr = gOXIDTbl.GetLocalEntry(&pLocalOXIDEntry);

    if (SUCCEEDED(hr))
    {
        // allocate a structure to hold the data and copy in the RifRef
        // list, OXIDEntry, and count of entries. Inc the OXID RefCnt to
        // ensure it stays alive until the posted message is processed.

        hr = E_OUTOFMEMORY;
        ULONG cbRifRef = cRifRef * sizeof(REMINTERFACEREF);
        ULONG cbAlloc  = sizeof(POSTRELRIFREF) + (cbRifRef-1);
        POSTRELRIFREF *pRelRifRef = (POSTRELRIFREF *) PrivMemAlloc(cbAlloc);

        if (pRelRifRef)
        {
            IncOXIDRefCnt(pOXIDEntry);              // keep alive
            pRelRifRef->pOXIDEntry = pOXIDEntry;
            pRelRifRef->cRifRef    = cRifRef;
            memcpy(&pRelRifRef->arRifRef, pRifRef, cbRifRef);

            if (!PostMessage((HWND)pLocalOXIDEntry->hServerSTA,
                             WM_OLE_ORPC_RELRIFREF,
                             GetCurrentThreadId(),
                             (LPARAM)pRelRifRef))
            {
                // Post failed, free the structure and report an error.
                DecOXIDRefCnt(pOXIDEntry);
                PrivMemFree(pRelRifRef);
                hr = RPC_E_SYS_CALL_FAILED;
            }
        }
    }

    ComDebOut((DEB_MARSHAL, "PostRelease hr:%x\n", hr));
    ASSERT_LOCK_HELD
    return hr;
}

//+-------------------------------------------------------------------
//
//  Function:   HandlePostReleaseRifRef
//
//  Synopsis:   Handles the ReleaseRifRef message that was posted to the
//              current thread (by the current thread) in order to do a
//              delayed remote release call. See PostReleaseRifRef above.
//
//  History:    05-Apr-96   Rickhi      Created.
//
//--------------------------------------------------------------------
INTERNAL HandlePostReleaseRifRef(LPARAM param)
{
    Win4Assert(param);
    ComDebOut((DEB_MARSHAL, "HandlePostRelease pRifRef:%x\n", param));
    POSTRELRIFREF *pRelRifRef = (POSTRELRIFREF *)param;

    ASSERT_LOCK_RELEASED
    LOCK

    // simply make the real remote release call now, then release the
    // reference we have on the OXIDEntry, and free the message buffer.
    // If this call fails, dont try again, otherwise we could spin busy
    // waiting. Instead, just let Rundown clean up the server.

    RemoteReleaseRifRef(pRelRifRef->pOXIDEntry,
                        pRelRifRef->cRifRef,
                        &pRelRifRef->arRifRef);

    DecOXIDRefCnt(pRelRifRef->pOXIDEntry);

    UNLOCK
    ASSERT_LOCK_RELEASED

    PrivMemFree(pRelRifRef);
    ComDebOut((DEB_MARSHAL, "HandlePostRelease hr:%x\n", S_OK));
    return S_OK;
}

//+-------------------------------------------------------------------
//
//  Member:     RemoteChangeRef
//
//  Synopsis:   calls the remote server to convert interface refereces
//              from strong to weak or vise versa. This behaviour is
//              required to support silent updates in the OLE container /
//              link / embedding scenarios.
//
//  Notes:      This functionality is not exposed in FreeThreaded apps
//              or in remote apps. The implication being that the container
//              must be on the same machine as the embedding.
//
//  History:    20-Nov-95   Rickhi      Created.
//
//--------------------------------------------------------------------
HRESULT CStdMarshal::RemoteChangeRef(BOOL fLock, BOOL fLastUnlockReleases)
{
    ComDebOut((DEB_MARSHAL, "RemoteChangeRef \n"));
    Win4Assert(ClientSide());
    Win4Assert(IsSTAThread()); // not allowed in MTA Apartment
    ASSERT_LOCK_RELEASED

    // must be at least 1 proxy already connected in order to be able
    // to do this. We cant just ASSERT that's true because we were not
    // holding the lock on entry.

    LOCK
    HRESULT hr = PreventDisconnect();

    // A previous version of OLE set the object to weak even it it was
    // currently disconnected, and it remembered that it was weak and set
    // any new interfaces that it later accquired to weak. I emulate that
    // behaviour here.

    if (fLock)
        _dwFlags &= ~SMFLAGS_WEAKCLIENT;
    else
        _dwFlags |= SMFLAGS_WEAKCLIENT;


    if (SUCCEEDED(hr))
    {
        REMINTERFACEREF *pRifRefAlloc = (REMINTERFACEREF *)
                               _alloca(_cIPIDs * sizeof(REMINTERFACEREF));
        REMINTERFACEREF *pRifRef = pRifRefAlloc;

        DWORD      cSecure    = gCapabilities & EOAC_SECURE_REFS ? 1 : 0;
        USHORT     cIIDs      = 0;
        OXIDEntry *pOXIDEntry = NULL;
        IPIDEntry *pNextIPID  = _pFirstIPID;

        while (pNextIPID)
        {
            if (!(pNextIPID->dwFlags & IPIDF_DISCONNECTED))
            {
                if (pOXIDEntry == NULL)
                {
                    // This is the first connected IPID we encountered.
                    // Get its OXID entry and make sure it is for a server
                    // process on the current machine.

                    if (!(pNextIPID->pOXIDEntry->dwFlags &
                          OXIDF_MACHINE_LOCAL))
                    {
                        // OXID is for a remote process. Abandon this call.
                        Win4Assert(cIIDs == 0);         // skip call below
                        Win4Assert(pOXIDEntry == NULL); // dont dec below
                        Win4Assert(hr == S_OK);         // report success
                        break;                          // exit while loop
                    }

                    // Remember the OXID and AddRef it to keep it alive
                    // over the duration of the call.

                    pOXIDEntry = pNextIPID->pOXIDEntry;
                    IncOXIDRefCnt(pOXIDEntry);
                }

                pRifRef->ipid = pNextIPID->ipid;

                if (!fLock && pNextIPID->cStrongRefs > 0)
                {
                        pRifRef->cPublicRefs    = pNextIPID->cStrongRefs;
                        pRifRef->cPrivateRefs   = pNextIPID->cPrivateRefs;
                        pNextIPID->cWeakRefs   += pNextIPID->cStrongRefs;
                        pNextIPID->cStrongRefs  = 0;
                        pNextIPID->cPrivateRefs = 0;

                        pRifRef++;
                        cIIDs++;
                }
                else if (fLock && pNextIPID->cStrongRefs == 0)
                {
                        pRifRef->cPublicRefs    = pNextIPID->cWeakRefs;
                        pRifRef->cPrivateRefs   = cSecure;
                        pNextIPID->cStrongRefs += pNextIPID->cWeakRefs;
                        pNextIPID->cWeakRefs    = 0;
                        pNextIPID->cPrivateRefs = cSecure;

                        pRifRef++;
                        cIIDs++;
                }
            }

            // get next IPIDentry for this object
            pNextIPID = pNextIPID->pNextOID;
        }

        if (cIIDs != 0)
        {
            // we have looped filling in the IPID list, and there are
            // entries in the list. go call the server now. First, set up
            // the flags, then reset the RifRef pointer since we trashed
            // it while walking the list above.

            DWORD dwFlags = (fLock) ? IRUF_CONVERTTOSTRONG : IRUF_CONVERTTOWEAK;
            if (fLastUnlockReleases)
                dwFlags |= IRUF_DISCONNECTIFLASTSTRONG;

            hr = RemoteChangeRifRef(pOXIDEntry, dwFlags, cIIDs, pRifRefAlloc);
        }

        if (pOXIDEntry)
        {
            // release the OXIDEntry
            DecOXIDRefCnt(pOXIDEntry);
        }
    }
    else
    {
        // A previous implementation of OLE returned S_OK if the object was
        // disconnected. I emulate that behaviour here.

        hr = S_OK;
    }

    DbgWalkIPIDs();
    UNLOCK
    ASSERT_LOCK_RELEASED

    // this will handle any Disconnect that came in while we were busy.
    hr = HandlePendingDisconnect(hr);

    ComDebOut((DEB_MARSHAL, "RemoteChangeRef hr:%x\n", hr));
    ASSERT_LOCK_RELEASED
    return hr;
}

//+-------------------------------------------------------------------
//
//  Function:   RemoteChangeRifRef
//
//  Synopsis:   calls the remote server to release some IPIDs
//
//  History:    20-Feb-95   Rickhi      Created.
//
//--------------------------------------------------------------------
INTERNAL RemoteChangeRifRef(OXIDEntry *pOXIDEntry, DWORD dwFlags,
                            USHORT cRifRef, REMINTERFACEREF *pRifRef)
{
    Win4Assert(pRifRef);
    ComDebOut((DEB_MARSHAL,
        "RemoteChangeRifRef pOXID:%x cRifRef:%x pRifRef:%x cRefs:%x ipid:%I\n",
         pOXIDEntry, cRifRef, pRifRef, pRifRef->cPublicRefs, &(pRifRef->ipid)));
    Win4Assert(pOXIDEntry);
    ASSERT_LOCK_HELD

    // get the IRemUnknown for the remote server
    IRemUnknown *pRemUnk;
    HRESULT hr = gOXIDTbl.GetRemUnk(pOXIDEntry, &pRemUnk);

    if (SUCCEEDED(hr))
    {
        UNLOCK
        ASSERT_LOCK_RELEASED
        hr = ((IRemUnknown2 *)pRemUnk)->RemChangeRef(dwFlags, cRifRef, pRifRef);
        ASSERT_LOCK_RELEASED
        LOCK
    }

    ComDebOut((DEB_MARSHAL, "RemoteChangeRifRef hr:%x\n", hr));
    ASSERT_LOCK_HELD
    return hr;
}

//+-------------------------------------------------------------------
//
//  Function:   RemoteReleaseStdObjRef
//
//  Synopsis:   calls the remote server to release an ObjRef
//
//  History:    20-Feb-95   Rickhi      Created.
//
//--------------------------------------------------------------------
INTERNAL RemoteReleaseStdObjRef(STDOBJREF *pStd, OXIDEntry *pOXIDEntry)
{
    ComDebOut((DEB_MARSHAL, "RemoteReleaseStdObjRef pStd:%x\n pOXIDEntry:%x",
              pStd, pOXIDEntry));
    ASSERT_LOCK_HELD

    REMINTERFACEREF rifRef;
    rifRef.ipid         = pStd->ipid;
    rifRef.cPublicRefs  = pStd->cPublicRefs;
    rifRef.cPrivateRefs = 0;

    // incase we get disconnected while in the RemRelease call
    // we need to extract the OXIDEntry and AddRef it.

    IncOXIDRefCnt(pOXIDEntry);
    RemoteReleaseRifRef(pOXIDEntry, 1, &rifRef);
    DecOXIDRefCnt(pOXIDEntry);

    ComDebOut((DEB_MARSHAL, "RemoteReleaseStdObjRef hr:%x\n", S_OK));
    ASSERT_LOCK_HELD
    return S_OK;
}

//+-------------------------------------------------------------------
//
//  Function:   RemoteReleaseObjRef
//
//  Synopsis:   calls the remote server to release an ObjRef
//
//  History:    20-Feb-95   Rickhi      Created.
//
//--------------------------------------------------------------------
INTERNAL RemoteReleaseObjRef(OBJREF &objref)
{
    return RemoteReleaseStdObjRef(&ORSTD(objref).std, GetOXIDFromObjRef(objref));
}

//+-------------------------------------------------------------------
//
//  Function:   GetOXIDFromObjRef, private
//
//  Synopsis:   extracts the OXID from the OBJREF.
//
//  History:    09-Jan-96   Rickhi      Created.
//
//--------------------------------------------------------------------
OXIDEntry *GetOXIDFromObjRef(OBJREF &objref)
{
    // TRICK: Internally we use the saResAddr.size field as the ptr
    // to the OXIDEntry. See ReadObjRef and FillObjRef.

    OXIDEntry *pOXIDEntry = (objref.flags & OBJREF_STANDARD)
                          ? *(OXIDEntry **)&ORSTD(objref).saResAddr
                          : *(OXIDEntry **)&ORHDL(objref).saResAddr;

    Win4Assert(pOXIDEntry);
    return pOXIDEntry;
}

//+-------------------------------------------------------------------
//
//  Function:   WriteObjRef, private
//
//  Synopsis:   Writes the objref into the stream
//
//  History:    20-Feb-95  Rickhi       Created.
//
//--------------------------------------------------------------------
INTERNAL WriteObjRef(IStream *pStm, OBJREF &objref, DWORD dwDestCtx)
{
    ASSERT_LOCK_RELEASED

    ULONG cbToWrite = (objref.flags & OBJREF_STANDARD)
                    ? (2*sizeof(ULONG)) + sizeof(IID) + sizeof(STDOBJREF)
                    : (2*sizeof(ULONG)) + sizeof(IID) + sizeof(STDOBJREF) + sizeof(CLSID);

    // write the fixed-sized part of the OBJREF into the stream
    HRESULT hr = pStm->Write(&objref, cbToWrite, NULL);

    if (SUCCEEDED(hr))
    {
        // write the resolver address into the stream.
        // TRICK: Internally we use the saResAddr.size field as the ptr
        // to the OXIDEntry. See ReadObjRef and FillObjRef.

        DUALSTRINGARRAY *psa;
        OXIDEntry *pOXIDEntry = GetOXIDFromObjRef(objref);

        if (pOXIDEntry->pMIDEntry != gpLocalMIDEntry ||
            dwDestCtx == MSHCTX_DIFFERENTMACHINE)
        {
            // the interface is for a remote server, or it is going to a
            // remote client, therefore, marshal the resolver strings
            psa = pOXIDEntry->pMIDEntry->Node.psaKey;
            Win4Assert(psa->wNumEntries != 0);
        }
        else
        {
            // the interface is for an OXID local to this machine and
            // the interface is not going to a remote client, marshal an
            // empty string (we pay attention to this in ReadObjRef)
            psa = &saNULL;
        }

        // These string bindings always come from the object exporter
        // who has already padded the size to 8 bytes.
        hr = pStm->Write(psa, SASIZE(psa->wNumEntries), NULL);

        ComDebOut((DEB_MARSHAL,"WriteObjRef psa:%x\n", psa));
    }

    ASSERT_LOCK_RELEASED
    return hr;
}

//+-------------------------------------------------------------------
//
//  Function:   ReadObjRef, private
//
//  Synopsis:   Reads the objref from the stream
//
//  History:    20-Feb-95  Rickhi       Created.
//
//--------------------------------------------------------------------
INTERNAL ReadObjRef(IStream *pStm, OBJREF &objref)
{
    ASSERT_LOCK_RELEASED

    // read the signature, flags, and iid fields of the objref so we know
    // what kind of objref we are dealing with and how big it is.

    HRESULT hr = StRead(pStm, &objref, 2*sizeof(ULONG) + sizeof(IID));

    if (SUCCEEDED(hr))
    {
        if ((objref.signature != OBJREF_SIGNATURE) ||
            (objref.flags & OBJREF_RSRVD_MBZ)      ||
            (objref.flags == 0))
        {
            // the objref signature is bad, or one of the reserved
            // bits in the flags is set, or none of the required bits
            // in the flags is set. the objref cant be interpreted so
            // fail the call.

            Win4Assert(!"Invalid Objref Flags");
            return RPC_E_INVALID_OBJREF;
        }

        // compute the size of the remainder of the objref and
        // include the size fields for the resolver string array

        STDOBJREF       *pStd = &ORSTD(objref).std;
        DUALSTRINGARRAY *psa;
        ULONG           cbToRead;

        if (objref.flags & OBJREF_STANDARD)
        {
            cbToRead = sizeof(STDOBJREF) + sizeof(ULONG);
            psa = &ORSTD(objref).saResAddr;
        }
        else if (objref.flags & OBJREF_HANDLER)
        {
            cbToRead = sizeof(STDOBJREF) + sizeof(CLSID) + sizeof(ULONG);
            psa = &ORHDL(objref).saResAddr;
        }
        else if (objref.flags & OBJREF_CUSTOM)
        {
            cbToRead = sizeof(CLSID) + 2*sizeof(DWORD);  // clsid + cbExtension + size
            psa = NULL;
        }

        // read the rest of the (fixed sized) objref from the stream
        hr = StRead(pStm, pStd, cbToRead);

        if (SUCCEEDED(hr))
        {
            if (psa != NULL)
            {
                // Non custom interface. Make sure the resolver string array
                // has some sensible values.
                if (psa->wNumEntries != 0 &&
                    psa->wSecurityOffset >= psa->wNumEntries)
                {
                    hr = RPC_E_INVALID_OBJREF;
                }
            }
            else
            {
                // custom marshaled interface
                if (ORCST(objref).cbExtension != 0)
                {
                    // skip past the extensions since we currently dont
                    // know about any extension types.
                    LARGE_INTEGER dlibMove;
                    dlibMove.LowPart  = ORCST(objref).cbExtension;
                    dlibMove.HighPart = 0;
                    hr = pStm->Seek(dlibMove, STREAM_SEEK_CUR, NULL);
                }
            }
        }

        if (SUCCEEDED(hr) && psa)
        {
            // Non custom interface. The data that follows is a variable
            // sized string array. Allocate memory for it and then read it.

            DbgDumpSTD(pStd);
            DUALSTRINGARRAY *psaNew;

            cbToRead = psa->wNumEntries * sizeof(WCHAR);
            if (cbToRead == 0)
            {
                // server must be local to this machine, just get the local
                // resolver strings and use them to resolve the OXID
                psaNew = gpsaLocalResolver;
            }
            else
            {
                // allocate space to read the strings
                psaNew = (DUALSTRINGARRAY *) _alloca(cbToRead + sizeof(ULONG));
                if (psaNew != NULL)
                {
                    // update the size fields and read in the rest of the data
                    psaNew->wSecurityOffset = psa->wSecurityOffset;
                    psaNew->wNumEntries = psa->wNumEntries;

                    hr = StRead(pStm, psaNew->aStringArray, cbToRead);
                }
                else
                {
                    psa->wNumEntries     = 0;
                    psa->wSecurityOffset = 0;
                    hr = E_OUTOFMEMORY;

                    // seek the stream past what we should have read, ignore
                    // seek errors, since the OOM takes precedence.

                    LARGE_INTEGER libMove;
                    libMove.LowPart  = cbToRead;
                    libMove.HighPart = 0;
                    pStm->Seek(libMove, STREAM_SEEK_CUR, 0);
                }
            }

            // TRICK: internally we want to keep the ObjRef a fixed size
            // structure, even though we have variable sized data. To do
            // this i use the saResAddr.size field of the ObjRef as a ptr
            // to the OXIDEntry. We pay attention to this in FillObjRef,
            // WriteObjRef and FreeObjRef.

            if (SUCCEEDED(hr))
            {
                // resolve the OXID.
                ASSERT_LOCK_RELEASED
                LOCK
                OXIDEntry *pOXIDEntry = NULL;
                hr = gResolver.ClientResolveOXID(pStd->oxid,
                                                 psaNew, &pOXIDEntry);
                UNLOCK
                ASSERT_LOCK_RELEASED
                *((void **) psa) = pOXIDEntry;
            }
            else
            {
                *((void **) psa) = NULL;
            }
        }
    }

    ComDebOut((DEB_MARSHAL,"ReadObjRef hr:%x objref:%x\n", hr, &objref));
    ASSERT_LOCK_RELEASED
    return hr;
}

//+-------------------------------------------------------------------
//
//  Function:   FreeObjRef, private
//
//  Synopsis:   Releases an objref that was read in from a stream via
//              ReadObjRef.
//
//  History:    20-Feb-95  Rickhi       Created.
//
//  Notes:      Anybody who calls ReadObjRef should call this guy to
//              free the objref. This decrements the refcnt on the
//              embedded pointer to the OXIDEntry.
//
//--------------------------------------------------------------------
INTERNAL_(void) FreeObjRef(OBJREF &objref)
{
    if (objref.flags & (OBJREF_STANDARD | OBJREF_HANDLER))
    {
        // TRICK: Internally we use the saResAddr.size field as the ptr to
        // the OXIDEntry. See ReadObjRef, WriteObjRef and FillObjRef.

        OXIDEntry *pOXIDEntry = GetOXIDFromObjRef(objref);

        LOCK
        Win4Assert(pOXIDEntry);
        DecOXIDRefCnt(pOXIDEntry);
        UNLOCK
    }
}

//+-------------------------------------------------------------------
//
//  Function:   MakeFakeObjRef, private
//
//  Synopsis:   Invents an OBJREF that can be unmarshaled in this process.
//              The objref is partially fact (the OXIDEntry) and partially
//              fiction (the OID).
//
//  History:    16-Jan-96   Rickhi      Created.
//
//  Notes:      This is used by MakeSCMProxy and GetRemUnk. Note that
//              the pOXIDEntry is not AddRef'd here because the OBJREF
//              created is only short-lived the callers guarantee it's
//              lifetime, so FreeObjRef need not be called.
//
//--------------------------------------------------------------------
INTERNAL MakeFakeObjRef(OBJREF &objref, OXIDEntry *pOXIDEntry,
                        REFIPID ripid, REFIID riid)
{
    // first, invent an OID since this could fail.

    STDOBJREF *pStd = &ORSTD(objref).std;
    HRESULT hr = gResolver.ServerGetReservedID(&pStd->oid);

    if (SUCCEEDED(hr))
    {
        pStd->flags           = SORF_NOPING | SORF_FREETHREADED;
        pStd->cPublicRefs     = 1;
        pStd->ipid            = ripid;
        OXIDFromMOXID(pOXIDEntry->moxid, &pStd->oxid);

        // TRICK: Internally we use the saResAddr.size field as the ptr to
        // the OXIDEntry. See ReadObjRef, WriteObjRef and FillObjRef.

        OXIDEntry **ppOXIDEntry = (OXIDEntry **) &ORSTD(objref).saResAddr;
        *ppOXIDEntry = pOXIDEntry;

        objref.signature = OBJREF_SIGNATURE;
        objref.flags     = OBJREF_STANDARD;
        objref.iid       = riid;
    }

    return hr;
}

//+-------------------------------------------------------------------
//
//  Function:   MakeCallableFromAnyApt, private
//
//  Synopsis:   set SORF_FREETHREADED in OBJREF so unmarshaled proxy
//              can be called from any apartment.
//
//  History:    16-Jan-96   Rickhi      Created.
//
//--------------------------------------------------------------------
void MakeCallableFromAnyApt(OBJREF &objref)
{
    STDOBJREF *pStd = &ORSTD(objref).std;
    pStd->flags |= SORF_FREETHREADED;
}

//+-------------------------------------------------------------------
//
//  Function:   FindStdMarshal, private
//
//  Synopsis:   Finds the CStdMarshal for the OID read from the stream
//
//  Arguements: [objref] - object reference
//              [ppStdMshl] - CStdMarshal returned, AddRef'd
//
//  Algorithm:  Read the objref, get the OID. If we already have an identity
//              for this OID, use that, otherwise either create an identity
//              object, or create a handler (which in turn will create the
//              identity).  The identity inherits CStdMarshal.
//
//  History:    20-Feb-95   Rickhi      Created.
//
//--------------------------------------------------------------------
INTERNAL FindStdMarshal(OBJREF &objref, CStdMarshal **ppStdMshl)
{
    ComDebOut((DEB_MARSHAL,
        "FindStdMarshal objref:%x ppStdMshl:%x\n", &objref, ppStdMshl));

    HRESULT hr = CO_E_OBJNOTCONNECTED;
    CStdIdentity *pStdId = NULL;

    if (ChkIfLocalOID(objref, &pStdId))
    {
        if (pStdId)
        {
            hr = S_OK;
        }
        else
        {
            hr = CO_E_OBJNOTCONNECTED;
        }
    }
    else
    {
        STDOBJREF *pStd = &ORSTD(objref).std;
        ComDebOut((DEB_MARSHAL, "poid: %x\n", &pStd->oid));

        ASSERT_LOCK_RELEASED
        LOCK

        OXIDEntry *pOXIDEntry = GetOXIDFromObjRef(objref);

        // OXID is for different apartment, check the identity table for
        // an existing OID.

        MOID moid;
        MOIDFromOIDAndMID(pStd->oid, pOXIDEntry->pMIDEntry->mid, &moid);

        hr = LookupIDFromID(moid, TRUE, &pStdId);

        if (FAILED(hr))
        {
            CStdIdentity *pStdIdPrev = NULL;
            BOOL fDuplicate = FALSE;

            if (objref.flags & OBJREF_STANDARD)
            {
                // create an instance of the identity for this OID. We want
                // to be holding the lock while we do this since it wont
                // exercise any app code.

                hr = CreateIdentityHandler(NULL, pStd->flags,
                                           IID_IStdIdentity, (void **)&pStdId);
                AssertOutPtrIface(hr, pStdId);

                if (SUCCEEDED(hr))
                {
                    // set the identity while holding the lock. The result is
                    // checked below and we release if this fails.

                    hr = pStdId->SetOID(moid);
                    Win4Assert(pStdIdPrev == NULL);
                }
            }
            else
            {
                // create an instance of the handler. the handler will
                // aggregate in the identity, but will pass GUID_NULL for
                // the OID so that the identity is not set in the table yet.

                Win4Assert(!(ORHDL(objref).std.flags & SORF_FREETHREADED));

                // dont want to hold the lock while creating the handler
                // since this involves running app code and calling the
                // SCM etc.

                UNLOCK
                ASSERT_LOCK_RELEASED

                hr = CoCreateInstance(ORHDL(objref).clsid, NULL,
                           CLSCTX_INPROC_HANDLER,
                           IID_IStdIdentity, (void **)&pStdId);

                AssertOutPtrIface(hr, pStdId);

                ASSERT_LOCK_RELEASED
                LOCK

                // look for the OID in the table again, since it may have
                // been added while we released the lock to create the
                // handler.

                if (SUCCEEDED(LookupIDFromID(moid, TRUE, &pStdIdPrev)))
                {
                    // object was unmarshaled while we released the lock
                    // to create the handler, so we will use the existing one.
                    // since we are releasing app code, we need to release the
                    // lock.

                    fDuplicate = TRUE;
                }
                else if (SUCCEEDED(hr))
                {
                    // set the OID now while we are holding the lock.
                    hr = pStdId->SetOID(moid);
                    Win4Assert(pStdIdPrev == NULL);
                }
            }

            if (pStdId && (FAILED(hr) || fDuplicate))
            {
                Win4Assert( (FAILED(hr) && (pStdIdPrev == NULL))  ||
                            (fDuplicate && (pStdIdPrev != NULL)) );
                UNLOCK
                ASSERT_LOCK_RELEASED

                pStdId->Release();
                pStdId = pStdIdPrev;

                ASSERT_LOCK_RELEASED
                LOCK
            }
        }

        UNLOCK
        ASSERT_LOCK_RELEASED
    }

    *ppStdMshl = (CStdMarshal *)pStdId;
    AssertOutPtrIface(hr, *ppStdMshl);

    ComDebOut((DEB_MARSHAL,
        "FindStdMarshal pStdMshl:%x hr:%x\n", *ppStdMshl, hr));
    return hr;
}

//+------------------------------------------------------------------------
//
//  Function:   CompleteObjRef, public
//
//  Synopsis:   Fills in the missing fields of an OBJREF from a STDOBJREF
//              and resolves the OXID. Also sets fLocal to TRUE if the
//              object was marshaled in this apartment.
//
//  History:    22-Jan-96   Rickhi  Created
//
//-------------------------------------------------------------------------
HRESULT CompleteObjRef(OBJREF &objref, OXID_INFO &oxidInfo, REFIID riid, BOOL *pfLocal)
{
    // tweak the objref so we can call ReleaseMarshalObjRef or UnmarshalObjRef
    objref.signature = OBJREF_SIGNATURE;
    objref.flags     = OBJREF_STANDARD;
    objref.iid       = riid;

    HRESULT hr = InitChannelIfNecessary();
    if (FAILED(hr))
        return hr;

    ASSERT_LOCK_RELEASED
    LOCK

    OXIDEntry *pOXIDEntry = NULL;
    MIDEntry *pMIDEntry;
    hr = GetLocalMIDEntry(&pMIDEntry);

    if (SUCCEEDED(hr))
    {
        hr = FindOrCreateOXIDEntry(ORSTD(objref).std.oxid,
                                   oxidInfo,
                                   FOCOXID_NOREF,
                                   gpsaLocalResolver,
                                   gLocalMid,
                                   pMIDEntry,
                                   &pOXIDEntry);
    }

    if (SUCCEEDED(hr))
    {
        OXIDEntry **ppOXIDEntry = (OXIDEntry **) &ORSTD(objref).saResAddr;
        *ppOXIDEntry = pOXIDEntry;

        *pfLocal = (pOXIDEntry == GetLocalOXIDEntry());
    }

    UNLOCK
    ASSERT_LOCK_RELEASED
    return hr;
}