//+-------------------------------------------------------------------
//
//  File:       remunkps.cxx
//
//  Contents:   IRemoteUnnknown custom proxy/stub implementation
//
//  Classes:    CRemUnknownFactory
//              CRemUnknownSyncP
//              CRemUnknownAsyncP
//
//  History:    15-Dec-97       MattSmit  Created
//
//--------------------------------------------------------------------

#include <ole2int.h>
#include "remunkps.hxx"
#include "..\..\com\dcomrem\sync.hxx"
// out internal psclass factory implementation
EXTERN_C HRESULT ProxyDllGetClassObject(REFCLSID clsid, REFIID iid, void **ppv);



//+--------------------------------------------------------------------
//
// Function:  RemUnkGetClassObject
//
// Synopsis:  Creates a factory object for our custom IRemUnknown proxy.
//
//---------------------------------------------------------------------

HRESULT RemUnkPSGetClassObject(REFIID riid, LPVOID *ppv)
{
    ComDebOut((DEB_CHANNEL, "RemUnkPSGetClassObject IN riid:%I, ppv:0x%x\n", &riid, ppv));


    HRESULT hr = E_OUTOFMEMORY;
    CRemUnknownFactory *pObj = new CRemUnknownFactory();
    if (pObj)
    {
        hr = pObj->QueryInterface(riid, ppv);
    }
    ComDebOut((DEB_CHANNEL, "RemUnkPSGetClassObject OUT hr:0x%x\n", hr));
    return hr;



}
//+-------------------------------------------------------------------
//
// Class:      CRemUnknownFactory
//
// Synopsis:   Custom factory for the proxy/stub for IRemUnknown.
//
//--------------------------------------------------------------------

//+-------------------------------------------------------------------
//
// Interface:  IUnKnown
//
// Synopsis:   Simple IUnknown implementation.  QI will support
//             IPSFactoryBuffer
//
//--------------------------------------------------------------------

STDMETHODIMP CRemUnknownFactory::QueryInterface(REFIID riid, PVOID *pv)
{
    ComDebOut((DEB_CHANNEL, "CRemUnknownFactory::QueryInterface IN riid:%I, pv:0x%x\n", &riid, pv));

    if ((riid == IID_IUnknown) || (riid == IID_IPSFactoryBuffer))
    {
        *pv = this;
        AddRef();
        return S_OK;
    }
    else
    {
        return E_NOINTERFACE;
    }


}

STDMETHODIMP_(ULONG) CRemUnknownFactory::AddRef()
{
    return InterlockedIncrement((PLONG) &_cRefs);
}


STDMETHODIMP_(ULONG) CRemUnknownFactory::Release()
{
    ULONG ret = InterlockedDecrement((PLONG) &_cRefs);
    if (ret == 0)
    {
        delete this;
    }
    return ret;
}




//+-------------------------------------------------------------------
//
// Interface:  IPSFactoryBuffer
//
//--------------------------------------------------------------------


//+-------------------------------------------------------------------
//
// Method:     CreateProxy
//
// Synopsis:   Creates a custom synchronous proxy object so we can
//             implement ICallFactory on the proxy.
//
//--------------------------------------------------------------------
STDMETHODIMP CRemUnknownFactory::CreateProxy(IUnknown *pUnkOuter,
                                             REFIID riid,
                                             IRpcProxyBuffer **ppProxy,
                                             void **ppv)
{
    ComDebOut((DEB_CHANNEL, "CRemUnknownFactory::CreateProxy IN pUnkOuter:0x%x, riid:%I,"
                " ppProxy:0x%x, ppv:0x%x\n", pUnkOuter, &riid, ppProxy, ppv));

    HRESULT hr;
    Win4Assert((riid == IID_IRemUnknown) ||
               (riid == IID_IRemUnknown2) ||
               (riid == IID_IRemUnknownN) ||
               (riid == IID_IRundown)) ;

    hr = E_OUTOFMEMORY;
    CRemUnknownSyncP *pObj = new CRemUnknownSyncP();
    if (pObj)
    {
        hr = pObj->Init(pUnkOuter, riid, ppProxy, ppv);
        pObj->_IntUnk.Release();
    }

    // Clean up out parameters on error.
    if (FAILED(hr))
    {
        *ppProxy = NULL;
        *ppv     = NULL;
    }

    ComDebOut((DEB_CHANNEL, "CRemUnknownFactory::CreateProxy OUT hr:0x%x\n", hr));
    return hr;
}


