//+-------------------------------------------------------------------
//
//  File:       remhdlr.hxx
//
//  Contents:   remote object handler definition
//
//  Classes:    CPSIX       - proxy/stub interface record
//              CIXList     - list of CPSIX records
//              CRemoteHdlr - remote object handler
//
//  History:    23-Nov-92   Rickhi      Modified from Ole2 sources
//
//  The general architecture is thus...
//
//          [ CRemoteHdlr ]--->[ CPSIX ]-->[ CPSIX ]--->[ CPSIX ]
//                    |
//          [ CRemoteHdlr ]--->[ CPSIX ]-->[ CPSIX ]
//
//  There is one RH per remote object. The RHs are accessed via the identity
//  object.  The CPSIX is a wrapper class for an interface stub or proxy.
//  They are chained off the RHs, one per interface.
//
//  The RHs implement the IMarshal interface and take care of marshalling
//  and unmarshalling interfaces on the objects.
//
//  The RH is never aggregated; the identity object is the only creator of
//  the RH and the two interfaces it supports (IRemoteHdlr and IMarshal) can
//  *never* be passed outside the aggregate in which the identity object exists.
//
//  The RH gets a punk (_punkObj below) when created.  In the local case this is
//  the actual server object and is addref'd always.  In the remote case the
//  pointer is the pointer to the controlling unknown of the aggregate in which
//  the identity object exists (and is not addref'd).  As mentioned, the RH is
//  not aggregated, but the proxy objects are; the _punkObj becomes the
//  controlling unknown of the proxy objects so their interfaces can be passed
//  outside the identity (really handler) aggregate.
//
//--------------------------------------------------------------------

#ifndef __REMHDLR__
#define __REMHDLR__

//  forward declaration of classes
class   CPSIX;                      //  interface wrapper
class   CIXList;                    //  list of interface wrappers
class   CRemoteHdlr;                //  remote handler

#include    <sem.hxx>               //  CMutexSem, CLock
#include    <dd.hxx>                //  doubly linked list class
#include    <iface.h>               //  SHandlerDataHdr
#include    <olerem.h>              //  GUIDs
#include    <channelb.hxx>          //  CRpcChannelBuffer


//+-------------------------------------------------------------------
//
//  Class:      CPSIX
//
//  Purpose:    This class provides a wrapper around the interface
//      proxies and stubs, insulating higher-level code from
//              knowing whether an interface is a proxy or a stub, and
//              insulating the proxies from knowing the internals of the
//              remote object handling implementation.
//
//  Interface:
//
//  History:    23-Nov-92   Rickhi      Created
//
//--------------------------------------------------------------------

class CPSIX : public CListEntry
{
  friend CIXList;

public:

    //  Constructor & Destructor
            CPSIX(REFIID            riid,
                  IRpcProxyBuffer   *pIProxy);

            CPSIX(REFIID            riid,
                          IRpcStubBuffer    *pStub);

                    ~CPSIX(void);

    IRpcStubBuffer  *GetIStub(void);    //  used by marshalling code
#if DBG == 1
    void	    VerifyReconnect(IUnknown *);
#endif

private:
    //  CODEWORK: make this a union
    IRpcProxyBuffer   *_pIProxy;        //  proxy interface

    IRpcStubBuffer    *_pStub;          //  stub entry
    IID                _iid;            //  only needed for stubs
#if DBG==1
    DWORD	       _cRefs;		//  number of refs held by stub
#endif
};


//+-------------------------------------------------------------------------
//
//  Class:      CIXListBase (xlb)
//
//  Purpose:    Base class for Head list of interface objects.
//
//  Interface:  first -- get first item in the list.
//              next -- get next item in the list.
//
//  History:    23-Nov-92   Rickhi      Created
//
//  Notes:      See dd.hxx for details of this macro.
//
//--------------------------------------------------------------------------
DERIVED_LIST_HEAD(CIXListBase, CPSIX);


