/*++ 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 // 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 #include #include //*************************************************************************** // // 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; }