//+-------------------------------------------------------------------
//
// Method:     CreateStub
//
// Synopsis:   Create the MIDL generated stub and give it back
//             because we do not need to get involved on the server
//             side.
//
//--------------------------------------------------------------------
STDMETHODIMP CRemUnknownFactory::CreateStub(REFIID riid,
                                            IUnknown *pUnkServer,
                                            IRpcStubBuffer **ppStub)
{
    ComDebOut((DEB_CHANNEL, "CRemUnknownFactory::CreateStub IN riid:%I, pUnkServer:0x%x, ppStub:0x%x\n",
               &riid, pUnkServer, ppStub));
    HRESULT hr;
    IPSFactoryBuffer *pFac;
    if (SUCCEEDED(hr = ProxyDllGetClassObject(CLSID_PSOlePrx32, IID_IPSFactoryBuffer, (void **) &pFac)))
    {
        hr = pFac->CreateStub(riid, pUnkServer, ppStub);
        pFac->Release();
    }
    ComDebOut((DEB_CHANNEL, "CRemUnknownFactory::CreateStub OUT hr:0x%x\n", hr));

    return hr;
}







//+-------------------------------------------------------------------
//
// Class:      CRemUnknownSyncP::CRpcProxyBuffer
//
// Synopsis:   Internal Unknown and IRpcProxyBuffer implementation
//             for CRemUnknownSyncP
//
//--------------------------------------------------------------------
//+-------------------------------------------------------------------
//
// Interface:  IUnKnown
//
// Synopsis:   Simple IUnknown implementation.  QI will support
//             IRpcProxyBuffer, ICallFactory and delegate the
//             rest to the MIDL implementation
//
//--------------------------------------------------------------------
STDMETHODIMP CRemUnknownSyncP::CRpcProxyBuffer::QueryInterface(REFIID riid, PVOID *pv)
{
    ComDebOut((DEB_CHANNEL, "CRemUnknownSyncP::CRpcProxyBuffer::QueryInterface"
               " riid:%I, pv:0x%x\n", &riid, pv));

    CRemUnknownSyncP *This = (CRemUnknownSyncP *) (((BYTE *) this) - offsetof(CRemUnknownSyncP, _IntUnk));

    if ((riid == IID_IUnknown) || (riid == IID_IRpcProxyBuffer))
    {
        *pv = this;
        AddRef();
        return S_OK;
    }
    else if (riid == IID_ICallFactory)
    {
        *pv = (ICallFactory * ) This;
        This->AddRef();
        return S_OK;
    }
    else
    {
        return This->_pPrx->QueryInterface(riid, pv);
    }
}

STDMETHODIMP_(ULONG) CRemUnknownSyncP::CRpcProxyBuffer::AddRef()
{
    return InterlockedIncrement((PLONG) &_cRefs);
}


STDMETHODIMP_(ULONG) CRemUnknownSyncP::CRpcProxyBuffer::Release()
{
    ULONG ret = InterlockedDecrement((PLONG) &_cRefs);
    if (ret == 0)
    {
        CRemUnknownSyncP *This = (CRemUnknownSyncP *) (((BYTE *) this) - offsetof(CRemUnknownSyncP, _IntUnk));
        delete This;
    }
    return ret;
}



//+-------------------------------------------------------------------
//
// Interface:  IRpcProxyBuffer
//
// Synopsis:   Delegates IrpcProxyBuffer calls to the MIDL generated
//
//
//--------------------------------------------------------------------
STDMETHODIMP CRemUnknownSyncP::CRpcProxyBuffer::Connect(IRpcChannelBuffer *pRpcChannelBuffer)
{
    ComDebOut((DEB_CHANNEL, "CRemUnknownSyncP::CRpcProxyBuffer::Connect pRpcChannelBuffer:0x%x\n", pRpcChannelBuffer));

    CRemUnknownSyncP *This = (CRemUnknownSyncP *) (((BYTE *) this) - offsetof(CRemUnknownSyncP, _IntUnk));
    return This->_pPrx->Connect(pRpcChannelBuffer);
}