//+-------------------------------------------------------------------------
//
//  Class:      CIXList (ixl)
//
//  Purpose:    Head list of interface objects.
//
//  Interface:  AddToList -- add an entry to a list of proxies
//
//  History:    23-Nov-92   Rickhi      Created
//
//  Notes:      This class adds a few methods to its macro defined base class.
//
//--------------------------------------------------------------------------
class CIXList : public CIXListBase
{
public:

    CPSIX       *FindProxyIX(REFIID riid, void**ppv);
						//  search for proxy IX
    CPSIX       *FindStubIX(REFIID riid);       //  search for stub IX

    void        AddToList(CPSIX *pIXToAdd);     //  add IX to list
    void        ConnectProxies(IRpcChannelBuffer *channel);//  connect proxies
    DWORD	CountStubRefs();		//  number of refs held by stubs
    void        DisconnectStubs(void);          //  disconnect stub IXs
    void        DisconnectProxies(void);        //  disconnect proxy IXs

#if DBG == 1
    void        AssertValid(BOOL fLocal);
#else
    void        AssertValid(BOOL fLocal) { }
#endif
};




//+-------------------------------------------------------------------------
//
//  Class:      CRemoteHdlr (RH)
//
//  Purpose:    remote object handler.
//
//  Interface:
//
//  History:    23-Nov-92   Rickhi      Created
//
//--------------------------------------------------------------------------
class CRemoteHdlr : public IRemoteHdlr, public IMarshal
{
public:
    CRemoteHdlr(IUnknown *punkObj, IWeakRef *pWR,
	IStdIdentity *pStdID, DWORD dwFlags, HRESULT &hr);

    //  IUnknown methods. I dont inherit tracking because i have
    //  special processing to do when the refcnt goes to zero.

    STDMETHOD(QueryInterface)(REFIID riid, void **ppv);
    STDMETHOD_(ULONG,AddRef)(void);
    STDMETHOD_(ULONG,Release)(void);


    //  IMarshal methods
    STDMETHOD(GetUnmarshalClass) (REFIID riid, void *pv,
                          DWORD dwDestContext, LPVOID pvDestContext,
                          DWORD mshlflags, LPCLSID pCid);
    STDMETHOD(GetMarshalSizeMax) (REFIID riid, void *pv,
                          DWORD dwDestContext, LPVOID pvDestContext,
                          DWORD mshlflags, LPDWORD pSize);
    STDMETHOD(MarshalInterface)  (IStream *pStm, REFIID riid, void *pv,
                          DWORD dwDestContext, LPVOID pvDestContext,
                          DWORD mshlflags);
    STDMETHOD(UnmarshalInterface)(IStream *pStm, REFIID riid, void **ppv);
    STDMETHOD(ReleaseMarshalData)(IStream *pStm);
    STDMETHOD(DisconnectObject)(DWORD dwReserved);


    // IRemoteHdlr methods
    STDMETHOD_(IRpcChannelBuffer *, GetChannel)(BOOL fAddRef);

    //  used by the channel to lock the RH during an Rpc call.
    STDMETHOD_(ULONG, LockClient)(void);
    STDMETHOD_(ULONG, UnLockClient)(void);

    // used during destruction of the identity object to clear the RH ptr.
    STDMETHOD_(void, ClearIdentity)(void);

    // returns TRUE if iid is supported; like QueryInterface except no
    // interface pointer returned; also works on both client and server sides.
    STDMETHOD_(BOOL, DoesSupportIID)(REFIID riid);

    // add/subtract a reference; vectors to identity; may return error
    STDMETHOD(AddConnection)(DWORD extconn, DWORD reserved);
    STDMETHOD(ReleaseConnection)(DWORD extconn, DWORD reserved, BOOL fLastReleaseCloses);

    STDMETHOD_(BOOL, IsConnected)(void);
    STDMETHOD_(void, Disconnect)(void);
    STDMETHOD(LockConnection)(BOOL fLock, BOOL fLastUnlockReleases);

