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

1551 lines
46 KiB
C++

/*++
Copyright (C) 1996-2001 Microsoft Corporation
Module Name:
REFRCACH.CPP
Abstract:
Refresher Server Side Implementation
History:
--*/
#include "precomp.h"
#include <wbemcore.h>
//*****************************************************************************
//*****************************************************************************
// 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;
}