STDMETHODIMP_(void) CRemUnknownSyncP::CRpcProxyBuffer::Disconnect()
{
    ComDebOut((DEB_CHANNEL, "CRemUnknownSyncP::CRpcProxyBuffer::Disconnect\n" ));

    CRemUnknownSyncP *This = (CRemUnknownSyncP *) (((BYTE *) this) - offsetof(CRemUnknownSyncP, _IntUnk));
    This->_pPrx->Disconnect();
}





//+-------------------------------------------------------------------
//
// Class:      CRemUnknownSyncP
//
// Synopsis:   Synchronous proxy for IRemUknown
//
//--------------------------------------------------------------------
//+-------------------------------------------------------------------
//
// Method:     Init
//
// Synopsis:   Initializes the proxy wrapper by creating the MIDL
//             generated proxy and saving the outer unknown.
//
//--------------------------------------------------------------------
HRESULT CRemUnknownSyncP::Init(IUnknown *pUnkOuter,
                               REFIID riid,
                               IRpcProxyBuffer **ppProxy,
                               void **ppv)
{
    ComDebOut((DEB_CHANNEL, "CRemUnknownSyncP::Init IN pUnkOuter:0x%x,"
               " riid:%I, ppProxy:0x%x, ppv:0x%x\n", pUnkOuter, &riid, ppProxy, ppv));

    HRESULT hr;

    _pCtrlUnk = pUnkOuter;
    IPSFactoryBuffer *pFac;
    if (SUCCEEDED(hr = ProxyDllGetClassObject(CLSID_PSOlePrx32, IID_IPSFactoryBuffer, (void **) &pFac)))
    {
        IRpcProxyBuffer *pPrx = 0;
        if (SUCCEEDED(hr = pFac->CreateProxy(pUnkOuter, riid, &pPrx, ppv)))
        {
            _pPrx = pPrx;
            *ppProxy = &_IntUnk;
            (*ppProxy)->AddRef();

        }
        pFac->Release();
    }

    ComDebOut((DEB_CHANNEL, "CRemUnknownSyncP::Init OUT hr:0x%x\n", hr));
    return hr;
}

//+-------------------------------------------------------------------
//
// Method:     Destructor
//
// Synopsis:   Release the MIDL generated proxy if we aquired it.
//
//--------------------------------------------------------------------

CRemUnknownSyncP::~CRemUnknownSyncP()
{
    ComDebOut((DEB_CHANNEL, "CRemUnknownSyncP~CRemUnknownSyncP \n" ));

    if (_pPrx)
    {
        _pPrx->Release();
        _pPrx = NULL;
    }
}



//+-------------------------------------------------------------------
//
// Interface:  IUnKnown
//
// Synopsis:   Simple IUnknown implementation.  Delegates to outer.
//
//--------------------------------------------------------------------
STDMETHODIMP CRemUnknownSyncP::QueryInterface(REFIID riid, PVOID *pv)
{
    return _pCtrlUnk->QueryInterface(riid, pv);
}
STDMETHODIMP_(ULONG) CRemUnknownSyncP::AddRef()
{
    return _pCtrlUnk->AddRef();
}
STDMETHODIMP_(ULONG) CRemUnknownSyncP::Release()
{
    return _pCtrlUnk->Release();
}


//+-------------------------------------------------------------------
//
// Interface:  ICallFactory
//
// Synopsis:   Interface for creating asynchronous call objects
//
//--------------------------------------------------------------------
//+-------------------------------------------------------------------
//
// Method:     CreateCall
//
// Synopsis:   create and initialize an async call object
//
//--------------------------------------------------------------------
STDMETHODIMP CRemUnknownSyncP::CreateCall(REFIID                riid,
                                          IUnknown             *pCtrlUnk,
                                          REFIID                riid2,
                                          IUnknown **ppUnk)
{

    ComDebOut((DEB_CHANNEL, "CRemUnknownSyncP::CreateCall IN "
               "riid:%I, pCtrlUnk:0x%x, riid2:%I, ppUnk:0x%x\n",
                &riid, pCtrlUnk, &riid2, ppUnk));

    HRESULT hr = E_OUTOFMEMORY;
    Win4Assert((riid == IID_AsyncIRemUnknown) || (riid == IID_AsyncIRemUnknown2));
    CRemUnknownAsyncCallP *pObj = new CRemUnknownAsyncCallP(pCtrlUnk);
    if (pObj)
    {
        hr = pObj->_IntUnk.QueryInterface(riid2, (void **) ppUnk);
        pObj->_IntUnk.Release();
    }

    ComDebOut((DEB_CHANNEL, "CRemUnknownSyncP::CreateCall OUT hr:0x%x\n", hr));
    return hr;
}