    STDMETHOD_(IRpcStubBuffer *, LookupStub)(REFIID, IUnknown **, HRESULT *phr);
    STDMETHOD_(void, FinishCall)( IRpcStubBuffer *pStub, IUnknown *pUnkServer);
    STDMETHOD_(void, GetObjectID)( OID * );

#if DBG == 1
    void        AssertValid();
#else
    void        AssertValid() { }
#endif

private:
    //  Internal methods to find or create an interface proxy or stub
    CPSIX          *FindIX(REFIID riid, void **ppv, DWORD dwFlag, HRESULT *phr);

    //  internal unmarshalling and releasing functions
    STDMETHOD(Unmarshal)(IStream *pStm, REFIID riid,
                 SHandlerDataHdr &ifh, void **ppv);

    STDMETHOD(ReleaseData)(IStream *pStm, SHandlerDataHdr &ifh);

    CPSIX          *CreateInterfaceProxy(REFIID riid, void **ppv, HRESULT *phr);
    CPSIX          *CreateInterfaceStub(REFIID riid, HRESULT *phr);
    STDMETHOD_(BOOL, IsRequestedByWOW)( REFIID riid );

            ~CRemoteHdlr(void);     // callable only from Release

    DWORD                 _dwFlags;         //  internal flags
    ULONG                 _cReferences;     //  reference count
    DWORD		  _cCallsInProgress;//	number of calls in progress

    //  controlling unknown of object; if local, the object is separate and
    // we addref always; if remote, the object is the handler and we never
    // addref it.  Disconnect on server side sets it to NULL.  See more above.
    IUnknown              *_punkObj;

    // IWR on object (server side only; CODEWORK: combine with _punkObj
    // above using a flags in _dwFlags to indicate if it is IWR).
    IWeakRef  *_pWR;

    IStdIdentity          *_pstdID;         // identity; never addref'd

    CRpcChannelBuffer     *_pChannel;       //  ptr to channel used by ...
    CIXList               _IXList;          //  list of IXs on this interface

    static CMutexSem      _mxs;             //  global mutext
};




//  Definition of values for RHFlags
typedef enum tagRHFLAGS
{
    RHFLAGS_LOCAL       = 1,        //  object is local to this process
    RHFLAGS_PENDINGDISCONNECT = 2,  //	Disconnect during a call; delay releases
    RHFLAGS_GETTINGIX   = 4         //  RH is busy getting a proxy or stub
} RHFLAGS;

//  Definition of values for RFlags
typedef enum tagRFLAGS
{
    FLG_QUERYINTERFACE = 1,         //  called from QueryInterface
    FLG_MARSHAL        = 2,         //  called from MarshalInterface
    FLG_UNMARSHAL      = 4          //  called from UnmarshalInterface
} RFLAGS;


//  macros to determine if the CRemoteHdlr is for a LOCAL or REMOTE object
#define IS_LOCAL_RH     (_dwFlags & RHFLAGS_LOCAL)
#define IS_REMOTE_RH    !(IS_LOCAL_RH)




//+-------------------------------------------------------------------
//
//  Member:     CRemoteHdlr::~CRemoteHdlr, public
//
//  Synopsis:   destructor for the remote handler object
//
//  History:    23-Nov-93   Rickhi       Created
//
//--------------------------------------------------------------------

inline CRemoteHdlr::~CRemoteHdlr(void)
{
    CairoleDebugOut((DEB_MARSHAL,
             "Delete CRemoteHdlr pRH:%x pChannel:%x\n",
             this, _pChannel));

    // don't need to disconnect here since the identity destruction did that.

    Win4Assert(_cCallsInProgress == 0);

    // can't assert RH valid since identity is gone; can assert channel
    _pChannel->AssertValid(TRUE, FALSE);
    _IXList.AssertValid(IS_LOCAL_RH);

    //  release the channel. note that it might still be around until
    //  all the proxies disconnect from it, which is done in the _IXList
    //  destructor.

    // safe release of channel
    _pChannel->Release();
    _pChannel = NULL;
}


