/*++

Copyright (C) 1998-2001 Microsoft Corporation

Module Name:

    MTGTMRSH.CPP

Abstract:

    Multi Target Marshaling.

History:

--*/

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

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

//***************************************************************************
//
//  CMultiTargetFactoryBuffer::XEnumFactory::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 CMultiTargetFactoryBuffer::XEnumFactory::CreateProxy(IN IUnknown* pUnkOuter, 
    IN REFIID riid, OUT IRpcProxyBuffer** ppProxy, void** ppv)
{
    if(riid != IID_IWbemMultiTarget)
    {
        *ppProxy = NULL;
        *ppv = NULL;
        return E_NOINTERFACE;
    }

    CMultiTargetProxyBuffer* pProxy = new CMultiTargetProxyBuffer(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;
}

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

    CMultiTargetStubBuffer* pStub = new CMultiTargetStubBuffer(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* CMultiTargetFactoryBuffer::GetInterface(REFIID riid)
//
//  DESCRIPTION:
//
//  CMultiTargetFactoryBuffer 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* CMultiTargetFactoryBuffer::GetInterface(REFIID riid)
{
    if(riid == IID_IPSFactoryBuffer)
        return &m_XEnumFactory;
    else return NULL;
}
        
//****************************************************************************
//****************************************************************************
//                          PROXY
//****************************************************************************
//****************************************************************************

//***************************************************************************
//
//  CMultiTargetProxyBuffer::CMultiTargetProxyBuffer
//  ~CMultiTargetProxyBuffer::CMultiTargetProxyBuffer
//
//  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
//
//***************************************************************************

CMultiTargetProxyBuffer::CMultiTargetProxyBuffer(CLifeControl* pControl, IUnknown* pUnkOuter)
    : m_pControl(pControl), m_pUnkOuter(pUnkOuter), m_lRef(0), 
        m_XMultiTargetFacelet(this), m_pChannel(NULL), m_pOldProxy( NULL ), m_pOldProxyMultiTarget( NULL ),
        m_fTriedSmartEnum( FALSE ), m_fUseSmartMultiTarget( FALSE ), m_hSmartNextMutex( INVALID_HANDLE_VALUE ),
        m_pSmartMultiTarget( NULL ), m_fRemote( false )
{
    m_pControl->ObjectCreated(this);
    InitializeCriticalSection( &m_cs );
//    m_StubType = UNKNOWN;

}

CMultiTargetProxyBuffer::~CMultiTargetProxyBuffer()
{
    if ( NULL != m_pSmartMultiTarget )
    {
        m_pSmartMultiTarget->Release();
    }

    // This MUST be released before releasing
    // the Proxy pointer
    if ( NULL != m_pOldProxyMultiTarget )
    {
        m_pOldProxyMultiTarget->Release();
    }

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

    if(m_pChannel)
        m_pChannel->Release();

    // Cleanup the mutex
    if ( INVALID_HANDLE_VALUE != m_hSmartNextMutex )
    {
        CloseHandle( m_hSmartNextMutex );
    }

    m_pControl->ObjectDestroyed(this);

    DeleteCriticalSection( &m_cs );

}

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

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

HRESULT STDMETHODCALLTYPE CMultiTargetProxyBuffer::QueryInterface(REFIID riid, void** ppv)
{
    if(riid == IID_IUnknown || riid == IID_IRpcProxyBuffer)
    {
        *ppv = (IRpcProxyBuffer*)this;
    }
    else if(riid == IID_IWbemMultiTarget)
    {
        *ppv = (IWbemMultiTarget*)&m_XMultiTargetFacelet;
    }
    else return E_NOINTERFACE;

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

//***************************************************************************
//
//  HRESULT STDMETHODCALLTYPE CMultiTargetProxyBuffer::XMultiTargetFacelet::
//                      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 CMultiTargetProxyBuffer::XMultiTargetFacelet::
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  CMultiTargetProxyBuffer::XMultiTargetFacelet::
QueryBlanket( IUnknown* pProxy, DWORD* pAuthnSvc, DWORD* pAuthzSvc,
    OLECHAR** pServerPrincName, DWORD* pAuthnLevel, DWORD* pImpLevel,
    void** pAuthInfo, DWORD* pCapabilities )
{
    HRESULT hr = S_OK;

    // Return our 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  CMultiTargetProxyBuffer::XMultiTargetFacelet::
SetBlanket( IUnknown* pProxy, DWORD AuthnSvc, DWORD AuthzSvc,
            OLECHAR* pServerPrincName, DWORD AuthnLevel, DWORD ImpLevel,
            void* pAuthInfo, DWORD Capabilities )
{
    HRESULT hr = S_OK;

    IClientSecurity*    pCliSec;

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

        // Make sure we have a smart enumerator and that we are going to
        // be using it.  If so, make sure the values applied to us are also
        // applied to it's proxy

        if ( SUCCEEDED( m_pObject->InitSmartMultiTarget( TRUE, AuthnSvc, AuthzSvc, pServerPrincName,
                    AuthnLevel, ImpLevel, pAuthInfo, Capabilities ) ) && m_pObject->m_fUseSmartMultiTarget )
        {
            // Now repeat the above operation for the smart enumerator
            // Ignore the IUnknown if we are not remoting
            hr = WbemSetProxyBlanket(  m_pObject->m_pSmartMultiTarget, AuthnSvc, AuthzSvc, pServerPrincName,
                    AuthnLevel, ImpLevel, pAuthInfo, Capabilities, !m_pObject->m_fRemote );

        }   // If initialized smart enumerator

    }   // If Set Blanket on IUnknown

    return hr;
}

HRESULT STDMETHODCALLTYPE  CMultiTargetProxyBuffer::XMultiTargetFacelet::
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;
}

//////////////////////////////////////////////
//////////////////////////////////////////////

//  IWbemMultiTarget Methods -- Pass Thrus for now

//////////////////////////////////////////////
//////////////////////////////////////////////

HRESULT STDMETHODCALLTYPE CMultiTargetProxyBuffer::XMultiTargetFacelet::
DeliverEvent(DWORD dwNumEvents, IWbemClassObject** apEvents, WBEM_REM_TARGETS* aTargets,
           long lSDLength, BYTE* pSD)
{
    // Also, we will need to queue calls into this proxy, preserving timeouts,
    // so I'm thinking a mutex would come in handy about now...

    HRESULT hr = WBEM_S_NO_ERROR;

    // Make sure we have a smart enumerator if we can get one
    m_pObject->InitSmartMultiTarget();

    // If we have a smart enumerator, go behind everyone's back and use this guy (nobody
    // will be the wiser...

    if ( m_pObject->m_fUseSmartMultiTarget && NULL != m_pObject->m_pSmartMultiTarget )
    {

        // Function MUST be thread safe
        CInCritSec ics(&m_pObject->m_cs);

        // Calculate data length first 
        DWORD dwLength;

        GUID*   pGUIDs = NULL;
        BOOL*   pfSendFullObject = NULL;

        try
        {
            // Allocate arrays for the guid and the flags
            pGUIDs = new GUID[dwNumEvents];
            pfSendFullObject = new BOOL[dwNumEvents];

            CWbemMtgtDeliverEventPacket packet;
            hr = packet.CalculateLength(dwNumEvents, apEvents, &dwLength, 
                    m_ClassToIdMap, pGUIDs, pfSendFullObject );

            if ( SUCCEEDED( hr ) )
            {

                // As we could be going cross process/machine, use the
                // COM memory allocator
                LPBYTE pbData = (LPBYTE) CoTaskMemAlloc( dwLength );

                if ( NULL != pbData )
                {

                    // Write the objects out to the buffer
                    hr = packet.MarshalPacket( pbData, dwLength, dwNumEvents, 
                                                apEvents, pGUIDs, pfSendFullObject);

                    // Copy the values, we're golden.
                    if ( SUCCEEDED( hr ) )
                    {
                        // Now we can send the data to the stub
                        hr = m_pObject->m_pSmartMultiTarget->DeliverEvent( dwNumEvents, dwLength, pbData, aTargets, lSDLength, pSD );
                    }

                    // Because the buffer is an [in] parameter, it lies on our heads
                    // to free it up.
                    CoTaskMemFree( pbData );
                }
                else
                {
                    hr = WBEM_E_OUT_OF_MEMORY;
                }

            }   // IF CalculateLength()
        }
        catch (CX_MemoryException)
        {
            hr = WBEM_E_OUT_OF_MEMORY;
        }
        catch(...)
        {
            hr = WBEM_E_FAILED;
        }

        // Cleanup the arrays
        if ( NULL != pGUIDs )
        {
            delete [] pGUIDs;
        }

        if ( NULL != pfSendFullObject )
        {
            delete pfSendFullObject;
        }

    }   // IF using Smart Enumeration
    else
    {
        // No Smart enumerator (doh!), so use the old one
        hr = m_pObject->m_pOldProxyMultiTarget->DeliverEvent(dwNumEvents, apEvents, aTargets, lSDLength, pSD );
    }

    return hr;
}

HRESULT STDMETHODCALLTYPE CMultiTargetProxyBuffer::XMultiTargetFacelet::
DeliverStatus( long lFlags, HRESULT hresStatus, LPCWSTR wszStatus, IWbemClassObject* pErrorObj,
            WBEM_REM_TARGETS* pTargets, long lSDLength, BYTE* pSD)
{
    // Just pass through to the old sink.
    return m_pObject->m_pOldProxyMultiTarget->DeliverStatus( lFlags, hresStatus, wszStatus, pErrorObj, pTargets, lSDLength, pSD );
}

/*
HRESULT STDMETHODCALLTYPE CMultiTargetProxyBuffer::XMultiTargetFacelet::
      Next(long lTimeout, ULONG uCount, IWbemClassObject** apObj, ULONG FAR* puReturned)
{

    // At this point we will Query for the new, improved IEnumWCOSmartNext interface.
    // If we get it, we will maintain a pointer to that interface and
    // pass through to that interface.  We will also call CoCreateGuid() so
    // we get a unique identifier on the other end for sending wbem objects
    // back and forth cleanly.

    // The interface will have a single method IEnumWCOSmartNext::Next
    // This will take a GUID identifying this proxy, lTimeout, uCount,
    // puReturned, then dwBuffSize and BYTE**.

    // The other end will allocate memory via CoTaskMemAlloc and this side will
    // Free it via CoTaskMemFree.

    // The other side will Marshal returned objects into the memory block.
    // This side will Unmarshal it (and then free the block).

    //
    //  SAMPLE IDL:
    //  IEnumWCOSmartNext::Next(    [in] GUID proxyGUID,
    //                              [in] LONG lTimeout,
    //                              [in] unsigned long uCount,
    //                              [in, out] DWORD* puReturned,
    //                              [in, out] DWORD* pdwBuffSize,
    //                              [in, out, size_is[,*pdwBuffSize] BYTE** pBuffer
    //

    // Also, we will need to queue calls into this proxy, preserving timeouts,
    // so I'm thinking a mutex would come in handy about now...

    HRESULT hr = WBEM_S_NO_ERROR;

    // Make sure the timeout value makes sense
    if ( lTimeout < 0 && lTimeout != WBEM_INFINITE )
    {
        return WBEM_E_INVALID_PARAMETER;
    }

    // Make sure we have a smart enumerator if we can get one
    m_pObject->InitSmartMultiTarget();

    // If we have a smart enumerator, go behind everyone's back and use this guy (nobody
    // will be the wiser...

    if ( m_pObject->m_fUseSmartMultiTarget && NULL != m_pObject->m_pSmartMultiTarget )
    {
        DWORD   dwOldTick = GetTickCount();
        DWORD   dwReturn = WaitForSingleObject( m_pObject->m_hSmartNextMutex, lTimeout );

        if (  WAIT_OBJECT_0 == dwReturn )
        {
            BYTE*   pBuffer = NULL;
            ULONG   uSizeOfBuffer = 0;

            // Adjust timeout (if it was > 0) for any milliseconds we may
            // have just been waiting.

            if ( lTimeout > 0 )
            {
                // Get the current tick count.  Be aware that a tick count will
                // rollover every 30 some odd days, so trap for this case by
                // checking that the new tick count >= the old one
                
                DWORD   dwCurrTick = GetTickCount();
                dwCurrTick = ( dwCurrTick >= dwOldTick ? dwCurrTick : dwOldTick );

                // Adjust the timeout, but don't let it fall below 0
                lTimeout -= ( dwCurrTick - dwOldTick );
                lTimeout = ( lTimeout < 0 ? 0 : lTimeout );
            }

            // Ask the server for objects
            hr = m_pObject->m_pSmartMultiTarget->Next( m_pObject->m_guidSmartEnum, lTimeout, uCount, puReturned, &uSizeOfBuffer, &pBuffer );

            // Only need to unmarshal if objects are in the buffer
            if ( SUCCEEDED( hr ) && *puReturned > 0 )
            {

                CWbemSmartEnumNextPacket packet( (LPBYTE) pBuffer, uSizeOfBuffer );
                long lObjectCount; 
                IWbemClassObject ** pObjArray;
                hr = packet.UnmarshalPacket( lObjectCount, pObjArray, m_ClassCache );

                if ( SUCCEEDED( hr ) )
                {
                    // Copy *puReturned pointers from the allocated pObjArray into apObj.
                    CopyMemory( apObj, pObjArray, ( *puReturned * sizeof(IWbemClassObject*) ) );

                    // Clean up pObjArray  It is the caller's responsibility to free
                    // the IWbemClassObject* pointers.
                    delete [] pObjArray;

                }   // IF UnmarshalPacket

                // Free the memory buffer (allocated by WinMgmt via CoTaskMemAlloc)
                CoTaskMemFree( pBuffer );

            }   // IF Next

            ReleaseMutex( m_pObject->m_hSmartNextMutex );

        }   // IF WAIT_OBJECT_0
        else if ( WAIT_TIMEOUT == dwReturn )
        {
            // Timed out on the mutex
            hr = WBEM_S_TIMEDOUT;
        }
        else
        {
            hr = WBEM_E_FAILED;
        }

    }   // IF using Smart Enumeration
    else
    {
        // No Smart enumerator (doh!), so use the old one
        hr = m_pObject->m_pOldProxyMultiTarget->Next( lTimeout, uCount, apObj, puReturned );
    }

    return hr;

}
*/

//***************************************************************************
//
//  STDMETHODIMP CMultiTargetProxyBuffer::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 CMultiTargetProxyBuffer::Connect(IRpcChannelBuffer* pChannel)
{

    // get a pointer to the old sink 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_IWbemMultiTarget, &m_pOldProxy, (void**) &m_pOldProxyMultiTarget );
    pIPS->Release();

    // Connect the old proxy to the channel
    hr = m_pOldProxy->Connect( pChannel );

    // Save an internal reference to the channel
    if(m_pChannel)
        return E_UNEXPECTED;
    
    m_pChannel = pChannel;
    if(m_pChannel)
        m_pChannel->AddRef();

    return hr;
}

//***************************************************************************
//
//  HRESULT CMultiTargetProxyBuffer::InitSmartMultiTarget(void)
//
//  DESCRIPTION:
//
//  Called during the initialization of the proxy.  This function sets up
//  the smart enumerator pointer so we can perform intelligent marshaling.
//  This cannot be called during a Connect operation.
//
//  RETURN VALUE:
//
//  S_OK                all is well
//
//***************************************************************************

HRESULT CMultiTargetProxyBuffer::InitSmartMultiTarget( BOOL fSetBlanket, DWORD AuthnSvc, DWORD AuthzSvc,
            OLECHAR* pServerPrincName, DWORD AuthnLevel, DWORD ImpLevel,
            void* pAuthInfo, DWORD Capabilities )
{
    HRESULT hr = WBEM_S_NO_ERROR;

    // Function MUST be thread safe
    CInCritSec ics(&m_cs);

    // If we haven't tried to estalish smart enumeration, do so now

    // If we haven't tried to get a smart enumerator, try to get one.  If
    // we are able to get one, initialize member data we will use in all
    // operations from this proxy.

    if ( NULL == m_pSmartMultiTarget )
    {

        // We'll only get this interface pointer if the server is a new
        // version that understands this interface.  If it does, the pointer
        // will be marshaled through for us.  To get to this pointer,
        // we go directly through our punkOuter.  From the "fetcher" interface
        // we will then get the actual smart enumerator.  We can then free up
        // the fetcher and release it's lock on the proxy manager.  The
        // smart enumerator will be handled on its own.

        IWbemFetchSmartMultiTarget* pFetchSmartMultiTarget;

        hr = m_pUnkOuter->QueryInterface( IID_IWbemFetchSmartMultiTarget, (void**) &pFetchSmartMultiTarget );

        // Generate a GUID to identify us when we call the smart enumerator
        if ( SUCCEEDED( hr ) )
        {

            // If we need to, set the blanket on the proxy, otherwise, the call to GetSmartEnum
            // may fail.
            if ( fSetBlanket )
            {
                // Ignore the IUnknown if we are not remoting
                hr = WbemSetProxyBlanket( pFetchSmartMultiTarget, AuthnSvc, AuthzSvc, pServerPrincName,
                            AuthnLevel, ImpLevel, pAuthInfo, Capabilities, !m_fRemote );
            }

            if ( SUCCEEDED( hr ) )
            {

                hr = pFetchSmartMultiTarget->GetSmartMultiTarget( &m_pSmartMultiTarget );

                if ( SUCCEEDED( hr ) )
                {
                    // We need a GUID
                    hr = CoCreateGuid( &m_guidSmartEnum );

                    if ( SUCCEEDED( hr ) )
                    {
                        // We'll also need a Mutex (so we can timeout) here

                        m_hSmartNextMutex = CreateMutex( NULL, FALSE, NULL );

                        if ( INVALID_HANDLE_VALUE != m_hSmartNextMutex )
                        {
                            // We have everything we need to do things smartly
                            m_fUseSmartMultiTarget = TRUE;
                        }
                    }   // IF CoCreateGuid

                }   // IF got Smart MultiTarget

            }   // IF security OK
            
            // Done with the fetcher interface
            pFetchSmartMultiTarget->Release();

        }   // IF QueryInterface
        else
        {
            hr = WBEM_S_NO_ERROR;
        }

    }   // IF NULL == m_pSmartMultiTarget

    return hr;
}

//***************************************************************************
//
//  STDMETHODIMP CMultiTargetProxyBuffer::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 CMultiTargetProxyBuffer::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 Enum MUST be released first.

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

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

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

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


//***************************************************************************
//
//  void* CMultiTargetFactoryBuffer::GetInterface(REFIID riid)
//
//  DESCRIPTION:
//
//  CMultiTargetFactoryBuffer 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* CMultiTargetStubBuffer::GetInterface(REFIID riid)
{
    if(riid == IID_IRpcStubBuffer)
        return &m_XMultiTargetStublet;
    else
        return NULL;
}

CMultiTargetStubBuffer::XMultiTargetStublet::XMultiTargetStublet(CMultiTargetStubBuffer* pObj) 
    : CImpl<IRpcStubBuffer, CMultiTargetStubBuffer>(pObj), m_pServer(NULL), m_lConnections( 0 )
{
}

CMultiTargetStubBuffer::XMultiTargetStublet::~XMultiTargetStublet() 
{
    if(m_pServer)
        m_pServer->Release();

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

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

STDMETHODIMP CMultiTargetStubBuffer::XMultiTargetStublet::Connect(IUnknown* pUnkServer)
{
    if(m_pServer)
        return E_UNEXPECTED;

    HRESULT hres = pUnkServer->QueryInterface(IID_IWbemMultiTarget, 
                        (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_IWbemMultiTarget, m_pServer, &m_pObject->m_pOldStub );

    pIPS->Release();

    // Successful connection

    m_lConnections++;
    return S_OK;
}

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

void STDMETHODCALLTYPE CMultiTargetStubBuffer::XMultiTargetStublet::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 CMultiTargetStubBuffer::XMultiTargetStublet::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 CMultiTargetStubBuffer::XMultiTargetStublet::Invoke(RPCOLEMESSAGE* pMessage, 
                                        IRpcChannelBuffer* pChannel)
{
    // SetStatus is a pass through to the old layer
    return m_pObject->m_pOldStub->Invoke( pMessage, pChannel );
}

IRpcStubBuffer* STDMETHODCALLTYPE CMultiTargetStubBuffer::XMultiTargetStublet::IsIIDSupported(
                                    REFIID riid)
{
    if(riid == IID_IWbemMultiTarget)
    {
        // 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 CMultiTargetStubBuffer::XMultiTargetStublet::CountRefs()
{
    // See Page 340-41 in Inside DCOM
    return m_lConnections;
}

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

    *ppv = m_pServer;
    return S_OK;
}

void STDMETHODCALLTYPE CMultiTargetStubBuffer::XMultiTargetStublet::DebugServerRelease(void* pv)
{
}