//+-------------------------------------------------------------------
//
// Class:      CRemUnknownAsyncCallP::CRpcProxyBuffer
//
// Synopsis:   Inner unknown and proxy buffer interface
//
//--------------------------------------------------------------------
//+-------------------------------------------------------------------
//
// Interface:  IUnKnown
//
// Synopsis:   Simple IUnknown implementation.  QI will support
//             IRpcProxyBuffer and AsyncIRemUnknown
//
//--------------------------------------------------------------------
STDMETHODIMP CRemUnknownAsyncCallP::CRpcProxyBuffer::QueryInterface(REFIID riid, PVOID *pv)
{
    ComDebOut((DEB_CHANNEL, "CRemUnknownAsyncCallP::CRpcProxyBuffer::QueryInterface "
               " riid:%I, pv:0x%x\n", &riid, pv));

    CRemUnknownAsyncCallP *This = (CRemUnknownAsyncCallP *) (((BYTE *) this) - offsetof(CRemUnknownAsyncCallP, _IntUnk));

    if ((riid == IID_IUnknown) || (riid == IID_IRpcProxyBuffer))
    {
        *pv = this;
        AddRef();
        return S_OK;
    }
    else if ((riid == IID_AsyncIRemUnknown) || (riid == IID_AsyncIRemUnknown2) )
    {
        *pv = (AsyncIRemUnknown2 * ) This;
        This->AddRef();
        return S_OK;
    }

    return E_NOINTERFACE;
}

STDMETHODIMP_(ULONG) CRemUnknownAsyncCallP::CRpcProxyBuffer::AddRef()
{
    return InterlockedIncrement((PLONG) &_cRefs);
}


STDMETHODIMP_(ULONG) CRemUnknownAsyncCallP::CRpcProxyBuffer::Release()
{
    ULONG ret = InterlockedDecrement((PLONG) &_cRefs);
    if (ret == 0)
    {
        CRemUnknownAsyncCallP *This = (CRemUnknownAsyncCallP *) (((BYTE *) this) - offsetof(CRemUnknownAsyncCallP, _IntUnk));
        delete This;
    }
    return ret;
}



//+-------------------------------------------------------------------
//
// Interface:  IRpcProxyBuffer
//
// Synopsis:   handles logistics of connecting and disconnecting
//             the proxy call object
//
//--------------------------------------------------------------------
STDMETHODIMP CRemUnknownAsyncCallP::CRpcProxyBuffer::Connect(IRpcChannelBuffer *pRpcChannelBuffer)
{
    CRemUnknownAsyncCallP *This = (CRemUnknownAsyncCallP *) (((BYTE *) this) - offsetof(CRemUnknownAsyncCallP, _IntUnk));
    Win4Assert(!This->_pChnl);
    return pRpcChannelBuffer->QueryInterface(IID_IAsyncRpcChannelBuffer, (void **) &(This->_pChnl));
}


STDMETHODIMP_(void) CRemUnknownAsyncCallP::CRpcProxyBuffer::Disconnect(void)
{
    CRemUnknownAsyncCallP *This = (CRemUnknownAsyncCallP *) (((BYTE *) this) - offsetof(CRemUnknownAsyncCallP, _IntUnk));
    if (This->_pChnl)
    {
        This->_pChnl->Release();
        This->_pChnl = NULL;
    }
}








//+-------------------------------------------------------------------
//
// Class:      CRemUnknownAsyncCallP
//
// Synopsis:   Asynchronous proxy call object for IRemUknown
//
//--------------------------------------------------------------------
//+-------------------------------------------------------------------
//
// Interface:  IUnKnown
//
// Synopsis:   Simple IUnknown implementation.  Delegates to outer.
//
//--------------------------------------------------------------------
STDMETHODIMP CRemUnknownAsyncCallP::QueryInterface(REFIID riid, PVOID *pv)
{
    return _pCtrlUnk->QueryInterface(riid, pv);
}

