2025-04-27 07:49:33 -04:00

1366 lines
37 KiB
C++

/*++
Copyright (C) 1996-2001 Microsoft Corporation
Module Name:
OBJENUM.CPP
Abstract:
Implements the class implementing IEnumWbemClassObject interface.
Classes defined:
CEnumWbemClassObject
History:
a-raymcc 16-Jul-96 Created.
--*/
#include "precomp.h"
#include <wbemcore.h>
// Read in by the config manager. Controls our cache and overflow buffer sizes as
// well as timeouts
extern DWORD g_dwMaxEnumCacheSize;
extern DWORD g_dwMaxEnumOverflowSize;
extern DWORD g_dwEnumOverflowTimeout;
// Marshaling Packet definitions
#include <wbemclasstoidmap.h>
#include <wbemguidtoclassmap.h>
#include <smartnextpacket.h>
//***************************************************************************
//
// See objenum.h for documentation.
//
//***************************************************************************
SCODE CBasicEnumWbemClassObject::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
{
*ppvObj = 0;
if (IID_IUnknown==riid || IID_IEnumWbemClassObject == riid)
{
*ppvObj = (IEnumWbemClassObject*)this;
AddRef();
return NOERROR;
}
else if ( IID_IWbemFetchSmartEnum == riid )
{
*ppvObj = (IWbemFetchSmartEnum*) this;
AddRef();
return NOERROR;
}
return ResultFromScode(E_NOINTERFACE);
}
//***************************************************************************
//
// See objenum.h for documentation.
//
//***************************************************************************
ULONG CBasicEnumWbemClassObject::AddRef()
{
return InterlockedIncrement(&m_lRefCount);
}
//***************************************************************************
//
// See objenum.h for documentation.
//
//***************************************************************************
ULONG CBasicEnumWbemClassObject::Release()
{
_ASSERT(m_lRefCount > 0, "Release() called with no matching AddRef()");
long lRef = InterlockedDecrement(&m_lRefCount);
if (0 != lRef)
return lRef;
delete this;
return 0;
}
//***************************************************************************
//***************************************************************************
//
// ASYNC
//
//***************************************************************************
//***************************************************************************
CDataAccessor::CDataAccessor() : m_dwTotal( 0 ), m_dwNumObjects( 0 )
{
ConfigMgr::AddCache();
}
CDataAccessor::~CDataAccessor()
{
ConfigMgr::RemoveCache();
}
BOOL CDataAccessor::RecordAdd(IWbemClassObject* p)
{
DWORD dwSize = ((CWbemObject*)p)->GetBlockLength();
m_dwTotal += dwSize;
++m_dwNumObjects;
return TRUE;
}
void CDataAccessor::ReportRemove(IWbemClassObject* p)
{
DWORD dwSize = ((CWbemObject*)p)->GetBlockLength();
m_dwTotal -= dwSize;
--m_dwNumObjects;
ConfigMgr::RemoveFromCache(dwSize);
}
class CForwardAccessor : public CDataAccessor
{
protected:
CFlexQueue m_q;
public:
CForwardAccessor(){}
virtual ~CForwardAccessor();
BOOL Add(IWbemClassObject* p)
{
RecordAdd(p);
if(m_q.Enqueue(p)) {p->AddRef(); return TRUE;}
else return FALSE;
}
BOOL Next(DWORD& rdwPos, IWbemClassObject*& rp)
{
if((rp = (IWbemClassObject*)m_q.Dequeue()) != NULL)
{
rdwPos++;
ReportRemove(rp);
return TRUE;
}
else return FALSE;
}
BOOL IsResettable() {return FALSE;}
void Clear( void );
// Return whether or not the queue is empty
BOOL IsDone( DWORD dwPos ) { return m_q.GetQueueSize() == 0; }
};
class CBiAccessor : public CDataAccessor
{
protected:
CFlexArray m_a;
public:
~CBiAccessor();
BOOL Add(IWbemClassObject* p)
{
RecordAdd(p);
if ( m_a.Add(p) == CFlexArray::no_error )
{
p->AddRef();
return TRUE;
}
else
{
return FALSE;
}
}
BOOL Next(DWORD& rdwPos, IWbemClassObject*& rp)
{
if(rdwPos < m_a.Size())
{
rp = (IWbemClassObject*)m_a.GetAt(rdwPos++);
rp->AddRef();
return TRUE;
}
else return FALSE;
}
BOOL IsResettable() {return TRUE;}
void Clear( void );
// Return whether or not the position is at our max
BOOL IsDone( DWORD dwPos ) { return !( dwPos < m_a.Size() ); }
};
CForwardAccessor::~CForwardAccessor()
{
// Tell the accessor to clear itself
Clear();
}
void CForwardAccessor::Clear( void )
{
IWbemClassObject* p;
while((p = (IWbemClassObject*)m_q.Dequeue()) != NULL)
{
ReportRemove(p);
p->Release();
}
}
CBiAccessor::~CBiAccessor()
{
// Tell the accessor to clear itself
Clear();
}
void CBiAccessor::Clear()
{
for(int i = 0; i < m_a.Size(); i++)
{
IWbemClassObject* p = (IWbemClassObject*)m_a[i];
ReportRemove(p);
p->Release();
}
m_a.Empty();
}
CEnumeratorCommon::CEnumeratorCommon( long lFlags, IWbemServices* pNamespace, LPCWSTR pwszQueryType,
LPCWSTR pwszQuery, long lEnumType, IWbemContext* pContext )
: m_pErrorInfo(NULL), m_dwNumDesired(0xFFFFFFFF), m_lFlags(lFlags),
m_pErrorObj(NULL), m_hres(WBEM_S_TIMEDOUT),
m_bComplete(FALSE), m_pNamespace(pNamespace), m_pForwarder(NULL),
m_lRef(0),m_fGotFirstObject( FALSE ), m_csNext(), m_lEntryCount( 0 ),
m_wstrQueryType( pwszQueryType ), m_wstrQuery( pwszQuery ),
m_lEnumType( lEnumType ), m_pContext( pContext ), m_fCheckMinMaxControl( g_dwMaxEnumCacheSize != 0 )
{
InitializeCriticalSection(&m_cs);
// One event indicating that we are ready
m_hReady = CreateEvent(NULL, TRUE, FALSE, NULL);
if ( NULL == m_hReady )
{
throw CX_MemoryException();
}
pNamespace->AddRef();
m_pSink = new CEnumSink(this);
if ( NULL == m_pSink )
{
throw CX_MemoryException();
}
// Allocate the approprioate accessor
if ( !( m_lFlags & WBEM_FLAG_FORWARD_ONLY ) )
{
m_pData = new CBiAccessor;
}
else
{
m_pData = new CForwardAccessor;
}
if ( NULL == m_pData )
{
throw CX_MemoryException();
}
if ( NULL != m_pContext )
{
m_pContext->AddRef();
}
}
CEnumeratorCommon::~CEnumeratorCommon()
{
m_lRef = 100;
// This will detach the sink from us
Cancel(WBEM_E_CALL_CANCELLED, TRUE);
EnterCriticalSection(&m_cs);
// Cleanup the event handles
if ( NULL != m_hReady )
{
CloseHandle(m_hReady);
m_hReady = NULL;
}
// Cleanup
if(m_pErrorInfo)
m_pErrorInfo->Release();
if(m_pErrorObj)
m_pErrorObj->Release();
if(m_pNamespace)
m_pNamespace->Release();
if(m_pForwarder)
m_pForwarder->Release();
if ( NULL != m_pContext )
{
m_pContext->Release();
m_pContext = NULL;
}
delete m_pData;
LeaveCriticalSection(&m_cs);
DeleteCriticalSection(&m_cs);
}
void CEnumeratorCommon::Cancel( HRESULT hres, BOOL fSetStatus )
{
CEnumSink* pEnumSink = NULL;
// This can happen on multiple threads, so let's clear
// m_pSink with a critical section. After that point we can
// actually schedule the cancel and detach the sink.
EnterCriticalSection( &m_cs );
// AddRef it now. We will release it
// outside the critical section when we
// are done with it.
pEnumSink = m_pSink;
// Check that the sink is not NULL before we AddRef/Release it
if ( NULL != m_pSink )
{
pEnumSink->AddRef();
m_pSink->Release();
m_pSink = NULL;
}
// Next, clear out our object
LeaveCriticalSection( &m_cs );
// Do this outside of a critical section.
// Cancel the asynchrnous call
// ===========================
if ( NULL != pEnumSink )
{
if(!m_bComplete)
{
CAsyncReq_CancelAsyncCall *pReq = new
CAsyncReq_CancelAsyncCall(pEnumSink, NULL);
if ( NULL != pReq )
{
ConfigMgr::EnqueueRequest(pReq);
}
}
// This ensures that no further objects will be indicated through this sink
// By detaching outside of m_cs, we guarantee that we will not deadlock, since
// detach enters another critical section inside of which it again reenters
// m_cs (whew!)
pEnumSink->Detach();
// Gone daddy gone
pEnumSink->Release();
}
// Finally, since the call has been cancelled, let's clean up
// any latent objects (of course, this has to be done in a
// critical section)
EnterCriticalSection( &m_cs );
// We must clear both the data and the object cache
m_pData->Clear();
LeaveCriticalSection( &m_cs );
if ( fSetStatus )
{
SetStatus(WBEM_STATUS_COMPLETE, hres, NULL);
}
}
void CEnumeratorCommon::AddRef()
{
InterlockedIncrement(&m_lRef);
}
void CEnumeratorCommon::Release()
{
if(InterlockedDecrement(&m_lRef) == 0)
delete this;
}
HRESULT CEnumeratorCommon::NextAsync(DWORD& rdwPos, ULONG uCount,
IWbemObjectSink* pSink)
{
if(pSink == NULL)
return WBEM_E_INVALID_PARAMETER;
// This function MUST block around the actual Next Critical Section.
// Therefore if any nexts are occurring, we should at least have the
// good taste to give them a chance to finish what they are doing.
// On Next operations, we MUST enter this critical section BEFORE m_cs, because
// we want to block other Next operations, but not operations that will cause
// a Next operation to complete. For example, the Indicate operation MUST be
// allowed to place objects into our arrays, or pass them down to the forwarding
// sink, or we will definitely deadlock.
// Below, you will see that if we get all of the requested data out, we will
// leave the critical section. If not, then the operation actually hasn't
// completed and we will want the m_csNext critical section to remain blocked.
// When the operation completes (either when an object completing the requested
// block is indicated, or the SetStatus() function is called), the critical
// section will actually be released.
m_csNext.Enter();
EnterCriticalSection(&m_cs);
// Controls how many objects we will let pile up in the enumerator
m_dwNumDesired = uCount;
ULONG uReturned = 0;
while (uReturned < uCount)
{
IWbemClassObject *pSrc;
if(m_pData->Next(rdwPos, pSrc))
{
/*
pSrc->Clone(pUsrArray + nUsrIx++);
*/
pSink->Indicate(1, &pSrc);
pSrc->Release();
uReturned++;
m_dwNumDesired--;
}
else break;
}
if(uReturned < uCount && !m_bComplete)
{
CBasicObjectSink* pWrapper = NULL;
CCountedSink* pCountedSink = NULL;
try
{
pWrapper = new CWrapperSink(pSink);
if ( NULL == pWrapper )
{
throw CX_MemoryException();
}
pWrapper->AddRef();
pCountedSink =
new CCountedSink(pWrapper, uCount-uReturned);
if ( NULL == pCountedSink )
{
throw CX_MemoryException();
}
pWrapper->Release();
m_pForwarder = pCountedSink;
m_pdwForwarderPos = &rdwPos;
pCountedSink->AddRef();
LeaveCriticalSection(&m_cs);
// We are intentionally staying in m_csNext. Another thread will be responsible for
// unlocking the critical section. This is a cross we have to bear because we support
// NextAsync
return WBEM_S_NO_ERROR;
}
catch(...)
{
// Cleanup
if ( NULL != pWrapper )
{
pWrapper->Release();
}
if ( NULL != pCountedSink )
{
pCountedSink->Release();
}
// The Next Operation is complete so we should release the critical section
m_csNext.Leave();
LeaveCriticalSection(&m_cs);
return WBEM_E_OUT_OF_MEMORY;
}
}
else
{
HRESULT hReturn = WBEM_S_NO_ERROR;
pSink->SetStatus(0, ( WBEM_S_TIMEDOUT == m_hres ? WBEM_S_NO_ERROR : m_hres ), NULL, m_pErrorObj);
// If the complete flag is set, check if the data accessor is really done. If so, then
// return WBEM_S_FALSE. In all other cases return WBEM_S_NO_ERROR.
if ( m_bComplete )
{
if ( m_pData->IsDone( rdwPos ) )
{
hReturn = WBEM_S_FALSE;
}
}
// The Next Operation is complete so we should release the critical section
m_csNext.Leave();
LeaveCriticalSection(&m_cs);
return hReturn;
}
}
HRESULT CEnumeratorCommon::Skip(DWORD& rdwPos, long lTimeout, ULONG nNum)
{
// We just turn this around into a Next command, except we tell Next to
// skip the objects (on each next it just releases them)
return Next(rdwPos, lTimeout, nNum, NULL, NULL);
}
HRESULT CEnumeratorCommon::Next(
DWORD& rdwPos,
long lTimeout,
ULONG uCount,
IWbemClassObject **pUsrArray,
ULONG* puReturned
)
{
// This function MUST block around the actual Next Critical Section.
// Therefore if any nexts are occurring, we should at least have the
// good taste to give them a chance to finish what they are doing.
// On Next operations, we MUST enter this critical section BEFORE m_cs, because
// we want to block other Next operations, but not operations that will cause
// a Next operation to complete. For example, the Indicate operation MUST be
// allowed to place objects into our arrays, or pass them down to the forwarding
// sink, or we will definitely deadlock.
m_csNext.Enter();
EnterCriticalSection(&m_cs);
// Controls how many objects we will let pile up in the enumerator
m_dwNumDesired = uCount;
ULONG uReturned = 0;
HRESULT hres = S_OK;
DWORD dwStart = GetTickCount();
// Start grabbing elements and cloning them to the user's array.
// =============================================================
int nUsrIx = 0;
while (uReturned < uCount)
{
IWbemClassObject *pSrc;
if(m_pData->Next(rdwPos, pSrc))
{
// Skip the object or store it in
// the user's array
if ( NULL == pUsrArray )
{
pSrc->Release();
}
else
{
pUsrArray[nUsrIx++] = pSrc;
}
// Increment this so we know how many objects
// we've processed (skipping or not).
uReturned++;
}
else
{
// Check if we've reached the end
// ==============================
if(m_bComplete)
{
// The end
// =======
SetErrorInfo(0, m_pErrorInfo);
hres = m_hres;
if (hres == WBEM_S_NO_ERROR)
{
hres = WBEM_S_FALSE;
}
else if ( FAILED( hres ) )
{
// If the array is not NULL we need to make sure we release any
// objects we set and clear the array.
if ( NULL != pUsrArray )
{
for ( DWORD dwCtr = 0; dwCtr < uReturned; dwCtr++ )
{
if ( NULL != pUsrArray[dwCtr] )
{
pUsrArray[dwCtr]->Release();
pUsrArray[dwCtr] = NULL;
}
}
}
// The number returned is actually 0
uReturned = 0;
}
break;
}
// Check if we've still got time
// =============================
DWORD dwTimeSpent = GetTickCount() - dwStart;
if((DWORD)lTimeout < dwTimeSpent)
{
// Out of time
// ===========
hres = WBEM_S_TIMEDOUT;
break;
}
// Calculate how many more we need
// ===============================
m_dwNumDesired = uCount - uReturned;
ResetEvent(m_hReady);
// Wait for them to arrive
// =======================
// By doing this, we will force other Next operations to block (we still own
// that critical section), but we allow objects to be indicated in, and SetStatus
// to be called so m_hReady can actually become signalled. Once it is signalled,
// we DO want to pull objects out, so we will turn right around and grab hold
// of m_cs.
// Boy, that was obvious, huh?
LeaveCriticalSection(&m_cs);
DWORD dwRes = CCoreQueue::QueueWaitForSingleObject(m_hReady,
(DWORD)lTimeout - dwTimeSpent);
EnterCriticalSection(&m_cs);
}
}
// We ALWAYS release m_csNext on the way out of this function.
m_csNext.Leave();
LeaveCriticalSection(&m_cs);
// Set info for the caller.
// ========================
if (puReturned)
*puReturned = uReturned;
return hres;
}
HRESULT CEnumeratorCommon::Indicate(long lNumObjects,
IWbemClassObject** apObjects,
long* plNumUsed )
{
HRESULT hres = S_OK;
// Check whether or not we're OOM. If we are, dump the whole connection
if ( !m_fCheckMinMaxControl )
{
if ( !CWin32DefaultArena::ValidateMemSize() )
{
// we are out of memory
// ====================
Cancel(WBEM_E_OUT_OF_MEMORY, TRUE);
return WBEM_E_OUT_OF_MEMORY;
}
}
// We MUST be in this critical section to change the data.
// We do NOT want m_csNext, since that will be owned by another
// thread (although see below where we release it)...
EnterCriticalSection(&m_cs);
// We haven't used any objects yet.
*plNumUsed = lNumObjects;
// If this is the first object, then set the ready event (sort of like doing a semi-synch under everyone's
// noses).
// if ( lNumObjects > 0 && !m_fGotFirstObject )
// {
// m_fGotFirstObject = TRUE;
// m_hres = WBEM_S_NO_ERROR; // We're ready to go
// SetEvent( m_hReady );
// }
DWORD dwThisSize = 0;
// This time actually add the objects to the appropriate arrays
for(long l = 0; SUCCEEDED(hres) && l < *plNumUsed; l++)
{
if ( !m_pData->Add(apObjects[l]) )
{
hres = WBEM_E_OUT_OF_MEMORY;
}
else
{
dwThisSize += ((CWbemObject*)apObjects[l])->GetBlockLength();
}
}
// Check for failures, if so, leave m_cs and dump the enumerator
if ( FAILED(hres) )
{
LeaveCriticalSection( &m_cs );
Cancel( hres, TRUE );
return hres;
}
// Inform memory control of this addition
// ======================================
DWORD dwTotalSize = m_pData->GetTotalSize();
DWORD dwSleep = 0;
if ( m_fCheckMinMaxControl )
{
hres = ConfigMgr::AddToCache(dwThisSize, dwTotalSize, &dwSleep);
}
// If we have a forwarder, pass-through any objects we can get away with
// We get this from calling NextAsync().
if ( NULL != m_pForwarder )
{
bool fCheckMemory = m_fCheckMinMaxControl;
for ( l = 0; NULL != m_pForwarder && l < *plNumUsed; l++ )
{
IWbemClassObject* p;
// We know that we just added the objects to the accessor
// so no need to check return codes here.
m_pData->Next(*m_pdwForwarderPos, p);
if(m_pForwarder->Indicate(1, &p) == WBEM_S_FALSE)
{
m_pForwarder->Release();
m_pForwarder = NULL;
// Since we completed the request here, no sense in checking for
// OOM conditions.
fCheckMemory = false;
// By releasing m_csNext here, we are effectively telling the code
// that a NextAsync call has completed. Remember, NextAsync() will
// Enter m_csNext, but if not all objects are available, leave the
// critical section hanging.
m_csNext.Leave();
}
p->Release();
} // Pass through all of the available objects
// At this point, if we had an error from before, try going into the cache
// again, since the calls to ::Next may have released out some of the objects
if ( fCheckMemory && hres != S_OK )
{
dwTotalSize = m_pData->GetTotalSize();
dwSleep = 0;
// See how the memory's doing now. By specifying 0, we won't cause
// the size to grow.
hres = ConfigMgr::AddToCache( 0, dwTotalSize, &dwSleep );
}
} // IF we have a forwarder
// Don't update anything if we're going to dump the enumerator
// Check if we have satisfied the reader's requirement
if(lNumObjects >= m_dwNumDesired)
{
SetEvent(m_hReady);
m_dwNumDesired = 0;
}
else
{
m_dwNumDesired -= lNumObjects;
}
LeaveCriticalSection(&m_cs);
// Finally, if we were checking the min/max control and we got a failure, we need to
// cancel the transaction
if ( m_fCheckMinMaxControl )
{
// No sense in sleeping
// ====================
if((m_lFlags & WBEM_FLAG_RETURN_IMMEDIATELY) == 0)
{
if(hres != S_OK)
{
// we are out of memory
// ====================
Cancel(WBEM_E_OUT_OF_MEMORY, TRUE);
return WBEM_E_OUT_OF_MEMORY;
}
}
else
{
if(dwSleep)
Sleep(dwSleep);
}
}
return hres;
}
HRESULT CEnumeratorCommon::SetStatus(long lFlags, HRESULT hresParam,
IWbemClassObject* pStatusObj)
{
if(lFlags != WBEM_STATUS_COMPLETE)
return WBEM_S_NO_ERROR;
EnterCriticalSection(&m_cs);
if(pStatusObj)
{
pStatusObj->QueryInterface(IID_IErrorInfo,
(void**)&m_pErrorInfo);
}
m_hres = hresParam;
m_bComplete = TRUE;
if(m_pForwarder)
{
m_pForwarder->SetStatus(lFlags, hresParam, NULL, pStatusObj);
m_pForwarder->Release();
m_pForwarder = NULL;
// By releasing m_csNext here, we are effectively telling the code
// that a NextAsync call has completed. Remember, NextAsync() will
// Enter m_csNext, but if not all objects are available, leave the
// critical section hanging.
m_csNext.Leave();
}
// Signal the event last
SetEvent(m_hReady);
LeaveCriticalSection(&m_cs);
return WBEM_S_NO_ERROR;
}
HRESULT CEnumeratorCommon::GetCallResult(long lTimeout, HRESULT* phres)
{
if(lTimeout < 0 && lTimeout != -1)
return WBEM_E_INVALID_PARAMETER;
// ALWAYS enter m_csNext first, since we will release m_cs before we release
// m_csNext. This will block anyone else from getting access to m_csNext until
// m_hReady is signalled
m_csNext.Enter();
EnterCriticalSection(&m_cs);
if(m_bComplete)
{
*phres = m_hres;
LeaveCriticalSection(&m_cs);
// If we're in here, we completed BEFORE this function got hold of
// m_cs (cool, huh?)
m_csNext.Leave();
return WBEM_S_NO_ERROR;
}
// Since we didn't complete, we should wait on m_hReady to
// become signalled, which will happen when the first object
// arrives.
LeaveCriticalSection(&m_cs);
DWORD dwRes = CCoreQueue::QueueWaitForSingleObject(m_hReady, lTimeout);
// Well, at least we can ascertain that something happened. Now we can
// release our lock on m_csNext.
m_csNext.Leave();
if(dwRes != WAIT_OBJECT_0)
{
return WBEM_S_TIMEDOUT;
}
*phres = m_hres;
SetErrorInfo(0, m_pErrorInfo);
return WBEM_S_NO_ERROR;
}
HRESULT CEnumeratorCommon::Reset( DWORD& rdwPos )
{
// If they said forwards-only, well that's just tough...
if(!IsResettable())
{
return WBEM_E_INVALID_OPERATION;
}
// Something's is really messed up!
if ( m_lEnumType <= enumtypeFirst || m_lEnumType >= enumTypeLast )
{
return WBEM_E_FAILED;
}
HRESULT hr = WBEM_S_NO_ERROR;
// We need access to m_csNext before we can properly reset the enumeration. This
// way we won't stomp all over any executing Next or Skip operations. We also want to
// ensure that no Next operations step in while we are resubmitting the request
CEnterWbemCriticalSection entercsNext( &m_csNext );
// This shouldn't fail, since we're basically infinite
if ( entercsNext.IsEntered() )
{
// First, check if we've actually got the full set of objects in the cache. If we do,
// empty the data accessor and reload it with the objects in the cache.
EnterCriticalSection( &m_cs );
// Reset the enumerator position reference
rdwPos = 0;
LeaveCriticalSection( &m_cs );
} // IF we got into m_csNext
else
{
hr = WBEM_S_TIMEDOUT;
}
return hr;
}
CEnumeratorCommon::CEnumSink::CEnumSink(CEnumeratorCommon* pEnum)
: CObjectSink(1), m_pEnum(pEnum)
{
}
CEnumeratorCommon::CEnumSink::~CEnumSink()
{
}
void CEnumeratorCommon::CEnumSink::Detach()
{
CInCritSec ics(&m_cs);
m_pEnum = NULL;
}
HRESULT CEnumeratorCommon::CEnumSink::
Indicate(long lNumObjects, IWbemClassObject** apObjects)
{
CInCritSec ics(&m_cs);
long lNumUsed = 0;
if ( NULL == m_pEnum )
{
return WBEM_S_NO_ERROR;
}
else
{
return m_pEnum->Indicate( lNumObjects, apObjects, &lNumUsed );
}
}
STDMETHODIMP CEnumeratorCommon::CEnumSink::
SetStatus(long lFlags, HRESULT hresParam, BSTR, IWbemClassObject* pStatusObj)
{
CInCritSec ics(&m_cs);
if(m_pEnum == NULL) return WBEM_S_NO_ERROR;
HRESULT hres = m_pEnum->SetStatus(lFlags, hresParam, pStatusObj);
return hres;
}
CAsyncEnumerator::CAsyncEnumerator(long lFlags, IWbemServices* pNamespace, LPCWSTR pwszQueryType,
LPCWSTR pwszQuery, long lEnumType, IWbemContext* pContext )
: m_XSmartEnum( this )
{
// Explicit cast to get to IUnknown, since compiler can't get us there.
gClientCounter.AddClientPtr( (IEnumWbemClassObject*) this, ENUMERATOR);
m_pCommon = new CEnumeratorCommon( lFlags, pNamespace, pwszQueryType, pwszQuery,
lEnumType, pContext );
m_pCommon->AddRef();
m_dwPos = 0;
m_pGuidToClassMap = NULL;
InitializeCriticalSection( &m_cs );
}
CAsyncEnumerator::CAsyncEnumerator( const CAsyncEnumerator& async )
: m_XSmartEnum( this )
{
// Explicit cast to get to IUnknown, since compiler can't get us there.
gClientCounter.AddClientPtr( (IEnumWbemClassObject*) this, ENUMERATOR);
m_pCommon = async.m_pCommon;
m_pCommon->AddRef();
// Preserve the last position
m_dwPos = async.m_dwPos;
m_pGuidToClassMap = NULL;
InitializeCriticalSection( &m_cs );
}
CAsyncEnumerator::~CAsyncEnumerator()
{
// Explicit cast to get to IUnknown, since compiler can't get us there.
gClientCounter.RemoveClientPtr((IEnumWbemClassObject*) this);
m_pCommon->Release();
// Cleanup the map
if ( NULL != m_pGuidToClassMap )
{
delete m_pGuidToClassMap;
}
DeleteCriticalSection( &m_cs );
}
STDMETHODIMP CAsyncEnumerator::Reset()
{
if(!m_Security.AccessCheck())
return WBEM_E_ACCESS_DENIED;
// Handoff to the common enumerator
return m_pCommon->Reset( m_dwPos );
}
STDMETHODIMP CAsyncEnumerator::Next(long lTimeout, ULONG uCount,
IWbemClassObject** apObj, ULONG* puReturned)
{
*puReturned = 0;
if(!m_Security.AccessCheck())
return WBEM_E_ACCESS_DENIED;
if(lTimeout < 0 && lTimeout != -1)
return WBEM_E_INVALID_PARAMETER;
return m_pCommon->Next(m_dwPos, lTimeout, uCount, apObj, puReturned);
}
STDMETHODIMP CAsyncEnumerator::NextAsync(ULONG uCount, IWbemObjectSink* pSink)
{
if(!m_Security.AccessCheck())
return WBEM_E_ACCESS_DENIED;
return m_pCommon->NextAsync(m_dwPos, uCount, pSink);
}
STDMETHODIMP CAsyncEnumerator::Clone(IEnumWbemClassObject** ppEnum)
{
*ppEnum = NULL;
if(!m_Security.AccessCheck())
return WBEM_E_ACCESS_DENIED;
if(m_pCommon->IsResettable())
{
CAsyncEnumerator* pEnum = new CAsyncEnumerator( *this );
if ( NULL != pEnum )
{
// We're good to go
*ppEnum = pEnum;
return WBEM_S_NO_ERROR;
}
return WBEM_E_OUT_OF_MEMORY;
}
else
{
*ppEnum = NULL;
return WBEM_E_INVALID_OPERATION;
}
}
STDMETHODIMP CAsyncEnumerator::Skip(long lTimeout, ULONG nNum)
{
if(!m_Security.AccessCheck())
return WBEM_E_ACCESS_DENIED;
if(lTimeout < 0 && lTimeout != -1)
return WBEM_E_INVALID_PARAMETER;
return m_pCommon->Skip(m_dwPos, lTimeout, nNum);
}
HRESULT CAsyncEnumerator::GetCallResult(long lTimeout, HRESULT* phres)
{
if(lTimeout < 0 && lTimeout != -1)
return WBEM_E_INVALID_PARAMETER;
return m_pCommon->GetCallResult(lTimeout, phres);
}
// IWbemFetchSmartEnum Methods
STDMETHODIMP CAsyncEnumerator::GetSmartEnum( IWbemWCOSmartEnum** ppSmartEnum )
{
*ppSmartEnum = NULL;
if(!m_Security.AccessCheck())
return WBEM_E_ACCESS_DENIED;
return m_XSmartEnum.QueryInterface( IID_IWbemWCOSmartEnum, (void**) ppSmartEnum );
};
SCODE CAsyncEnumerator::XSmartEnum::QueryInterface(REFIID riid, LPVOID FAR* ppvObj)
{
*ppvObj = 0;
if ( IID_IUnknown==riid || IID_IWbemWCOSmartEnum == riid)
{
*ppvObj = (IWbemWCOSmartEnum*)this;
AddRef();
return NOERROR;
}
else
{
return m_pOuter->QueryInterface( riid, ppvObj );
}
}
ULONG CAsyncEnumerator::XSmartEnum::AddRef( void )
{
return m_pOuter->AddRef();
}
ULONG CAsyncEnumerator::XSmartEnum::Release( void )
{
return m_pOuter->Release();
}
// IWbemWCOSmartEnum Methods!
STDMETHODIMP CAsyncEnumerator::XSmartEnum::
Next( REFGUID proxyGUID, long lTimeout, ULONG uCount,
ULONG* puReturned, ULONG* pdwBuffSize, BYTE** pBuffer)
{
*puReturned = 0;
*pdwBuffSize = 0;
*pBuffer = NULL;
if(!m_pOuter->m_Security.AccessCheck())
return WBEM_E_ACCESS_DENIED;
// Use a critical section to guard this operation.
CInCritSec(&m_pOuter->m_cs);
// Todo: The proxyGUID needs to be mapped to a per PROXY WbemClassToIdMap
// object so we ensure that data is lobotomized properly.
// Allocate a map if we need one
// Allocate a map if we need one
if ( NULL == m_pOuter->m_pGuidToClassMap )
{
m_pOuter->m_pGuidToClassMap = new CWbemGuidToClassMap;
}
// Look for the GUID in the cache. If we don't find it, it's new so add it
CWbemClassToIdMap* pClassToId = NULL;
CGUID guid( proxyGUID );
HRESULT hr = m_pOuter->m_pGuidToClassMap->GetMap( guid, &pClassToId );
if ( FAILED( hr ) )
{
hr = m_pOuter->m_pGuidToClassMap->AddMap( guid, &pClassToId );
}
// Only continue if we have a cache to work with
if ( SUCCEEDED( hr ) )
{
// Allocate a transient array to copy objects into
IWbemClassObject** apObj = new IWbemClassObject*[uCount];
if ( NULL != apObj )
{
// Clear the object array
ZeroMemory( apObj, uCount * sizeof(IWbemClassObject*) );
// Pass through to the regular next function
hr = m_pOuter->Next( lTimeout, uCount, apObj, puReturned );
if ( SUCCEEDED( hr ) )
{
// Only marshal data if we need to
if ( *puReturned > 0 )
{
// hr contains the actual proper return code, so let's not overwrite
// that valu unless something goes wrong during marshaling.
HRESULT hrMarshal = WBEM_S_NO_ERROR;
// Caluclate data length first
DWORD dwLength;
GUID* pguidClassIds = new GUID[*puReturned];
BOOL* pfSendFullObject = new BOOL[*puReturned];
CWbemSmartEnumNextPacket packet;
if(pguidClassIds && pfSendFullObject)
{
hrMarshal = packet.CalculateLength(*puReturned, apObj, &dwLength,
*pClassToId, pguidClassIds, pfSendFullObject );
}
else
{
hrMarshal = WBEM_E_OUT_OF_MEMORY;
}
if ( SUCCEEDED( hrMarshal ) )
{
// As we could be going cross process/machine, use the
// COM memory allocator
LPBYTE pbData = (LPBYTE) CoTaskMemAlloc( dwLength );
if ( NULL != pbData )
{
// hr contains the actual proper return code, so let's not overwrite
// that valu unless something goes wrong during marshaling.
// Write the objects out to the buffer
HRESULT hrMarshal = packet.MarshalPacket( pbData, dwLength, *puReturned, apObj,
pguidClassIds, pfSendFullObject);
// Copy the values, we're golden.
if ( SUCCEEDED( hr ) )
{
*pdwBuffSize = dwLength;
*pBuffer = pbData;
}
else
{
// Store the new error code
hr = hrMarshal;
// Clean up the memory --- something went wrong
CoTaskMemFree( pbData );
}
}
else
{
hr = WBEM_E_OUT_OF_MEMORY;
}
} // IF CalculateLength()
else
{
// Set the error code
hr = hrMarshal;
}
// Clean up the guid and flag arrays
if ( NULL != pguidClassIds )
{
delete [] pguidClassIds;
}
if ( NULL != pfSendFullObject )
{
delete [] pfSendFullObject;
}
} // IF *puReturned > 0
else
{
// NULL these out
*pdwBuffSize = 0;
*pBuffer = NULL;
}
} // IF SUCCEEDED IEnumWbemClassObject::Next
// We need to Release all the objects in the array, since we have
// inherited ownership from the old Next.
// ==============================================================
for(int i = 0; i < *puReturned; i++)
apObj[i]->Release();
delete [] apObj;
} // IF NULL != apObj
else
{
hr = WBEM_E_OUT_OF_MEMORY;
}
} // IF Cache obtained
return hr;
}