/*++ Copyright (C) 1996-2001 Microsoft Corporation Module Name: REFRCACH.CPP Abstract: Refresher Server Side Implementation History: --*/ #include "precomp.h" #include //***************************************************************************** //***************************************************************************** // OBJECT REQUEST RECORD //***************************************************************************** //***************************************************************************** CRefresherCache::CObjectRequestRecord::CObjectRequestRecord( long lExternalRequestId, CWbemObject* pRefreshedObject, long lProviderRequestId) : CRequestRecord(lExternalRequestId), m_pRefreshedObject(pRefreshedObject), m_lInternalRequestId(lProviderRequestId) { if(m_pRefreshedObject) m_pRefreshedObject->AddRef(); } HRESULT CRefresherCache::CObjectRequestRecord::Cancel( CProviderRecord* pContainer) { return pContainer->Cancel(m_lInternalRequestId); } CRefresherCache::CObjectRequestRecord::~CObjectRequestRecord() { if(m_pRefreshedObject) m_pRefreshedObject->Release(); } //***************************************************************************** //***************************************************************************** // ENUM REQUEST RECORD //***************************************************************************** //***************************************************************************** CRefresherCache::CEnumRequestRecord::CEnumRequestRecord( long lExternalRequestId, CRemoteHiPerfEnum* pHPEnum, long lProviderRequestId) : CRequestRecord(lExternalRequestId), m_pHPEnum(pHPEnum), m_lInternalRequestId(lProviderRequestId) { if(m_pHPEnum) m_pHPEnum->AddRef(); } HRESULT CRefresherCache::CEnumRequestRecord::Cancel( CProviderRecord* pContainer) { return pContainer->Cancel(m_lInternalRequestId); } CRefresherCache::CEnumRequestRecord::~CEnumRequestRecord() { if(m_pHPEnum) m_pHPEnum->Release(); } //***************************************************************************** //***************************************************************************** // PROVIDER RECORD //***************************************************************************** //***************************************************************************** CRefresherCache::CProviderRecord::CProviderRecord( CCommonProviderCacheRecord* pProviderCacheRecord, IWbemRefresher* pRefresher, CWbemNamespace* pNamespace) : m_pProviderCacheRecord(pProviderCacheRecord), m_pProvider(NULL), m_pInternalRefresher(pRefresher), m_pNamespace(pNamespace), m_cs() { if(m_pProviderCacheRecord) { m_pProviderCacheRecord->AddRef(); m_pProvider = pProviderCacheRecord->GetHiPerf(); } if(m_pProvider) m_pProvider->AddRef(); if(m_pInternalRefresher) m_pInternalRefresher->AddRef(); if(m_pNamespace) m_pNamespace->AddRef(); } CRefresherCache::CProviderRecord::~CProviderRecord() { if(m_pProviderCacheRecord) m_pProviderCacheRecord->Release(); if(m_pProvider) m_pProvider->Release(); if(m_pInternalRefresher) m_pInternalRefresher->Release(); if(m_pNamespace) m_pNamespace->Release(); } HRESULT CRefresherCache::CProviderRecord::AddObjectRequest( CWbemObject* pRefreshedObject, long lProviderRequestId, long lNewId) { // Locks and Unlocks going into and coming out of scope CInCritSec ics( &m_cs ); CObjectRequestRecord* pRequest = NULL; // Watch for OOM exceptions try { pRequest = new CObjectRequestRecord(lNewId, pRefreshedObject, lProviderRequestId); m_apRequests.Add(pRequest); return WBEM_S_NO_ERROR; } catch( CX_MemoryException ) { if ( NULL != pRequest ) { delete pRequest; } return WBEM_E_OUT_OF_MEMORY; } catch(...) { if ( NULL != pRequest ) { delete pRequest; } return WBEM_E_FAILED; } } HRESULT CRefresherCache::CProviderRecord::AddEnumRequest( CRemoteHiPerfEnum* pHPEnum, long lProviderRequestId, long lNewId ) { // Locks and Unlocks going into and coming out of scope CInCritSec ics( &m_cs ); CEnumRequestRecord* pRequest = NULL; // Watch for OOM exceptions try { pRequest = new CEnumRequestRecord(lNewId, pHPEnum, lProviderRequestId); m_apEnumRequests.Add(pRequest); return WBEM_S_NO_ERROR; } catch( CX_MemoryException ) { if ( NULL != pRequest ) { delete pRequest; } return WBEM_E_OUT_OF_MEMORY; } catch(...) { if ( NULL != pRequest ) { delete pRequest; } return WBEM_E_FAILED; } } HRESULT CRefresherCache::CProviderRecord::Remove(long lId, BOOL* pfIsEnum ) { HRESULT hres = WBEM_S_NO_ERROR; // Locks and Unlocks going into and coming out of scope CInCritSec ics( &m_cs ); // Need to know if we axed an enumerator or an actual object *pfIsEnum = FALSE; // Check object requests, then enum requests for(int i = 0; i < m_apRequests.GetSize(); i++) { CObjectRequestRecord* pRequest = m_apRequests[i]; if(pRequest->GetExternalRequestId() == lId) { hres = pRequest->Cancel(this); m_apRequests.RemoveAt(i); return hres; } } for(i = 0; i < m_apEnumRequests.GetSize(); i++) { CEnumRequestRecord* pRequest = m_apEnumRequests[i]; if(pRequest->GetExternalRequestId() == lId) { hres = pRequest->Cancel(this); m_apEnumRequests.RemoveAt(i); *pfIsEnum = TRUE; return hres; } } return WBEM_S_FALSE; } HRESULT CRefresherCache::CProviderRecord::Find( long lId ) { // Locks and Unlocks going into and coming out of scope CInCritSec ics( &m_cs ); // Check object requests, then enum requests for(int i = 0; i < m_apRequests.GetSize(); i++) { CObjectRequestRecord* pRequest = m_apRequests[i]; if(pRequest->GetExternalRequestId() == lId) { return WBEM_S_NO_ERROR; } } for(i = 0; i < m_apEnumRequests.GetSize(); i++) { CEnumRequestRecord* pRequest = m_apEnumRequests[i]; if(pRequest->GetExternalRequestId() == lId) { return WBEM_S_NO_ERROR; } } return WBEM_S_FALSE; } HRESULT CRefresherCache::CProviderRecord::Cancel(long lId) { // Locks and Unlocks going into and coming out of scope CInCritSec ics( &m_cs ); if(m_pProvider) { // Watch for any exceptions that may get thrown try { return m_pProvider->StopRefreshing(m_pInternalRefresher, lId, 0); } catch(...) { return WBEM_E_PROVIDER_FAILURE; } } return WBEM_S_FALSE; } HRESULT CRefresherCache::CProviderRecord::Refresh(long lFlags) { // Locks and Unlocks going into and coming out of scope CInCritSec ics( &m_cs ); if(m_pInternalRefresher) { try { return m_pInternalRefresher->Refresh(0L); } catch(...) { // The provider threw an exception. Just return and let scoping // release anything we may be holding onto. return WBEM_E_PROVIDER_FAILURE; } } else return WBEM_S_NO_ERROR; } HRESULT CRefresherCache::CProviderRecord::Store( WBEM_REFRESHED_OBJECT* aObjects, long* plIndex) { HRESULT hres = WBEM_S_NO_ERROR; // Locks and Unlocks going into and coming out of scope CInCritSec ics( &m_cs ); // Error out if anything beefs // First handle the single objects, then we'll get the // enumerations for(int i = 0; SUCCEEDED( hres ) && i < m_apRequests.GetSize(); i++) { CObjectRequestRecord* pRequest = m_apRequests[i]; CWbemInstance* pInst = (CWbemInstance*)pRequest->GetRefreshedObject(); WBEM_REFRESHED_OBJECT* pRefreshed = aObjects + *plIndex; hres = pInst->GetTransferBlob(&(pRefreshed->m_lBlobType), &(pRefreshed->m_lBlobLength), &(pRefreshed->m_pbBlob)); if ( SUCCEEDED( hres ) ) { pRefreshed->m_lRequestId = pRequest->GetExternalRequestId(); (*plIndex)++; } else { // Clear all data in case of failure ZeroMemory( pRefreshed, sizeof(WBEM_REFRESHED_OBJECT) ); } } // Now handle the enumerations. Each enum will create an array // of BLOBs for( i = 0; SUCCEEDED( hres ) && i < m_apEnumRequests.GetSize(); i++) { CEnumRequestRecord* pRequest = m_apEnumRequests[i]; WBEM_REFRESHED_OBJECT* pRefreshed = aObjects + *plIndex; hres = pRequest->GetEnum()->GetTransferArrayBlob( &(pRefreshed->m_lBlobType), &(pRefreshed->m_lBlobLength), &(pRefreshed->m_pbBlob) ); if ( SUCCEEDED( hres ) ) { pRefreshed->m_lRequestId = pRequest->GetExternalRequestId(); (*plIndex)++; } else { // Clear all data in case of failure ZeroMemory( pRefreshed, sizeof(WBEM_REFRESHED_OBJECT) ); } } // We need to cleanup any allocated sub-blobs now if ( FAILED( hres ) ) { for ( int x = 0; x < *plIndex; x++ ) { WBEM_REFRESHED_OBJECT* pRefreshed = aObjects + x; if ( NULL != pRefreshed->m_pbBlob ) { CoTaskMemFree( pRefreshed->m_pbBlob ); pRefreshed->m_pbBlob = NULL; } } // FOR x } // IF FAILED(hres return hres; } //***************************************************************************** //***************************************************************************** // REFRESHER RECORD //***************************************************************************** //***************************************************************************** CRefresherCache::CRefresherRecord::CRefresherRecord(const WBEM_REFRESHER_ID& Id) : m_Id(Id), m_lRefCount(0), m_lNumObjects(0), m_lNumEnums(0), m_lLastId( 0 ), m_cs() { // We need a guid to uniquely identify this bad boy for remote auto-connect CoCreateGuid( &m_Guid ); } INTERNAL HRESULT CRefresherCache::CRefresherRecord:: AddProvider(CCommonProviderCacheRecord* pProviderCacheRecord, IWbemRefresher* pRefresher, CWbemNamespace* pNamespace, CRefresherCache::CProviderRecord** ppRecord ) { // Locks and Unlocks going into and coming out of scope CInCritSec ics( &m_cs ); CProviderRecord* pProvRecord = NULL; // Watch for memory exceptions try { pProvRecord = new CProviderRecord(pProviderCacheRecord, pRefresher, pNamespace); m_apProviders.Add(pProvRecord); *ppRecord = pProvRecord; return WBEM_S_NO_ERROR; } catch( CX_MemoryException ) { if ( NULL != pProvRecord ) { delete pProvRecord; } return WBEM_E_OUT_OF_MEMORY; } catch( ... ) { if ( NULL != pProvRecord ) { delete pProvRecord; } return WBEM_E_FAILED; } } INTERNAL CRefresherCache::CProviderRecord* CRefresherCache::CRefresherRecord:: FindProviderRecord(CCommonProviderCacheRecord* pProviderCacheRecord) { // Locks and Unlocks going into and coming out of scope CInCritSec ics( &m_cs ); for(int i = 0; i < m_apProviders.GetSize(); i++) { CProviderRecord* pProvRecord = m_apProviders[i]; if(pProvRecord->GetCacheRecord() == pProviderCacheRecord) return pProvRecord; } return NULL; } HRESULT CRefresherCache::CRefresherRecord::Remove(long lId) { // Locks and Unlocks going into and coming out of scope CInCritSec ics( &m_cs ); // Find it first // ============= for(int i = 0; i < m_apProviders.GetSize(); i++) { CProviderRecord* pProvRecord = m_apProviders[i]; BOOL fIsEnum = FALSE; HRESULT hres = pProvRecord->Remove( lId, &fIsEnum ); if(hres == WBEM_S_FALSE) continue; if(FAILED(hres)) return hres; // Found it // ======== if(pProvRecord->IsEmpty()) m_apProviders.RemoveAt(i); // Decrememt the proper counter if ( fIsEnum ) { m_lNumEnums--; } else { m_lNumObjects--; } return WBEM_S_NO_ERROR; } return WBEM_S_FALSE; } ULONG STDMETHODCALLTYPE CRefresherCache::CRefresherRecord::AddRef() { int x = 1; return InterlockedIncrement(&m_lRefCount); } ULONG STDMETHODCALLTYPE CRefresherCache::CRefresherRecord::Release() { long lRef = InterlockedDecrement(&m_lRefCount); if(lRef == 0) { // The remove call will check that this guy has really been released // before axing him. All functions go through FindRefresherRecord() // to get a record, which blocks on the same critical section as // remove. Since it AddRef()s the record before it returns, we // are ensured that if a client requests the same record // twice and one operation fails, releasing its object, before the // other has returned from a Find, that the ref count will get // bumped up again, so IsReleased() will fail, and the record won't // really be removed. ConfigMgr::GetRefresherCache()->RemoveRefresherRecord(this); // deletes } return lRef; } STDMETHODIMP CRefresherCache::CRefresherRecord::QueryInterface(REFIID riid, void** ppv) { if(riid == IID_IUnknown || riid == IID_IWbemRemoteRefresher) { AddRef(); *ppv = (IWbemRemoteRefresher*)this; return S_OK; } else if(riid == IID_IWbemRefresher) { AddRef(); *ppv = (IWbemRefresher*)this; return S_OK; } else return E_NOINTERFACE; } STDMETHODIMP CRefresherCache::CRefresherRecord::Refresh(long lFlags) { // Locks and Unlocks going into and coming out of scope CInCritSec ics( &m_cs ); // Go through all our providers and refresh them // ============================================= long lObjectIndex = 0; HRESULT hres; for(int i = 0; i < m_apProviders.GetSize(); i++) { CProviderRecord* pProvRecord = m_apProviders[i]; hres = pProvRecord->Refresh(lFlags); if(FAILED(hres)) return hres; } return WBEM_S_NO_ERROR; } STDMETHODIMP CRefresherCache::CRefresherRecord::RemoteRefresh( long lFlags, long* plNumObjects, WBEM_REFRESHED_OBJECT** paObjects) { HRESULT hres = WBEM_S_NO_ERROR; // Locks and Unlocks going into and coming out of scope CInCritSec ics( &m_cs ); // Use CoTaskMemAlloc()? if(paObjects) { // Original code //*paObjects = new WBEM_REFRESHED_OBJECT[m_lNumObjects]; *paObjects = (WBEM_REFRESHED_OBJECT*) CoTaskMemAlloc( ( m_lNumObjects + m_lNumEnums ) * sizeof(WBEM_REFRESHED_OBJECT)); if ( NULL != *paObjects ) { // Zero out the BLOB ZeroMemory( *paObjects, ( m_lNumObjects + m_lNumEnums ) * sizeof(WBEM_REFRESHED_OBJECT) ); } else { return WBEM_E_OUT_OF_MEMORY; } } // This value needs to reflect the number of objects as well as the number of enumerators we are shipping // back to the client. if(plNumObjects) { *plNumObjects = m_lNumObjects + m_lNumEnums; } // Go through all our providers and refresh them // ============================================= long lObjectIndex = 0; HRESULT hrFirstRefresh = WBEM_S_NO_ERROR; BOOL fOneSuccess = FALSE; BOOL fOneRefresh = FALSE; BOOL fPartialSuccess = FALSE; for(int i = 0; i < m_apProviders.GetSize(); i++) { CProviderRecord* pProvRecord = m_apProviders[i]; hres = pProvRecord->Refresh(lFlags); if ( SUCCEEDED( hres ) ) { if(paObjects) { // Store the result // ================ hres = pProvRecord->Store(*paObjects, &lObjectIndex); // If this fails, we will consider this catastrophic, since the // only reason this would fail is under out of memory conditions, // and in that case, since we are remoting, all sorts of things // could go wrong, so if this breaks, just cleanup and bail out. if ( FAILED( hres ) ) { if ( *paObjects ) { CoTaskMemFree( *paObjects ); *paObjects = NULL; } *plNumObjects = 0; return hres; } } // IF NULL != paObjects } // IF Refresh Succeeded // Always keep the first return code. We also need to track // whether or not we had at least one success, as well as if // the partial flag should be set. if ( !fOneRefresh ) { fOneRefresh = TRUE; hrFirstRefresh = hres; } // All other codes indicate something went awry if ( WBEM_S_NO_ERROR == hres ) { fOneSuccess = TRUE; // A prior refresh may have failed, a later one didn't if ( fOneRefresh && WBEM_S_NO_ERROR != hrFirstRefresh ) { fPartialSuccess = TRUE; } } else if ( fOneSuccess ) { // We must have had at least one success for the partial success // flag to be set. fPartialSuccess = TRUE; } } // FOR enum providers // At this point, if the partial success flag is set, that will // be our return. If we didn't have at least one success, then // the return code will be the first one we got back. Otherwise, // hres should contain the proper value if ( fPartialSuccess ) { hres = WBEM_S_PARTIAL_RESULTS; } else if ( !fOneSuccess ) { hres = hrFirstRefresh; } // Finally, if the object index is less than the number of array elements we "thought" // we would be sending back, make sure we reflect this. If it is zero, just delete the // elements (we wouldn't have allocated any sub-buffers anyway). Since // *plNumObjects is a sizeof, only *plNumObjects elements will be sent back, although // CoTaskMemFree() should cleanup the entire array buffer. if ( lObjectIndex != *plNumObjects ) { *plNumObjects = lObjectIndex; if ( 0 == lObjectIndex ) { if ( *paObjects ) { CoTaskMemFree( *paObjects ); *paObjects = NULL; } } } return hres; } STDMETHODIMP CRefresherCache::CRefresherRecord::StopRefreshing( long lNumIds, long* aplIds, long lFlags) { // Locks and Unlocks going into and coming out of scope CInCritSec ics( &m_cs ); HRESULT hr = WBEM_S_NO_ERROR; HRESULT hrFirst = WBEM_S_NO_ERROR; BOOL fOneSuccess = FALSE; BOOL fOneRemove = FALSE; BOOL fPartialSuccess = FALSE; for ( long lCtr = 0; lCtr < lNumIds; lCtr++ ) { hr = Remove( aplIds[lCtr] ); if ( !fOneRemove ) { hrFirst = hr; fOneRemove = TRUE; } // Record the fact we got at least one success if we got one // All other codes indicate something went awry if ( WBEM_S_NO_ERROR == hr ) { fOneSuccess = TRUE; // A prior refresh may have failed, a later one didn't if ( fOneRemove && WBEM_S_NO_ERROR != hrFirst ) { fPartialSuccess = TRUE; } } else if ( fOneSuccess ) { // We must have had at least one success for the partial success // flag to be set. fPartialSuccess = TRUE; } } // FOR enum ids // At this point, if the partial success flag is set, that will // be our return. If we didn't have at least one success, then // the return code will be the first one we got back. Otherwise, // hres should contain the proper value if ( fPartialSuccess ) { hr = WBEM_S_PARTIAL_RESULTS; } else if ( !fOneSuccess ) { hr = hrFirst; } return hr; } STDMETHODIMP CRefresherCache::CRefresherRecord::GetGuid( long lFlags, GUID* pGuid ) { if ( 0L != lFlags || NULL == pGuid ) { return WBEM_E_INVALID_PARAMETER; } *pGuid = m_Guid; return WBEM_S_NO_ERROR; } //***************************************************************************** //***************************************************************************** // REMOTE RECORD //***************************************************************************** //***************************************************************************** CRefresherCache::CRemoteRecord::CRemoteRecord(const WBEM_REFRESHER_ID& Id) : CRefresherRecord(Id) { } CRefresherCache::CRemoteRecord::~CRemoteRecord() { } HRESULT CRefresherCache::CRemoteRecord::GetProviderRefreshInfo( CCommonProviderCacheRecord* pProviderCacheRecord, CWbemNamespace* pNamespace, CProviderRecord** ppProvRecord, IWbemRefresher** ppRefresher ) { HRESULT hres = WBEM_S_NO_ERROR; // Get a refresher from the provider, unless already available // =========================================================== *ppProvRecord = FindProviderRecord(pProviderCacheRecord); // We couldn't find the record so make sure we are able to get a refresher if ( NULL == *ppProvRecord ) { try { hres = pProviderCacheRecord->GetHiPerf()->CreateRefresher( pNamespace, 0, ppRefresher); if ( SUCCEEDED(hres) && NULL == *ppRefresher ) { hres = WBEM_E_PROVIDER_FAILURE; } } catch(...) { // The provider threw an exception. Just return and let scoping // release anything we may be holding onto. return WBEM_E_PROVIDER_FAILURE; } } else { // Get the refresher pointer and AddRef it *ppRefresher = (*ppProvRecord)->GetInternalRefresher(); if(*ppRefresher) { (*ppRefresher)->AddRef(); } } return hres; } HRESULT CRefresherCache::CRemoteRecord::AddObjectRefresher( CCommonProviderCacheRecord* pProviderCacheRecord, CWbemNamespace* pNamespace, CWbemObject* pInstTemplate, long lFlags, IWbemContext* pContext, CRefreshInfo* pInfo) { // Enters and Leaves as a byproduct of scoping CInCritSec ics(&m_cs); // Get a refresher from the provider, unless already available // =========================================================== IWbemRefresher* pProvRefresher = NULL; CProviderRecord* pProvRecord = NULL; HRESULT hres = GetProviderRefreshInfo( pProviderCacheRecord, pNamespace, &pProvRecord, &pProvRefresher ); // Always release going out of scope CReleaseMe rmRefresh( pProvRefresher ); if ( SUCCEEDED( hres ) ) { // Call provider for information // ============================= IWbemObjectAccess* pRefreshedOA = NULL; long lProvRequestId; // Now try to add the object try { hres = pProviderCacheRecord->GetHiPerf()->CreateRefreshableObject( pNamespace, pInstTemplate, pProvRefresher, 0, pContext, &pRefreshedOA, &lProvRequestId); } catch(...) { // The provider threw an exception. Just return and let scoping // release anything we may be holding onto. return WBEM_E_PROVIDER_FAILURE; } // Always release going out of scope CReleaseMe rmRefreshed( pRefreshedOA ); CWbemObject* pRefreshedObject = (CWbemObject*)pRefreshedOA; CWbemObject* pClientObject = NULL; if ( SUCCEEDED( hres ) ) { // The object we return to the client, since we are remote, should // contain amended qualifiers if we are using localization, so to make // sure of this, we will clone an object off of the pInstTemplate and // the copy the instance data from the object the provider returned // to us. The provider can refresh the object it gave to us, since // we will only be sending the instance part hres = pInstTemplate->Clone( (IWbemClassObject**) &pClientObject ); if ( SUCCEEDED( hres ) ) { hres = pClientObject->CopyBlobOf( pRefreshedObject ); if ( SUCCEEDED( hres ) ) { hres = pNamespace->DecorateObject( pClientObject ); } } // IF Clones } // IF Object Created // Release going out of scope CReleaseMe rmClient( (IWbemClassObject*) pClientObject ); if ( SUCCEEDED( hres ) ) { // Add a new provider record if necessary if(pProvRecord == NULL) { hres = AddProvider( pProviderCacheRecord, pProvRefresher, pNamespace, &pProvRecord ); } // Now we will add the actual request if ( SUCCEEDED( hres ) ) { // Generate the new id from our datamember long lNewId = GetNewRequestId(); hres = pProvRecord->AddObjectRequest(pRefreshedObject, lProvRequestId, lNewId ); if ( SUCCEEDED( hres ) ) { m_lNumObjects++; pInfo->SetRemote(this, lNewId, pClientObject, &m_Guid); } } // IF we have a provider record } // IF created a client object } // IF Got refresher return hres; } HRESULT CRefresherCache::CRemoteRecord::AddEnumRefresher( CCommonProviderCacheRecord* pProviderCacheRecord, CWbemNamespace* pNamespace, CWbemObject* pInstTemplate, LPCWSTR wszClass, long lFlags, IWbemContext* pContext, CRefreshInfo* pInfo) { // Enters and Leaves as a byproduct of scoping CInCritSec ics(&m_cs); // Get a refresher from the provider, unless already available // =========================================================== IWbemRefresher* pProvRefresher = NULL; CProviderRecord* pProvRecord = NULL; HRESULT hres = GetProviderRefreshInfo( pProviderCacheRecord, pNamespace, &pProvRecord, &pProvRefresher ); // Always release going out of scope CReleaseMe rmRefresh( pProvRefresher ); if ( SUCCEEDED( hres ) ) { // Call provider for information // ============================= // Create a HiPerf Enumerator (We know we will need one // of these since we will only be in this code when we // go remote). CRemoteHiPerfEnum* pHPEnum = NULL; // Watch for OOM exceptions try { pHPEnum = new CRemoteHiPerfEnum; } catch( CX_MemoryException ) { hres = WBEM_E_OUT_OF_MEMORY; } catch( ... ) { hres = WBEM_E_FAILED; } if ( SUCCEEDED( hres ) ) { // Bump up the RefCount pHPEnum->AddRef(); // Release this pointer when we drop out of scope CReleaseMe rm( pHPEnum ); // The enumerator will need to know this hres = pHPEnum->SetInstanceTemplate( (CWbemInstance*) pInstTemplate ); if ( FAILED(hres) ) { return hres; } long lProvRequestId = 0; try { hres = pProviderCacheRecord->GetHiPerf()->CreateRefreshableEnum( pNamespace, wszClass, pProvRefresher, 0, pContext, pHPEnum, &lProvRequestId); } catch(...) { // The provider threw an exception. Just return and let scoping // release anything we may be holding onto. return WBEM_E_PROVIDER_FAILURE; } // Add a new provider record if we need one if( SUCCEEDED( hres ) && ( pProvRecord == NULL ) ) { hres = AddProvider(pProviderCacheRecord, pProvRefresher, pNamespace, &pProvRecord); } // Now we will add the actual request if ( SUCCEEDED( hres ) ) { // Generate the new id from our datamember long lNewId = GetNewRequestId(); HRESULT hres = pProvRecord->AddEnumRequest( pHPEnum, lProvRequestId, lNewId ); if ( SUCCEEDED( hres ) ) { m_lNumEnums++; pInfo->SetRemote(this, lNewId, pInstTemplate, &m_Guid); } } // IF we have a provider record } // IF Created HPEnum } // IF Got Refresher return hres; } //***************************************************************************** //***************************************************************************** // REFRESHER CACHE //***************************************************************************** //***************************************************************************** CRefresherCache::CRefresherCache() : m_cs() { } CRefresherCache::~CRefresherCache() { } HRESULT CRefresherCache::FindRefresherRecord(CRefresherId* pRefresherId, BOOL bCreate, CRefresherCache::CRefresherRecord** ppRecord ) { HRESULT hres = WBEM_S_NO_ERROR; // Enters and exits using scoping CInCritSec ics( &m_cs ); // We always AddRef() the record before returning so multiple requests will keep the // refcount correct so we won't remove and delete a record that another thread wants to // use (Remove blocks on the same critical section). // Look for it // =========== for(int i = 0; i < m_apRefreshers.GetSize(); i++) { if(m_apRefreshers[i]->GetId() == *pRefresherId) { m_apRefreshers[i]->AddRef(); *ppRecord = m_apRefreshers[i]; return WBEM_S_NO_ERROR; } } // If we weren't told to create it, then this is not an error if(!bCreate) { *ppRecord = NULL; return WBEM_S_FALSE; } CRefresherRecord* pNewRecord = NULL; // Watch for memory exceptions try { pNewRecord = new CRemoteRecord(*pRefresherId); m_apRefreshers.Add(pNewRecord); pNewRecord->AddRef(); *ppRecord = pNewRecord; return WBEM_S_NO_ERROR; } catch( CX_MemoryException ) { if ( NULL != pNewRecord ) { delete pNewRecord; } return WBEM_E_OUT_OF_MEMORY; } catch( ... ) { if ( NULL != pNewRecord ) { delete pNewRecord; } return WBEM_E_FAILED; } } HRESULT CRefresherCache::CreateInfoForProvider(CRefresherId* pDestRefresherId, CCommonProviderCacheRecord* pProviderCacheRecord, CWbemNamespace* pNamespace, REFCLSID rClientClsid, CWbemObject* pInstTemplate, long lFlags, IWbemContext* pContext, CRefreshInfo* pInfo) { HRESULT hres; // ClientLoadable ClassId MUST be present or this operation is a NO-GO if ( rClientClsid != CLSID_NULL ) { MSHCTX dwDestContext = GetDestinationContext(pDestRefresherId); // If this is In-Proc or Local, we'll just let the normal // client loadable logic handle it if( dwDestContext == MSHCTX_LOCAL || dwDestContext == MSHCTX_INPROC ) { // By decorating the object, we will store namespace and // server info in the object hres = pNamespace->DecorateObject( pInstTemplate ); if ( SUCCEEDED( hres ) ) { // Set the info appropriately now baseed on whether we are local to // the machine or In-Proc to WMI if ( dwDestContext == MSHCTX_INPROC ) { // We will use the hiperf provider interface // we already have loaded. pInfo->SetDirect(rClientClsid, pNamespace->GetName(), pInstTemplate, pProviderCacheRecord->GetHiPerf() ); } else { if (!pInfo->SetClientLoadable(rClientClsid, pNamespace->GetName(), pInstTemplate)) hres = WBEM_E_OUT_OF_MEMORY; } } return hres; } // Ensure that we will indeed have a refresher record. CRefresherRecord* pRecord = NULL; hres = FindRefresherRecord(pDestRefresherId, TRUE, &pRecord); if ( SUCCEEDED ( hres ) ) { // Now let the record take care of getting the object inside itself hres = pRecord->AddObjectRefresher( pProviderCacheRecord, pNamespace, pInstTemplate, lFlags, pContext, pInfo ); } if ( NULL != pRecord ) { pRecord->Release(); } } else { return WBEM_E_INVALID_OPERATION; } return hres; } HRESULT CRefresherCache::CreateEnumInfoForProvider(CRefresherId* pDestRefresherId, CCommonProviderCacheRecord* pProviderCacheRecord, CWbemNamespace* pNamespace, REFCLSID rClientClsid, CWbemObject* pInstTemplate, LPCWSTR wszClass, long lFlags, IWbemContext* pContext, CRefreshInfo* pInfo) { HRESULT hres = WBEM_S_NO_ERROR; MSHCTX dwDestContext = GetDestinationContext(pDestRefresherId); // The client loadable id must be set if ( rClientClsid != CLSID_NULL ) { // By decorating the object, we will store namespace and // server info so that a client can auto-reconnect to // us if necessary hres = pNamespace->DecorateObject( pInstTemplate ); if ( FAILED( hres ) ) { return hres; } // If this is In-Proc or Local, we'll just let the normal // client loadable logic handle it if( dwDestContext == MSHCTX_LOCAL || dwDestContext == MSHCTX_INPROC ) { // By decorating the object, we will store namespace and // server info in the object hres = pNamespace->DecorateObject( pInstTemplate ); if ( SUCCEEDED( hres ) ) { // Set the info appropriately now baseed on whether we are local to // the machine or In-Proc to WMI if ( dwDestContext == MSHCTX_INPROC ) { // We will use the hiperf provider interface // we already have loaded. pInfo->SetDirect(rClientClsid, pNamespace->GetName(), pInstTemplate, pProviderCacheRecord->GetHiPerf() ); } else { if (!pInfo->SetClientLoadable(rClientClsid, pNamespace->GetName(), pInstTemplate)) hres = WBEM_E_OUT_OF_MEMORY; } } return hres; } // Ensure that we will indeed have a refresher record. CRefresherRecord* pRecord = NULL; hres = FindRefresherRecord(pDestRefresherId, TRUE, &pRecord); if ( SUCCEEDED ( hres ) ) { // Add an enumeration to the Refresher hres = pRecord->AddEnumRefresher(pProviderCacheRecord, pNamespace, pInstTemplate, wszClass, lFlags, pContext, pInfo ); } if ( NULL != pRecord ) { pRecord->Release(); } } else { hres = WBEM_E_INVALID_OPERATION; } return hres; } HRESULT CRefresherCache::RemoveObjectFromRefresher(CRefresherId* pId, long lId, long lFlags) { // Enters and exits using scoping CInCritSec ics( &m_cs ); // Find the refresher // ================== CRefresherRecord* pRefresherRecord = NULL; HRESULT hres = FindRefresherRecord(pId, FALSE, &pRefresherRecord ); // Make sure this guy is released CReleaseMe rm( (IWbemRemoteRefresher*) pRefresherRecord ); // Both are error conditions if ( FAILED( hres ) || pRefresherRecord == NULL ) { return hres; } // Remove it from the record // ========================= return pRefresherRecord->Remove(lId); } MSHCTX CRefresherCache::GetDestinationContext(CRefresherId* pRefresherId) { char szBuffer[MAX_COMPUTERNAME_LENGTH + 1]; DWORD dwLen = MAX_COMPUTERNAME_LENGTH + 1; GetComputerNameA(szBuffer, &dwLen); if(_stricmp(szBuffer, pRefresherId->GetMachineName())) return MSHCTX_DIFFERENTMACHINE; if(pRefresherId->GetProcessId() != GetCurrentProcessId()) return MSHCTX_LOCAL; else return MSHCTX_INPROC; } BOOL CRefresherCache::RemoveRefresherRecord(CRefresherRecord* pRecord) { // Enters and exits using scoping CInCritSec ics( &m_cs ); // Check that the record is actually released, in case another thread successfully requested // the record from FindRefresherRecord() which will have AddRef'd the record again. if ( pRecord->IsReleased() ) { for(int i = 0; i < m_apRefreshers.GetSize(); i++) { if(m_apRefreshers[i] == pRecord) { m_apRefreshers.RemoveAt(i); return TRUE; } } } return FALSE; } //***************************************************************************** //***************************************************************************** // OBJECT REFRESHER //***************************************************************************** //***************************************************************************** CObjectRefresher::CObjectRefresher(CWbemObject* pTemplate) { IWbemClassObject* pCopy; pTemplate->Clone(&pCopy); m_pRefreshedObject = (CWbemObject*)pCopy; } CObjectRefresher::~CObjectRefresher() { if(m_pRefreshedObject) m_pRefreshedObject->Release(); } ULONG STDMETHODCALLTYPE CObjectRefresher::AddRef() { return InterlockedIncrement(&m_lRef); } ULONG STDMETHODCALLTYPE CObjectRefresher::Release() { long lRef = InterlockedDecrement(&m_lRef); if(lRef == 0) delete this; return lRef; } STDMETHODIMP CObjectRefresher::QueryInterface(REFIID riid, void** ppv) { if(riid == IID_IUnknown || riid == IID_IWbemRefresher) { AddRef(); *ppv = (IWbemRefresher*)this; return S_OK; } else return E_NOINTERFACE; } // Remote Hi Perf Enum support CRemoteHiPerfEnum::CRemoteHiPerfEnum() { } CRemoteHiPerfEnum::~CRemoteHiPerfEnum() { } HRESULT CRemoteHiPerfEnum::GetTransferArrayBlob( long *plBlobType, long *plBlobLen, BYTE** ppBlob) { // This is the correct BLOB type. Beware 800 series was sending WBEM_BLOB_TYPE_ENUM for everything *plBlobType = WBEM_BLOB_TYPE_ENUM; HRESULT hr = WBEM_S_NO_ERROR; long lBuffSize = 0, lLastBuffSize = 0, lNumObjects = 0; BYTE* pData = NULL; // Get through the lock first if ( m_Lock.Lock() ) { // Make sure we have objects to enumerate if ( m_aIdToObject.Size() > 0 ) { // Enumerate the objects in the array and add up the size of the // buffer we will have to allocate for ( DWORD dwCtr = 0; dwCtr < m_aIdToObject.Size(); dwCtr++ ) { CWbemInstance* pInst = (CWbemInstance*) ((CHiPerfEnumData*) m_aIdToObject[dwCtr])->m_pObj;; // Buffer Size lLastBuffSize = pInst->GetTransferArrayBlobSize(); // Skip zero length if ( 0 != lLastBuffSize ) { lBuffSize += lLastBuffSize; lNumObjects++; } } // Make sure we have a size to work with if ( lBuffSize > 0 ) { long lTempBuffSize = lBuffSize; // Entire buffer is prepended by a number of objects and a version lBuffSize += CWbemInstance::GetTransferArrayHeaderSize(); // May require CoTaskMemAlloc() pData = (BYTE*) CoTaskMemAlloc( lBuffSize ); if ( NULL != pData ) { BYTE* pTemp = pData; // Now write the header CWbemInstance::WriteTransferArrayHeader( lNumObjects, &pTemp ); // Now enumerate the objects and transfer into the array BLOB for ( dwCtr = 0; SUCCEEDED(hr) && dwCtr < m_aIdToObject.Size(); dwCtr++ ) { CWbemInstance* pInst = (CWbemInstance*) ((CHiPerfEnumData*) m_aIdToObject[dwCtr])->m_pObj; lLastBuffSize = pInst->GetTransferArrayBlobSize(); if ( lLastBuffSize > 0 ) { long lUsedSize; hr = pInst->GetTransferArrayBlob( lTempBuffSize, &pTemp, &lUsedSize ); #ifdef _DEBUG // During DEBUG HeapValidate our BLOB HeapValidate( GetProcessHeap(), 0, pData ); #endif // Account for BLOB size used if ( SUCCEEDED( hr ) ) { lTempBuffSize -= lUsedSize; } } } // FOR dwCtr // Cleanup if things exploded, otherwise perform garbage collection if ( FAILED( hr ) ) { CoTaskMemFree( pData ); pData = NULL; lBuffSize = 0; } else { // if everything is okay, go ahead and do any necessary garbage collection on // our arrays. m_aReusable.GarbageCollect(); } } else { hr = WBEM_E_OUT_OF_MEMORY; } } // IF lBuffSize > 0 } // IF Size() > 0 m_Lock.Unlock(); } // IF Lock() else { // If we can't get access to the enumerator to figure out which // BLOBs to transfer, something is badly worng. hr = WBEM_E_REFRESHER_BUSY; } // Make sure we store appropriate data *ppBlob = pData; *plBlobLen = lBuffSize; return hr; }