STDMETHODIMP_(ULONG) CRemUnknownAsyncCallP::AddRef()
{
    return _pCtrlUnk->AddRef();
}
STDMETHODIMP_(ULONG) CRemUnknownAsyncCallP::Release()
{
    return _pCtrlUnk->Release();
}





//+-------------------------------------------------------------------
//
// Interface:  AsyncIRemUnknown
//
// Synopsis:   This is the actual async interface which will marshal
//             the parameters.
//
//--------------------------------------------------------------------
//+----------------------------------------------------------------------------
//
//  Member:        CRemUnknownAsyncCallP::Begin_RemQueryInterface
//
//  Synopsis:      Marshal paramenters and save cIids
//
//  History:       23-Jan-98  MattSmit  Created
//
//-----------------------------------------------------------------------------
STDMETHODIMP CRemUnknownAsyncCallP::Begin_RemQueryInterface(
        REFIPID                ripid,
        unsigned long          cRefs,
        unsigned short         cIids,
        IID                   *iids
    )
{
    ComDebOut((DEB_CHANNEL, "CRemUnknownAsyncCallP::Begin_RemQueryInterface IN"
               " ripid:%I, crefs:0x%d, cIids:0x%d, iids:%I\n",
               &ripid, cRefs, cIids, iids));


    _hr = S_OK;
    HRESULT hr;

    // Get the synchronize interface
    ISynchronize *pSync;
    if (FAILED(hr = _pCtrlUnk->QueryInterface(IID_ISynchronize, (void **) &pSync) ))
    {
        ComDebOut((DEB_CHANNEL, "CRemUnknownAsyncCallP::Begin_RemQueryInterface"
                    " FAILED QI for ISyncronize\n"));
        return hr;
    }

    // set up message
    memset(&_msg, 0, sizeof(RPCOLEMESSAGE));

    _msg.cbBuffer = sizeof(IPID) +                       // ripid
        sizeof(unsigned long) +                          // cRefs
        sizeof(unsigned short) + 2 +                     // cIids + padding
        sizeof(unsigned long)  + (cIids * sizeof(IID));  // iids

    _msg.rpcFlags = RPC_BUFFER_ASYNC;
    _msg.iMethod = 3;

    // get buffer
    if (SUCCEEDED(hr = _pChnl->GetBuffer(&_msg, IID_IRemUnknown)))
    {
        ComDebOut((DEB_CHANNEL, "CRemUnknownAsyncCallP::Begin_RemQueryInterface  "
                   "buffer obtained _msg.cbBuffer:%d, _msg.Buffer:0x%x \n",
                   _msg.cbBuffer, _msg.Buffer));

        BYTE * p = (BYTE *)_msg.Buffer;
        memset(p, 0, _msg.cbBuffer);

        // marshal the parameters
        memcpy(p, &ripid, sizeof(IPID));
        p += sizeof(IPID);
        memcpy(p, &cRefs, sizeof(cRefs));
        p += sizeof (cRefs);
        memcpy(p, &cIids, sizeof(unsigned short));
        p += sizeof (unsigned short) + 2;
        memcpy(p, &cIids, sizeof(unsigned short));
        p += sizeof (unsigned long);
        unsigned short i;
        for (i=0; i<cIids; i++)
        {
            memcpy(p, iids+i, sizeof(IID));
            p += sizeof (IID);
        }

        // save this for later
        _cIids = cIids;



        // send
        ULONG status;
        hr = _pChnl->Send(&_msg, pSync, &status);

    }
    if (FAILED(hr))
    {
        pSync->Signal();
        _hr = hr;
    }
    pSync->Release();

    ComDebOut((DEB_CHANNEL, "CRemUnknownAsyncCallP::Begin_RemQueryInterface OUT hr:0x%x\n", hr));

    return hr;
}


