/*++

Copyright (C) 1998-2001 Microsoft Corporation

Module Name:

    UBSKMRSH.CPP

Abstract:

    Unbound Sink Marshaling

History:

--*/

#include "precomp.h"
#include <stdio.h>
#include "ubskmrsh.h"
#include <fastall.h>
#include <cominit.h>

#define WBEM_S_NEW_STYLE 0x400FF

//****************************************************************************
//****************************************************************************
//                          PS FACTORY
//****************************************************************************
//****************************************************************************

//***************************************************************************
//
//  CUnboundSinkFactoryBuffer::XUnboundSinkFactory::CreateProxy
//
//  DESCRIPTION:
//
//  Creates a facelet.  Also sets the outer unknown since the proxy is going to be 
//  aggregated.
//
//  RETURN VALUE:
//
//  S_OK                all is well
//
//***************************************************************************

STDMETHODIMP CUnboundSinkFactoryBuffer::XUnboundSinkFactory::CreateProxy(IN IUnknown* pUnkOuter, 
    IN REFIID riid, OUT IRpcProxyBuffer** ppProxy, void** ppv)
{
    if(riid != IID_IWbemUnboundObjectSink)
    {
        *ppProxy = NULL;
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    CUnboundSinkProxyBuffer* pProxy = new CUnboundSinkProxyBuffer(m_pObject->m_pLifeControl, pUnkOuter);

    SCODE   sc = E_OUTOFMEMORY;

    if ( NULL != pProxy )
    {
        pProxy->QueryInterface(IID_IRpcProxyBuffer, (void**)ppProxy);
        sc = pProxy->QueryInterface(riid, (void**)ppv);
    }

    return sc;
}

//***************************************************************************
//
//  CUnboundSinkFactoryBuffer::XUnboundSinkFactory::CreateStub
//
//  DESCRIPTION:
//
//  Creates a stublet.  Also passes a pointer to the clients IWbemUnboundObjectSink 
//  interface.
//
//  RETURN VALUE:
//
//  S_OK                all is well
//
//***************************************************************************
    
STDMETHODIMP CUnboundSinkFactoryBuffer::XUnboundSinkFactory::CreateStub(IN REFIID riid, 
    IN IUnknown* pUnkServer, OUT IRpcStubBuffer** ppStub)
{
    if(riid != IID_IWbemUnboundObjectSink)
    {
        *ppStub = NULL;
        return E_NOINTERFACE;
    }

    CUnboundSinkStubBuffer* pStub = new CUnboundSinkStubBuffer(m_pObject->m_pLifeControl, NULL);

    if ( NULL != pStub )
    {
        pStub->QueryInterface(IID_IRpcStubBuffer, (void**)ppStub);

        // Pass the pointer to the clients object

        if(pUnkServer)
        {
            HRESULT hres = (*ppStub)->Connect(pUnkServer);
            if(FAILED(hres))
            {
                delete pStub;
                *ppStub = NULL;
            }
            return hres;
        }
        else
        {
            return S_OK;
        }

    }
    else
    {
        return E_OUTOFMEMORY;
    }
}

//***************************************************************************
//
//  void* CUnboundSinkFactoryBuffer::GetInterface(REFIID riid)
//
//  DESCRIPTION:
//
//  CUnboundSinkFactoryBuffer is derived from CUnk.  Since CUnk handles the QI calls,
//  all classes derived from it must support this function.
//
//  RETURN VALUE:
//
//  S_OK                all is well
//
//***************************************************************************

void* CUnboundSinkFactoryBuffer::GetInterface(REFIID riid)
{
    if(riid == IID_IPSFactoryBuffer)
        return &m_XUnboundSinkFactory;
    else return NULL;
}
        
//****************************************************************************
//****************************************************************************
//                          PROXY
//****************************************************************************
//****************************************************************************

//***************************************************************************
//
//  CUnboundSinkProxyBuffer::CUnboundSinkProxyBuffer
//  ~CUnboundSinkProxyBuffer::CUnboundSinkProxyBuffer
//
//  DESCRIPTION:
//
//  Constructor and destructor.  The main things to take care of are the 
//  old style proxy, and the channel
//
//  RETURN VALUE:
//
//  S_OK                all is well
//
//***************************************************************************

CUnboundSinkProxyBuffer::CUnboundSinkProxyBuffer(CLifeControl* pControl, IUnknown* pUnkOuter)
    : m_pControl(pControl), m_pUnkOuter(pUnkOuter), m_lRef(0), 
        m_XUnboundSinkFacelet(this), m_pChannel(NULL), m_pOldProxy( NULL ), m_pOldProxyUnboundSink( NULL ),
        m_fRemote( false )
{
    m_pControl->ObjectCreated(this);
    m_StubType = UNKNOWN;

}

CUnboundSinkProxyBuffer::~CUnboundSinkProxyBuffer()
{
    // This MUST be released first

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

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

    if(m_pChannel)
        m_pChannel->Release();
    m_pControl->ObjectDestroyed(this);

}

ULONG STDMETHODCALLTYPE CUnboundSinkProxyBuffer::AddRef()
{
    return InterlockedIncrement(&m_lRef);
}

ULONG STDMETHODCALLTYPE CUnboundSinkProxyBuffer::Release()
{
    long lRef = InterlockedDecrement(&m_lRef);
    if(lRef == 0)
        delete this;
    return lRef;
}

HRESULT STDMETHODCALLTYPE CUnboundSinkProxyBuffer::QueryInterface(REFIID riid, void** ppv)
{
    if(riid == IID_IUnknown || riid == IID_IRpcProxyBuffer)
    {
        *ppv = (IRpcProxyBuffer*)this;
    }
    else if(riid == IID_IWbemUnboundObjectSink)
    {
        *ppv = (IWbemUnboundObjectSink*)&m_XUnboundSinkFacelet;
    }
    else return E_NOINTERFACE;

    ((IUnknown*)*ppv)->AddRef();
    return S_OK;
}

//***************************************************************************
//
//  HRESULT STDMETHODCALLTYPE CUnboundSinkProxyBuffer::XUnboundSinkFacelet::
//                      QueryInterface(REFIID riid, void** ppv)  
//
//  DESCRIPTION:
//
//  Supports querries for interfaces.   The only thing unusual is that
//  this object is aggregated by the proxy manager and so some interface
//  requests are passed to the outer IUnknown interface.
//
//  RETURN VALUE:
//
//  S_OK                all is well
//
//***************************************************************************

HRESULT STDMETHODCALLTYPE CUnboundSinkProxyBuffer::XUnboundSinkFacelet::
QueryInterface(REFIID riid, void** ppv)
{
    // All other interfaces are delegated to the UnkOuter
    if( riid == IID_IRpcProxyBuffer )
    {
        // Trick #2: this is an internal interface that should not be delegated!
        // ===================================================================

        return m_pObject->QueryInterface(riid, ppv);
    }
    else if ( riid == IID_IClientSecurity )
    {
        // We handle this here in the facelet
        AddRef();
        *ppv = (IClientSecurity*) this;
        return S_OK;
    }
    else
    {
        return m_pObject->m_pUnkOuter->QueryInterface(riid, ppv);
    }
}

//////////////////////////////
//  IClientSecurity Methods //
//////////////////////////////

HRESULT STDMETHODCALLTYPE  CUnboundSinkProxyBuffer::XUnboundSinkFacelet::
QueryBlanket( IUnknown* pProxy, DWORD* pAuthnSvc, DWORD* pAuthzSvc,
    OLECHAR** pServerPrincName, DWORD* pAuthnLevel, DWORD* pImpLevel,
    void** pAuthInfo, DWORD* pCapabilities )
{
    HRESULT hr = S_OK;

    // Return the security as stored in the pUnkOuter.

    IClientSecurity*    pCliSec;

    // We pass through to the PUNKOuter
    hr = m_pObject->m_pUnkOuter->QueryInterface( IID_IClientSecurity, (void**) &pCliSec );

    if ( SUCCEEDED( hr ) )
    {
        hr = pCliSec->QueryBlanket( pProxy, pAuthnSvc, pAuthzSvc, pServerPrincName,
                pAuthnLevel, pImpLevel, pAuthInfo, pCapabilities );
        pCliSec->Release();
    }

    return hr;
}

HRESULT STDMETHODCALLTYPE  CUnboundSinkProxyBuffer::XUnboundSinkFacelet::
SetBlanket( IUnknown* pProxy, DWORD AuthnSvc, DWORD AuthzSvc,
            OLECHAR* pServerPrincName, DWORD AuthnLevel, DWORD ImpLevel,
            void* pAuthInfo, DWORD Capabilities )
{
    HRESULT hr = S_OK;

    IClientSecurity*    pCliSec;

    // First set the security explicitly on our IUnknown, then we will Set the blanket
    // on ourselves using the punkOuter (It's tricky but it works...uh...we think).
    
    // This will enable us to make calls to QueryInterface(), AddRef()/Release() that
    // may have to go remote

    // Only set the IUnknown blanket if we are remoting and it appears that the authinfo contains
    // credentials
    if (    m_pObject->m_fRemote &&
            DoesContainCredentials( (COAUTHIDENTITY*) pAuthInfo ) )
    {
        // This will enable us to make calls to QueryInterface(), AddRef()/Release() that
        // may have to go remote

        hr = CoSetProxyBlanket( m_pObject->m_pUnkOuter, AuthnSvc, AuthzSvc, pServerPrincName,
                    AuthnLevel, ImpLevel, pAuthInfo, Capabilities );

    }

    if ( SUCCEEDED( hr ) )
    {
        // We pass through to the PUNKOuter
        hr = m_pObject->m_pUnkOuter->QueryInterface( IID_IClientSecurity, (void**) &pCliSec );

        if ( SUCCEEDED( hr ) )
        {
            hr = pCliSec->SetBlanket( pProxy, AuthnSvc, AuthzSvc, pServerPrincName,
                    AuthnLevel, ImpLevel, pAuthInfo, Capabilities );
            pCliSec->Release();
        }

    }   // If Set Blanket on IUnknown

    return hr;
}

HRESULT STDMETHODCALLTYPE  CUnboundSinkProxyBuffer::XUnboundSinkFacelet::
CopyProxy( IUnknown* pProxy, IUnknown** ppCopy )
{
    HRESULT hr = S_OK;

    IClientSecurity*    pCliSec;

    // We pass through to the PUNKOuter
    hr = m_pObject->m_pUnkOuter->QueryInterface( IID_IClientSecurity, (void**) &pCliSec );

    if ( SUCCEEDED( hr ) )
    {
        hr = pCliSec->CopyProxy( pProxy, ppCopy );
        pCliSec->Release();
    }

    return hr;
}

//***************************************************************************
//
//  HRESULT STDMETHODCALLTYPE CUnboundSinkProxyBuffer::XUnboundSinkFacelet::
//                      Indicate( LONG lObjectCount, IWbemClassObject** ppObjArray )  
//
//  DESCRIPTION:
//
//  Proxies the IWbemUnboundObjectSink::Indicate calls.  Note that if the stub is an
//  old style, then the old proxy/stub pair in wbemsvc.dll is used for backward
//  compatibility.
//
//  RETURN VALUE:
//
//  S_OK                all is well
//
//***************************************************************************

HRESULT STDMETHODCALLTYPE CUnboundSinkProxyBuffer::XUnboundSinkFacelet::
IndicateToConsumer( IWbemClassObject* pLogicalConsumer, LONG lObjectCount, IWbemClassObject** ppObjArray )
{
    HRESULT hr = S_OK;

    // Make sure the lObjectCount parameter and the array pointer make sense

    if ( lObjectCount < 0 )
    {
        return WBEM_E_INVALID_PARAMETER;
    }
    else if ( lObjectCount == 0 && NULL != ppObjArray )
    {
        return WBEM_E_INVALID_PARAMETER;
    }
    else if ( lObjectCount > 0 && NULL == ppObjArray )
    {
        return WBEM_E_INVALID_PARAMETER;
    }


    CInCritSec ics(&m_cs);

    // If the stublet is an old style, just let the old proxy handle it

    if(m_pObject->m_StubType == OLD) 
        return m_pObject->m_pOldProxyUnboundSink->IndicateToConsumer( pLogicalConsumer, lObjectCount, ppObjArray );

    // If the stublet is unknown, send just the first object and check the return
    // code to determine what is on the other side. 

    if(m_pObject->m_StubType == UNKNOWN) 
    {
        hr = m_pObject->m_pOldProxyUnboundSink->IndicateToConsumer( pLogicalConsumer, 1, ppObjArray );

        // bump up pointer to the next object so that it isnt sent more than once
    
        lObjectCount--;
        ppObjArray++;

        if(hr == WBEM_S_NEW_STYLE)
        {
            m_pObject->m_StubType = NEW;
        }
        else
        {
            // We have an old client, set the stub type and send any remaining objects

            m_pObject->m_StubType = OLD;
            if(lObjectCount > 0)
                hr = m_pObject->m_pOldProxyUnboundSink->IndicateToConsumer( pLogicalConsumer, lObjectCount, ppObjArray );
            return hr;
        }
    }


    if(lObjectCount < 1)
        return S_OK;            // if all done, then just return.

    // Create a packet and some data for it to use.  Then calculate 
    // the length of the packet

    DWORD dwLength;
    GUID* pguidClassIds = new GUID[lObjectCount];
    BOOL* pfSendFullObject = new BOOL[lObjectCount];

    // arrays will be deleted when we drop out of scope.
    CVectorDeleteMe<GUID>   delpguidClassIds( pguidClassIds );
    CVectorDeleteMe<BOOL>   delpfSendFullObject( pfSendFullObject );

    CWbemUnboundSinkIndicatePacket packet;
    hr = packet.CalculateLength( pLogicalConsumer, lObjectCount, ppObjArray, &dwLength, 
            m_pObject->m_ClassToIdMap, pguidClassIds, pfSendFullObject );

    // Declare the message structure

    RPCOLEMESSAGE msg;
    memset(&msg, 0, sizeof(msg));
    msg.cbBuffer = dwLength;

    // This is the id of the Invoke function.  This MUST be set before calling GetBuffer, or 
    // it will fail.

    msg.iMethod = 3;

    // allocate the channel buffer and marshal the data into it

    HRESULT hres = m_pObject->GetChannel()->GetBuffer(&msg, IID_IWbemUnboundObjectSink);
    if(FAILED(hres)) return hres;

    // Setup the packet for marshaling
    hr = packet.MarshalPacket(  (LPBYTE)msg.Buffer, dwLength, pLogicalConsumer, lObjectCount, ppObjArray, 
                                 pguidClassIds, pfSendFullObject);

    // Send the data to the stub only if the marshaling was successful

    if ( SUCCEEDED( hr ) )
    {

        DWORD dwRes;
        hr = m_pObject->GetChannel()->SendReceive(&msg, &dwRes);
        if(FAILED(hr))
        {
            if(msg.Buffer)
                m_pObject->GetChannel()->FreeBuffer(&msg);
            return dwRes;
        }

        // We appear to be ok, so get HRESULT

        LPBYTE pbData = (LPBYTE) msg.Buffer;
        hr = *((HRESULT*) pbData);
        m_pObject->GetChannel()->FreeBuffer(&msg);

    }
    else
    {
        // Clean up the buffer -- Marshaling the packet failed
        if(msg.Buffer)
            m_pObject->GetChannel()->FreeBuffer(&msg);
    }

    return hr;

}

//***************************************************************************
//
//  STDMETHODIMP CUnboundSinkProxyBuffer::Connect(IRpcChannelBuffer* pChannel)
//
//  DESCRIPTION:
//
//  Called during the initialization of the proxy.  The channel buffer is passed
//  to this routine.
//
//  RETURN VALUE:
//
//  S_OK                all is well
//
//***************************************************************************

STDMETHODIMP CUnboundSinkProxyBuffer::Connect(IRpcChannelBuffer* pChannel)
{

    // get a pointer to the old UnboundSink which is in WBEMSVC.DLL  this allows
    // for backward compatibility

    IPSFactoryBuffer*   pIPS;

    // Establish the marshaling context
    DWORD   dwCtxt = 0;
    pChannel->GetDestCtx( &dwCtxt, NULL );

    m_fRemote = ( dwCtxt == MSHCTX_DIFFERENTMACHINE );

    // This is tricky --- The old proxys/stub stuff is actually registered under the
    // IID_IWbemObjectSink in wbemcli_p.cpp.  This single class id, is backpointered
    // by ProxyStubClsId32 entries for all the standard WBEM interfaces.


    HRESULT hr = CoGetClassObject( IID_IWbemObjectSink, CLSCTX_INPROC_HANDLER | CLSCTX_INPROC_SERVER,
                    NULL, IID_IPSFactoryBuffer, (void**) &pIPS );

    // We aggregated it --- WE OWN IT!
    
    hr = pIPS->CreateProxy( this, IID_IWbemUnboundObjectSink, &m_pOldProxy, (void**) &m_pOldProxyUnboundSink );
    pIPS->Release();

    // Save a reference to the channel

    hr = m_pOldProxy->Connect( pChannel );

    if(m_pChannel)
        return E_UNEXPECTED;
    
    m_pChannel = pChannel;
    if(m_pChannel)
        m_pChannel->AddRef();

    return S_OK;
}

//***************************************************************************
//
//  STDMETHODIMP CUnboundSinkProxyBuffer::Disconnect(IRpcChannelBuffer* pChannel)
//
//  DESCRIPTION:
//
//  Called when the proxy is being disconnected.  It just frees various pointers.
//
//  RETURN VALUE:
//
//  S_OK                all is well
//
//***************************************************************************

void STDMETHODCALLTYPE CUnboundSinkProxyBuffer::Disconnect()
{
    // Old Proxy code

    if(m_pOldProxy)
        m_pOldProxy->Disconnect();

    // Complete the Disconnect by releasing our references to the
    // old proxy pointers.  The old Proxy UnboundSink MUST be released first.

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

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

    if(m_pChannel)
        m_pChannel->Release();
    m_pChannel = NULL;
}

//****************************************************************************
//****************************************************************************
//                          STUB
//****************************************************************************
//****************************************************************************


//***************************************************************************
//
//  void* CUnboundSinkFactoryBuffer::GetInterface(REFIID riid)
//
//  DESCRIPTION:
//
//  CUnboundSinkFactoryBuffer is derived from CUnk.  Since CUnk handles the QI calls,
//  all classes derived from this must support this function.
//
//  RETURN VALUE:
//
//  S_OK                all is well
//
//***************************************************************************


void* CUnboundSinkStubBuffer::GetInterface(REFIID riid)
{
    if(riid == IID_IRpcStubBuffer)
        return &m_XUnboundSinkStublet;
    else
        return NULL;
}

CUnboundSinkStubBuffer::XUnboundSinkStublet::XUnboundSinkStublet(CUnboundSinkStubBuffer* pObj) 
    : CImpl<IRpcStubBuffer, CUnboundSinkStubBuffer>(pObj), m_pServer(NULL), m_lConnections( 0 )
{
    m_bFirstIndicate = true;
}

CUnboundSinkStubBuffer::XUnboundSinkStublet::~XUnboundSinkStublet() 
{
    if(m_pServer)
        m_pServer->Release();

    if ( NULL != m_pObject->m_pOldStub )
    {
        m_pObject->m_pOldStub->Release();
        m_pObject->m_pOldStub = NULL;
    }
}

//***************************************************************************
//
//  STDMETHODIMP CUnboundSinkStubBuffer::Connect(IUnknown* pUnkServer)
//
//  DESCRIPTION:
//
//  Called during the initialization of the stub.  The pointer to the
//  IWbemObject UnboundSink object is passed in.
//
//  RETURN VALUE:
//
//  S_OK                all is well
//
//***************************************************************************

STDMETHODIMP CUnboundSinkStubBuffer::XUnboundSinkStublet::Connect(IUnknown* pUnkServer)
{
    if(m_pServer)
        return E_UNEXPECTED;

    HRESULT hres = pUnkServer->QueryInterface(IID_IWbemUnboundObjectSink, 
                        (void**)&m_pServer);
    if(FAILED(hres))
        return E_NOINTERFACE;

    // get a pointer to the old stub which is in WBEMSVC.DLL  this allows
    // for backward compatibility

    IPSFactoryBuffer*   pIPS;

    // This is tricky --- The old proxys/stub stuff is actually registered under the
    // IID_IWbemObjectSink in wbemcli_p.cpp.  This single class id, is backpointered
    // by ProxyStubClsId32 entries for all the standard WBEM interfaces.


    HRESULT hr = CoGetClassObject( IID_IWbemObjectSink, CLSCTX_INPROC_HANDLER | CLSCTX_INPROC_SERVER,
                    NULL, IID_IPSFactoryBuffer, (void**) &pIPS );

    hr = pIPS->CreateStub( IID_IWbemUnboundObjectSink, m_pServer, &m_pObject->m_pOldStub );

    pIPS->Release();

    // Successful connection

    m_lConnections++;
    return S_OK;
}

//***************************************************************************
//
//  void STDMETHODCALLTYPE CUnboundSinkStubBuffer::XUnboundSinkStublet::Disconnect()
//
//  DESCRIPTION:
//
//  Called when the stub is being disconnected.  It frees the IWbemUnboundObjectSink
//  pointer.
//
//  RETURN VALUE:
//
//  S_OK                all is well
//
//***************************************************************************

void STDMETHODCALLTYPE CUnboundSinkStubBuffer::XUnboundSinkStublet::Disconnect()
{
    // Inform the listener of the disconnect
    // =====================================

    HRESULT hres = S_OK;

    if(m_pObject->m_pOldStub)
        m_pObject->m_pOldStub->Disconnect();

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

    // Successful disconnect
    m_lConnections--;

}


//***************************************************************************
//
//  STDMETHODIMP CUnboundSinkStubBuffer::XUnboundSinkStublet::Invoke(RPCOLEMESSAGE* pMessage, 
//                                        IRpcChannelBuffer* pChannel)
//
//  DESCRIPTION:
//
//  Called when a method reaches the stublet.  This checks the method id and
//  then branches to specific code for the Indicate, or SetStatus calls.
//
//  RETURN VALUE:
//
//  S_OK                all is well
//
//***************************************************************************

STDMETHODIMP CUnboundSinkStubBuffer::XUnboundSinkStublet::Invoke(RPCOLEMESSAGE* pMessage, 
                                        IRpcChannelBuffer* pChannel)
{
    // SetStatus is a pass through to the old layer

    if ( pMessage->iMethod == 3 )
        return IndicateToConsumer_Stub( pMessage, pChannel );
    else
        return RPC_E_SERVER_CANTUNMARSHAL_DATA;

}

//***************************************************************************
//
//  HRESULT CUnboundSinkStubBuffer::XUnboundSinkStublet::IndicateToConsumer_Stub( RPCOLEMESSAGE* pMessage, 
//                                                        IRpcChannelBuffer* pBuffer )
//
//  DESCRIPTION:
//
//  Handles the Indicate function in the stublet.
//
//  RETURN VALUE:
//
//  S_OK                all is well
//
//***************************************************************************

HRESULT CUnboundSinkStubBuffer::XUnboundSinkStublet::IndicateToConsumer_Stub( RPCOLEMESSAGE* pMessage, IRpcChannelBuffer* pBuffer )
{
    HRESULT             hr = RPC_E_SERVER_CANTUNMARSHAL_DATA;
    SCODE sc;

    // Determine if an old style, or new style packet has arrived

    CWbemUnboundSinkIndicatePacket packet( (LPBYTE) pMessage->Buffer, pMessage->cbBuffer);
    sc = packet.IsValid();
    bool bOldStyle = (S_OK != packet.IsValid());

    if(bOldStyle)
    {
        // Pass the call in using the old style stub

        hr = m_pObject->m_pOldStub->Invoke( pMessage, pBuffer );
        if(hr == S_OK && m_bFirstIndicate)
        {
            // Let proxy know that we can handle the new style by returning a special return code.
        
            *(( HRESULT __RPC_FAR * )pMessage->Buffer) = WBEM_S_NEW_STYLE;
            m_bFirstIndicate = false;
            return hr;
        }
        return hr;
    }

    m_bFirstIndicate = false;

    // Got some new style data.  Unmarshall it.

    long lObjectCount = 0; 
    IWbemClassObject*   pLogicalConsumer = NULL;
    IWbemClassObject ** pObjArray = NULL;
    sc = packet.UnmarshalPacket( pLogicalConsumer, lObjectCount, pObjArray, m_ClassCache );

    // Only continue if the Unmarshaling succeeded.  If it failed, we still want
    // the sc to go back to the other side

    if ( SUCCEEDED( sc ) )
    {

        // Call the acual UnboundSink
        sc = m_pServer->IndicateToConsumer( pLogicalConsumer, lObjectCount, pObjArray );

        for ( int nCtr = 0; nCtr < lObjectCount; nCtr++ )
        {
            pObjArray[nCtr]->Release();
        }
    
        delete [] pObjArray;

        // Done with the logical consumer
        if ( NULL != pLogicalConsumer )
        {
            pLogicalConsumer->Release();
        }

    }

    // Send the results back

    pMessage->cbBuffer = sizeof(HRESULT);

    hr = pBuffer->GetBuffer( pMessage, IID_IWbemUnboundObjectSink );

    if ( SUCCEEDED( hr ) )
    {
        ((HRESULT*)pMessage->Buffer)[0] = sc;
    }
    else
    {
        hr = sc;
    }
    return hr;

}

IRpcStubBuffer* STDMETHODCALLTYPE CUnboundSinkStubBuffer::XUnboundSinkStublet::IsIIDSupported(
                                    REFIID riid)
{
    if(riid == IID_IWbemUnboundObjectSink)
    {
        // Don't AddRef().  At least that's what the sample on
        // Inside DCOM p.341 does.
        //AddRef(); // ?? not sure
        return this;
    }
    else return NULL;
}
    
ULONG STDMETHODCALLTYPE CUnboundSinkStubBuffer::XUnboundSinkStublet::CountRefs()
{
    // See Page 340-41 in Inside DCOM
    return m_lConnections;
}

STDMETHODIMP CUnboundSinkStubBuffer::XUnboundSinkStublet::DebugServerQueryInterface(void** ppv)
{
    if(m_pServer == NULL)
        return E_UNEXPECTED;

    *ppv = m_pServer;
    return S_OK;
}

void STDMETHODCALLTYPE CUnboundSinkStubBuffer::XUnboundSinkStublet::DebugServerRelease(void* pv)
{
}