//+-------------------------------------------------------------------
//
//  Member:     CPSIX::CPSIX, public
//
//  Synopsis:   constructor for Proxy interface wrapper
//
//  History:    23-Nov-92   Rickhi      Created
//
//  Exceptions: CException if QueryInterface fails
//
//  Notes:      the object interface in the case of a proxy is the
//              interface on the proxy itself.
//
//--------------------------------------------------------------------
inline CPSIX::CPSIX(REFIID riid, IRpcProxyBuffer *pIProxy)
    :   _pIProxy(pIProxy),
        _pStub(NULL),
	_iid(IID_NULL)
#if DBG==1
      , _cRefs((DWORD)-1)
#endif
{
    //  validate input parms
    Win4Assert(_pIProxy && "Improperly Constructed CPSIX");

    _pIProxy->AddRef();     //  keep the proxy
}

//+-------------------------------------------------------------------
//
//  Member:     CPSIX::CPSIX, public
//
//  Synopsis:   constructor for stub interface wrapper
//
//  History:    23-Nov-92   Rickhi      Created
//
//--------------------------------------------------------------------
inline CPSIX::CPSIX(REFIID riid, IRpcStubBuffer *pStub)
    :   _pStub(pStub),
        _pIProxy(NULL),
        _iid(riid)
#if DBG==1
      , _cRefs(pStub->CountRefs())
#endif
{
    //  validate input parms
    Win4Assert(_pStub && "Improperly Constructed CPSIX");
    Win4Assert(_cRefs && "Stub not connected in CPSIX ctor");

    _pStub->AddRef();       //  keep the stub
}


//+-------------------------------------------------------------------
//
//  Member:     CPSIX::~CPSIX, public
//
//  Synopsis:
//
//  History:    23-Nov-92   Rickhi      Created
//
//--------------------------------------------------------------------

inline CPSIX::~CPSIX(void)
{
    if (_pIProxy)
    {
	_pIProxy->Disconnect();      //  disconnect the proxy from channel
	ULONG ul = _pIProxy->Release();      //  release interface proxy
	Win4Assert(ul == 0);
    }
    else
    {
        _pStub->Release();              //  release interface stub
    }
}

//+-------------------------------------------------------------------
//
//  Member:     CPSIX::GetIStub, public
//
//  Synopsis:
//
//  History:    23-Nov-92   Rickhi      Created
//
//--------------------------------------------------------------------

inline IRpcStubBuffer *CPSIX::GetIStub(void)
{
    Win4Assert(_pIProxy == NULL);
    return _pStub;
}


#if DBG == 1
//+-------------------------------------------------------------------
//
//  Member:     CPSIX::VerifyReconnect, public
//
//  Synopsis:	Verify that the stub disconnec and reconnect work and
//		that the ref counts remain the same.
//
//  History:     1-Jun-94   CraigWi     Created
//
//--------------------------------------------------------------------

inline void CPSIX::VerifyReconnect(IUnknown *pUnkServer)
{
    Win4Assert(_pIProxy == NULL);	    // for stub only
    Win4Assert(_cRefs != -1);		    // for connected stubs only
    _pStub->Disconnect();
    Win4Assert(_pStub->Connect(pUnkServer) == NOERROR);

    Win4Assert(_pStub->CountRefs() == _cRefs);
}
#endif


//+-------------------------------------------------------------------
//
//  Member:     CIXList::AddToList
//
//  Synopsis:   adds a Stub object to list of Stub objects
//
//  History:    23-Nov-93   Rickhi       Created
//
//  Note:       Thread synchronization is the responsibility of the caller.
//
//--------------------------------------------------------------------

inline void CIXList::AddToList(CPSIX *pIXToAdd)
{
    //  validate input parms
    Win4Assert(pIXToAdd);

    CairoleDebugOut((DEB_MARSHAL, "New IX addr: %x\n", pIXToAdd));
    insert_at_end(pIXToAdd);
}

#endif  //  __REMHDLR__