//+----------------------------------------------------------------------------
//
//  Member:        CRemUnknownAsyncCallP::Finish_RemQueryInterface
//
//  Synopsis:      Unmarshal QI parameters
//
//  History:       23-Jan-98  MattSmit  Created
//
//-----------------------------------------------------------------------------
STDMETHODIMP CRemUnknownAsyncCallP::Finish_RemQueryInterface(REMQIRESULT **ppQIResults)
{
    ComDebOut((DEB_CHANNEL, "CRemUnknownAsyncCallP::Finish_RemQueryInterface IN ppQIResults:0x%x\n", ppQIResults));

    HRESULT hr;

    // check state
    if (FAILED(_hr) )
    {
        return _hr;
    }

    // ensure call has completed
    ISynchronize *pSync;
    if (SUCCEEDED(_pCtrlUnk->QueryInterface(IID_ISynchronize, (void **) &pSync) ))
    {
        hr = pSync->Wait(0, INFINITE);
        pSync->Release();

        if (FAILED(hr))
        {
            return hr;
        }
    }

    // receive
    ULONG status;
    if (SUCCEEDED(hr = _pChnl->Receive(&_msg, &status)))
    {

        hr = E_OUTOFMEMORY;
        // unmarshal parameters
        REMQIRESULT *p = (REMQIRESULT *)CoTaskMemAlloc(_cIids * sizeof(REMQIRESULT));

        if (p)
        {
            ComDebOut((DEB_CHANNEL, "_cIids = 0x%x, _msg.cbBuffer = 0x%x, _msg.Buffer=0x%x\n",
                       _cIids, _msg.cbBuffer, _msg.Buffer));
            ComDebOut((DEB_CHANNEL, "sizeof(REMQIRESULT)=0x%x, offsetof(REMQIRESULT, std)=0x%x\n",
                       sizeof(REMQIRESULT),offsetof(REMQIRESULT, std)));


//            Win4Assert(_cIids == ((unsigned long *) _msg.Buffer)[1]);
            BYTE * pBuffer = (BYTE *)_msg.Buffer;
            pBuffer += sizeof(DWORD)*2;
            memcpy(p, pBuffer, sizeof(REMQIRESULT) *_cIids);

            *ppQIResults = p;
            hr = S_OK;
        }

        // free the buffer
        _pChnl->FreeBuffer(&_msg);
    }
    ComDebOut((DEB_CHANNEL, "CRemUnknownAsyncCallP::Finish_RemQueryInterface OUT hr:0x%x\n", hr));
    return hr;

}

//+----------------------------------------------------------------------------
//
//  Member:        CRemUnknownAsyncCallP::Begin_RemAddRef
//
//  Synopsis:      Not implemented. Never used
//
//  History:       23-Jan-98  MattSmit  Created
//
//-----------------------------------------------------------------------------
STDMETHODIMP CRemUnknownAsyncCallP::Begin_RemAddRef(unsigned short cInterfaceRefs,
                                                    REMINTERFACEREF InterfaceRefs[])
{
    return E_NOTIMPL;
}
//+----------------------------------------------------------------------------
//
//  Member:        CRemUnknownAsyncCallP::Finish_RemAddRef
//
//  Synopsis:      Not implemented. Never used
//
//  History:       23-Jan-98  MattSmit  Created
//
//-----------------------------------------------------------------------------
STDMETHODIMP CRemUnknownAsyncCallP::Finish_RemAddRef(HRESULT *pResults)
{
    return E_NOTIMPL;
}


//+----------------------------------------------------------------------------
//
//  Member:        CRemUnknownAsyncCallP::Begin_RemRelease
//
//  Synopsis:      Marshal parameters.
//
//  History:       23-Jan-98  MattSmit  Created
//
//-----------------------------------------------------------------------------
STDMETHODIMP CRemUnknownAsyncCallP::Begin_RemRelease(unsigned short cInterfaceRefs,
                                                     REMINTERFACEREF InterfaceRefs[])
{
    ComDebOut((DEB_CHANNEL, "CRemUnknownAsyncCallP::Begin_RemRelease IN cInterfaceRefs:0x%d, ",
               "InterfaceRefs:0x%x\n", cInterfaceRefs, InterfaceRefs));


    _hr = S_OK;
    HRESULT hr;

    // Get the synchronize interface
    ISynchronize *pSync;
    if (FAILED(hr = _pCtrlUnk->QueryInterface(IID_ISynchronize, (void **) &pSync) ))
    {
        ComDebOut((DEB_CHANNEL, "CRemUnknownAsyncCallP::Begin_RemRelease FAILED QI for ISyncronize\n"));
        return hr;
    }

    // set up message
    memset(&_msg, 0, sizeof(RPCOLEMESSAGE));

    _msg.cbBuffer = sizeof(unsigned short) + 2 + // cInterfaceRefs
        sizeof(unsigned long) + (sizeof(REMINTERFACEREF) * cInterfaceRefs); // InterfaceRefs[]

    _msg.rpcFlags = RPC_BUFFER_ASYNC;
    _msg.iMethod = 5;

    // get buffer
    if (SUCCEEDED(hr = _pChnl->GetBuffer(&_msg, IID_IRemUnknown)))
    {
        BYTE * p = (BYTE *)_msg.Buffer;
        memset(p, 0, _msg.cbBuffer);

        // marshal the parameters

        memcpy(p, &cInterfaceRefs, sizeof(unsigned short));
        p += sizeof (unsigned short) + 2;
        memcpy(p, &cInterfaceRefs, sizeof(unsigned short));
        p += sizeof (unsigned long);
        unsigned short i;
        for (i=0; i<cInterfaceRefs; i++)
        {
            memcpy(p, InterfaceRefs+i, sizeof(REMINTERFACEREF));
            p += sizeof (REMINTERFACEREF);
        }

        // send
        ULONG status;
        hr = _pChnl->Send(&_msg, pSync, &status);

    }
    else
    {
        pSync->Signal();
    }

    if (FAILED(hr))
    {
        _hr = hr;
    }

    pSync->Release();


    return hr;
}
//+----------------------------------------------------------------------------
//
//  Member:        CRemUnknownAsyncCallP::Finish_RemRelease
//
//  Synopsis:      Check state and return.
//
//  History:       23-Jan-98  MattSmit  Created
//
//-----------------------------------------------------------------------------
STDMETHODIMP CRemUnknownAsyncCallP::Finish_RemRelease()
{

    HRESULT hr;

    // check state
    if (FAILED(_hr) )
    {
        return _hr;
    }

    // ensure call has completed
    ISynchronize *pSync;
    if (SUCCEEDED(_pCtrlUnk->QueryInterface(IID_ISynchronize, (void **) &pSync) ))
    {
        hr = pSync->Wait(0, INFINITE);
        pSync->Release();

        if (FAILED(hr))
        {
            return hr;
        }
    }

    // receive
    ULONG status;
    if (SUCCEEDED(hr = _pChnl->Receive(&_msg, &status)))
    {

        // free the buffer
        _pChnl->FreeBuffer(&_msg);
    }

    return hr;


}


//+----------------------------------------------------------------------------
//
//  Member:        CRemUnknownAsyncCallP::Begin_RemQueryInterface2
//
//  Synopsis:      Marshal parameters, save count for Finish.
//
//  History:       23-Jan-98  MattSmit  Created
//
//-----------------------------------------------------------------------------
STDMETHODIMP CRemUnknownAsyncCallP::Begin_RemQueryInterface2 (
        REFIPID                            ripid,
        unsigned short                     cIids,
        IID                               *iids
    )

{
    ComDebOut((DEB_CHANNEL, "CRemUnknownAsyncCallP::Begin_RemQueryInterface2 IN "
               "ripid:%I, cIids:0x%d, iids:0x%x\n", &ripid, cIids, iids));

    _hr = S_OK;
    HRESULT hr;

    // Get the synchronize interface
    ISynchronize *pSync;
    if (FAILED(hr = _pCtrlUnk->QueryInterface(IID_ISynchronize, (void **) &pSync) ))
    {
        ComDebOut((DEB_CHANNEL, "CRemUnknownAsyncCallP::Begin_RemQueryInterface2 FAILED QI for ISynchronize\n"));
        return hr;
    }

    // set up message
    memset(&_msg, 0, sizeof(RPCOLEMESSAGE));

    _msg.cbBuffer = sizeof(IPID) +                       // ripid
        sizeof(unsigned short) + 2 +                     // cIids + padding
        sizeof(unsigned long)  + (cIids * sizeof(IID));  // iids

    _msg.rpcFlags = RPC_BUFFER_ASYNC;
    _msg.iMethod = 6;

    // get buffer
    if (SUCCEEDED(hr = _pChnl->GetBuffer(&_msg, IID_IRemUnknown)))
    {
        BYTE * p = (BYTE *)_msg.Buffer;
        memset(p, 0, _msg.cbBuffer);

        // marshal the parameters
        memcpy(p, &ripid, sizeof(IPID));
        p += sizeof(IPID);
        memcpy(p, &cIids, sizeof(unsigned short));
        p += sizeof (unsigned short) + 2;
        memcpy(p, &cIids, sizeof(unsigned short));
        p += sizeof (unsigned long);
        memcpy(p, iids, sizeof(IID) * cIids);


        ComDebOut((DEB_CHANNEL, "RemQueryInterface2: _msg.Buffer:%x\n", _msg.Buffer));

        // save this for later
        _cIids = cIids;



        // send
        ULONG status;
        hr = _pChnl->Send(&_msg, pSync, &status);

    }
    if (FAILED(hr))
    {
        pSync->Signal();
        _hr = hr;
    }
    pSync->Release();

    ComDebOut((DEB_CHANNEL, "CRemUnknownAsyncCallP::Begin_RemQueryInterface2 OUT hr:0x%x\n", hr));

    return hr;
}


//+----------------------------------------------------------------------------
//
//  Member:        CRemUnknownAsyncCallP::Finish_RemQueryInterface2
//
//  Synopsis:      Unmarshal out parameters.
//
//  History:       23-Jan-98  MattSmit  Created
//
//-----------------------------------------------------------------------------
STDMETHODIMP CRemUnknownAsyncCallP::Finish_RemQueryInterface2(
        HRESULT           *phr,
        MInterfacePointer **ppMIF
    )
{
    ComDebOut((DEB_CHANNEL, "CRemUnknownAsyncCallP::Finish_RemQueryInterface2 IN "
                "phr:0x%x, ppMIF:0x%x\n", phr, ppMIF));

    HRESULT hr;

    // check state
    if (FAILED(_hr) )
    {
        return _hr;
    }

    // ensure call has completed
    hr = WaitObject(_pCtrlUnk, 0, INFINITE);
    if (FAILED(hr))
    {
        return hr;
    }

    // receive
    ULONG status;
    if (SUCCEEDED(hr = _pChnl->Receive(&_msg, &status)))
    {
        ComDebOut((DEB_CHANNEL, "CRemUnknownAsyncCallP::Finish_RemQueryInterface2 \n",
                   "_cIids = 0x%x, _msg.cbBuffer = 0x%x, _msg.Buffer=0x%x\n",
                   _cIids, _msg.cbBuffer, _msg.Buffer));

        BYTE * pBuffer = (BYTE *)_msg.Buffer;
        pBuffer += sizeof(ULONG); // skip the count
        memcpy(phr, pBuffer, sizeof(HRESULT) *_cIids); // copy the hresults
        pBuffer += sizeof(HRESULT) *_cIids; // advance past hresults
        pBuffer += sizeof(ULONG); // skip count
        pBuffer += sizeof(ULONG) * _cIids; // not sure what this is, but its there.
        for(int i = 0; i < _cIids; i++)
        {

            // allocate memory and transfer to out parameters.
            ULONG *pulCntData = (PULONG) pBuffer;
            ULONG cb = sizeof(MInterfacePointer) + *pulCntData;
            MInterfacePointer *pMIF = (MInterfacePointer *) CoTaskMemAlloc(cb);
            if (pMIF)
            {
                pMIF->ulCntData = *pulCntData;
                pBuffer += sizeof(ULONG);
                pBuffer += sizeof(ULONG); // skip count
                memcpy(pMIF->abData, pBuffer, pMIF->ulCntData);
                ppMIF[i] = pMIF;
                pBuffer += pMIF->ulCntData;

            }
            else
            {
                // no memory, so free up what we have left
                // an return an error
                hr = E_OUTOFMEMORY;
                for (int j = 0; j<i; j++)
                {
                    CoTaskMemFree(ppMIF[j]);
                    ppMIF[j] = NULL;
                }
                break;

            }

        }


        // free the buffer
        _pChnl->FreeBuffer(&_msg);
    }
    ComDebOut((DEB_CHANNEL, "CRemUnknownAsyncCallP::Finish_RemQueryInterface2 "
                   "phr:%x, ppMIF:%x\n", phr, ppMIF));
    ComDebOut((DEB_CHANNEL, "CRemUnknownAsyncCallP::Finish_RemQueryInterface2 OUT hr:0x%x\n", hr));
    return hr;

}