mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1260 lines
36 KiB
1260 lines
36 KiB
//***************************************************************************
|
|
//
|
|
// (c) 1997-1999 by Microsoft Corp.
|
|
//
|
|
// REFRESHR.CPP
|
|
//
|
|
// Mapped NT5 Perf Counter Provider
|
|
//
|
|
// raymcc 02-Dec-97 Created.
|
|
// raymcc 20-Feb-98 Updated to use new initializer.
|
|
// bobw 8-Jub-98 optimized for use with NT Perf counters
|
|
//
|
|
//***************************************************************************
|
|
|
|
#include "wpheader.h"
|
|
#include <stdio.h>
|
|
#include "oahelp.inl"
|
|
|
|
#define __WBEMSECURITY 1
|
|
|
|
// HRESULT CNt5PerfProvider::CheckImpersonationLevel (void);
|
|
// BOOL CNt5PerfProvider::HasPermission (void);
|
|
|
|
// Timeout for our wait calls
|
|
#define REFRESHER_MUTEX_WAIT_TIMEOUT 10000
|
|
|
|
class CMutexReleaseMe
|
|
{
|
|
private:
|
|
HANDLE m_hMutex;
|
|
|
|
public:
|
|
CMutexReleaseMe( HANDLE hMutex ) : m_hMutex( hMutex ) {};
|
|
~CMutexReleaseMe() { if ( NULL != m_hMutex ) ReleaseMutex( m_hMutex ); };
|
|
};
|
|
|
|
//***************************************************************************
|
|
//
|
|
// RefresherCacheEl::RefresherCacheEl
|
|
//
|
|
// Constructor
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
RefresherCacheEl::RefresherCacheEl()
|
|
{
|
|
m_dwPerfObjIx = 0;
|
|
m_pClassMap = NULL;
|
|
m_pSingleton = NULL;
|
|
m_lSingletonId = 0;
|
|
m_plIds = NULL; // array of ID's
|
|
m_lEnumArraySize = 0; // size of ID array in elements
|
|
m_pHiPerfEnum = NULL;
|
|
m_lEnumId = 0;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// RefresherCacheEl::~RefresherCacheEl()
|
|
//
|
|
// Destructor
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
RefresherCacheEl::~RefresherCacheEl()
|
|
{
|
|
LONG nNumInstances;
|
|
int i;
|
|
|
|
delete m_pClassMap;
|
|
|
|
if (m_pSingleton != NULL) {
|
|
m_pSingleton->Release();
|
|
m_pSingleton = NULL;
|
|
m_lSingletonId = 0;
|
|
}
|
|
|
|
nNumInstances = m_aInstances.Size();
|
|
for (i = 0; i < nNumInstances; i++) {
|
|
delete (CachedInst *) m_aInstances[i];
|
|
}
|
|
|
|
nNumInstances = m_aEnumInstances.Size();
|
|
if (nNumInstances> 0) {
|
|
IWbemObjectAccess *pAccess;
|
|
for (i = 0; i < nNumInstances ; i++) {
|
|
pAccess = (IWbemObjectAccess *)(m_aEnumInstances.GetAt(i));
|
|
if (pAccess != NULL) {
|
|
pAccess->Release();
|
|
}
|
|
}
|
|
m_aEnumInstances.Empty();
|
|
}
|
|
|
|
if (m_plIds != NULL) {
|
|
delete (m_plIds);
|
|
m_plIds = NULL;
|
|
m_lEnumArraySize = 0;
|
|
}
|
|
|
|
if (m_pHiPerfEnum != NULL) {
|
|
m_pHiPerfEnum->Release();
|
|
m_pHiPerfEnum = NULL;
|
|
}
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNt5Refresher constructor
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
CNt5Refresher::CNt5Refresher(CNt5PerfProvider *pPerfProviderArg)
|
|
{
|
|
assert (pPerfProviderArg != NULL);
|
|
|
|
m_ClsidType = pPerfProviderArg->m_OriginClsid;
|
|
m_pPerfProvider = pPerfProviderArg;
|
|
|
|
m_pPerfProvider = NULL; // for testing of local class map
|
|
|
|
if (m_pPerfProvider != NULL) {
|
|
m_pPerfProvider->AddRef();
|
|
}
|
|
m_hAccessMutex = CreateMutex (NULL, TRUE, NULL);
|
|
m_dwGetGetNextClassIndex = 0;
|
|
m_lRef = 0; // COM Ref Count
|
|
m_lProbableId = 1; // Used for new IDs
|
|
m_aCache.Empty(); // clear and reset the array
|
|
RELEASE_MUTEX (m_hAccessMutex);
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNt5Refresher destructor
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
CNt5Refresher::~CNt5Refresher()
|
|
{
|
|
int nNumElements;
|
|
int i;
|
|
|
|
PRefresherCacheEl pCacheEl;
|
|
|
|
assert (m_lRef == 0);
|
|
|
|
// Make sure we get access to the mutex before we try and clean things up.
|
|
// If we don't get it in a reasonable time, something's up. Since we're
|
|
// destructing, we'll just quietly let stuff go.
|
|
|
|
if ( WaitForSingleObject( m_hAccessMutex, REFRESHER_MUTEX_WAIT_TIMEOUT ) == WAIT_OBJECT_0 )
|
|
{
|
|
// This will auto-release the mutex in case something bad happens
|
|
CMutexReleaseMe mrm( m_hAccessMutex );
|
|
|
|
nNumElements = m_aCache.Size();
|
|
for (i = 0; i < nNumElements; i++) {
|
|
pCacheEl = (PRefresherCacheEl)m_aCache[i];
|
|
|
|
// We want to call this once for each instance
|
|
for ( int n = 0; n < pCacheEl->m_aInstances.Size(); n++ )
|
|
{
|
|
m_PerfObj.RemoveClass (pCacheEl->m_pClassMap->m_pClassDef);
|
|
}
|
|
|
|
// If we have a Singleton value, RemoveClass should be
|
|
// called once more
|
|
if ( NULL != pCacheEl->m_pSingleton )
|
|
{
|
|
m_PerfObj.RemoveClass (pCacheEl->m_pClassMap->m_pClassDef);
|
|
}
|
|
|
|
// And finally if we have an enumerator, remove the class
|
|
// once more.
|
|
if ( NULL != pCacheEl->m_pHiPerfEnum )
|
|
{
|
|
m_PerfObj.RemoveClass (pCacheEl->m_pClassMap->m_pClassDef);
|
|
}
|
|
|
|
delete pCacheEl;
|
|
}
|
|
|
|
if (m_pPerfProvider != NULL) {
|
|
m_pPerfProvider->Release();
|
|
m_pPerfProvider = NULL;
|
|
}
|
|
}
|
|
|
|
CloseHandle (m_hAccessMutex);
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNt5Refresher::Refresh
|
|
//
|
|
// Executed to refresh a set of instances bound to the particular
|
|
// refresher.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
HRESULT CNt5Refresher::Refresh(/* [in] */ long lFlags)
|
|
{
|
|
HRESULT hrReturn = WBEM_S_NO_ERROR;
|
|
HRESULT hReturn = S_OK;
|
|
BOOL bRes;
|
|
|
|
UNREFERENCED_PARAMETER(lFlags);
|
|
|
|
BOOL bNeedCoImpersonate = FALSE;
|
|
|
|
//
|
|
// this is ugly
|
|
// wmicookr is not impersonating, because
|
|
// it relys on other provider to do that
|
|
// but, it calls the IWbemRefresher::Refresh when it's inside winmgmt or wmiprvse
|
|
// and it calls it from an UN-Impersonated thread
|
|
// so, we need that the provider calls CoImpersonateClient
|
|
// on a Refresh invocation, that is expensive in general,
|
|
// only if the provider has been invoked through the Server CLSID
|
|
//
|
|
BOOL fRevert;
|
|
if (CNt5PerfProvider::CLSID_SERVER == m_ClsidType)
|
|
{
|
|
#ifdef __WBEMSECURITY
|
|
hReturn = CoImpersonateClient(); // make sure we're legit.
|
|
|
|
fRevert = SUCCEEDED( hReturn );
|
|
|
|
// The following error appears to occur when we are in-proc and there is no
|
|
// proxy/stub, so we are effectively impersonating already
|
|
|
|
if ( RPC_E_CALL_COMPLETE == hReturn ) {
|
|
hReturn = S_OK;
|
|
}
|
|
|
|
if (S_OK == hReturn) {
|
|
hReturn = CNt5PerfProvider::CheckImpersonationLevel();
|
|
}
|
|
// Check Registry security here.
|
|
if ((hReturn != S_OK) || (!CNt5PerfProvider::HasPermission())) {
|
|
// if Impersonation level is incorrect or
|
|
// the caller doesn't have permission to read
|
|
// from the registry, then they cannot continue
|
|
hReturn = WBEM_E_ACCESS_DENIED;
|
|
}
|
|
|
|
#else
|
|
hReturn = S_OK;
|
|
#endif
|
|
|
|
}
|
|
|
|
if (hReturn == S_OK)
|
|
{
|
|
// Make sure we get access to the mutex before we continue. If we can't
|
|
// get to it, something's wrong, so we'll just assume we are busy.
|
|
|
|
if ( WaitForSingleObject( m_hAccessMutex, REFRESHER_MUTEX_WAIT_TIMEOUT ) == WAIT_OBJECT_0 )
|
|
{
|
|
// This will auto-release the mutex in case something bad happens
|
|
CMutexReleaseMe mrm( m_hAccessMutex );
|
|
|
|
bRes = PerfHelper::RefreshInstances(this);
|
|
if (!bRes) {
|
|
hrReturn = WBEM_E_FAILED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
hrReturn = WBEM_E_REFRESHER_BUSY;
|
|
}
|
|
}
|
|
|
|
if (CNt5PerfProvider::CLSID_SERVER == m_ClsidType)
|
|
{
|
|
#ifdef __WBEMSECURITY
|
|
// Revert if we successfuly impersonated the user
|
|
if ( fRevert )
|
|
{
|
|
CoRevertToSelf();
|
|
}
|
|
#endif
|
|
}
|
|
|
|
|
|
return hrReturn;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNt5Refresher::AddRef
|
|
//
|
|
// Standard COM AddRef().
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
ULONG CNt5Refresher::AddRef()
|
|
{
|
|
return InterlockedIncrement(&m_lRef);
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNt5Refresher::Release
|
|
//
|
|
// Standard COM Release().
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
ULONG CNt5Refresher::Release()
|
|
{
|
|
long lRef = InterlockedDecrement(&m_lRef);
|
|
if(lRef == 0)
|
|
delete this;
|
|
return lRef;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNt5Refresher::QueryInterface
|
|
//
|
|
// Standard COM QueryInterface().
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
HRESULT CNt5Refresher::QueryInterface(REFIID riid, void** ppv)
|
|
{
|
|
if (riid == IID_IUnknown || riid == IID_IWbemRefresher)
|
|
{
|
|
*ppv = (IWbemRefresher *) this;
|
|
AddRef();
|
|
return S_OK;
|
|
}
|
|
else return E_NOINTERFACE;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNt5Refresher::RemoveObject
|
|
//
|
|
// Removes an object from the refresher. Since we don't know
|
|
// by ID alone which class it is, we loop through all the ones we
|
|
// have until somebody claims it and returns TRUE for a removal.(
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
BOOL CNt5Refresher::RemoveObject(LONG lId)
|
|
{
|
|
BOOL bReturn = FALSE;
|
|
BOOL bRes;
|
|
PRefresherCacheEl pCacheEl;
|
|
int nNumElements;
|
|
|
|
// Make sure we get access to the mutex before we continue. If we can't
|
|
// get to it, something's wrong, so we'll just assume we are busy.
|
|
|
|
if ( WaitForSingleObject( m_hAccessMutex, REFRESHER_MUTEX_WAIT_TIMEOUT ) == WAIT_OBJECT_0 )
|
|
{
|
|
// This will auto-release the mutex in case something bad happens
|
|
CMutexReleaseMe mrm( m_hAccessMutex );
|
|
|
|
nNumElements = m_aCache.Size();
|
|
for (int i = 0; i < nNumElements; i++)
|
|
{
|
|
pCacheEl = PRefresherCacheEl(m_aCache[i]);
|
|
assert (pCacheEl != NULL);
|
|
|
|
bRes = pCacheEl->RemoveInst(lId);
|
|
if (bRes == TRUE) {
|
|
// found the matching instance so
|
|
// de register this with the perf library
|
|
m_PerfObj.RemoveClass (pCacheEl->m_pClassMap->m_pClassDef);
|
|
bReturn = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
SetLastError( (ULONG) WBEM_E_REFRESHER_BUSY );
|
|
bReturn = FALSE;
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNt5Refresher::FindSingletonInst
|
|
//
|
|
// Based on a perf object identification, locates a singleton WBEM
|
|
// instance of that class within this refresher and returns the pointer
|
|
// to it and its WBEM class info.
|
|
//
|
|
// Note that the <dwPerfObjIx> maps directly to a WBEM Class entry.
|
|
//
|
|
// To save execution time, we don't AddRef() the return value and the
|
|
// caller doesn't Release().
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
BOOL CNt5Refresher::FindSingletonInst(
|
|
IN DWORD dwPerfObjIx,
|
|
OUT IWbemObjectAccess **pInst,
|
|
OUT CClassMapInfo **pClsMap
|
|
)
|
|
{
|
|
BOOL bReturn = FALSE;
|
|
PRefresherCacheEl pCacheEl;
|
|
|
|
int l = 0;
|
|
int u = m_aCache.Size() - 1;
|
|
int m;
|
|
|
|
// Binary search the cache.
|
|
// ========================
|
|
|
|
while (l <= u) {
|
|
|
|
m = (l + u) / 2;
|
|
|
|
pCacheEl = PRefresherCacheEl(m_aCache[m]);
|
|
|
|
if (dwPerfObjIx < pCacheEl->m_dwPerfObjIx) {
|
|
u = m - 1;
|
|
} else if (dwPerfObjIx > pCacheEl->m_dwPerfObjIx) {
|
|
l = m + 1;
|
|
} else {
|
|
*pClsMap = pCacheEl->m_pClassMap;
|
|
*pInst = pCacheEl->m_pSingleton; // No AddRef() caller doesn't
|
|
// change ref count
|
|
bReturn = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Not found
|
|
// =========
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNt5Refresher::FindInst
|
|
//
|
|
// Based on a perf object identification, locates a WBEM instance of
|
|
// that class within this refresher and returns the pointer to it.
|
|
//
|
|
// Note that the <dwPerfObjIx> maps directly to a WBEM Class entry.
|
|
//
|
|
// To save execution time, we don't AddRef() the return value and the
|
|
// caller doesn't Release().
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
BOOL CNt5Refresher::FindInst(
|
|
IN DWORD dwPerfObjIx,
|
|
IN LPWSTR pszInstName,
|
|
OUT IWbemObjectAccess **pInst,
|
|
OUT CClassMapInfo **pClsMap
|
|
)
|
|
{
|
|
BOOL bReturn = FALSE;
|
|
IWbemObjectAccess *pTmp;
|
|
PRefresherCacheEl pCacheEl;
|
|
|
|
int l = 0;
|
|
int u = m_aCache.Size() - 1;
|
|
int m;
|
|
|
|
// Binary search the cache.
|
|
// ========================
|
|
|
|
while (l <= u) {
|
|
m = (l + u) / 2;
|
|
|
|
pCacheEl = PRefresherCacheEl(m_aCache[m]);
|
|
|
|
if (dwPerfObjIx < pCacheEl->m_dwPerfObjIx) {
|
|
u = m - 1;
|
|
} else if (dwPerfObjIx > pCacheEl->m_dwPerfObjIx) {
|
|
l = m + 1;
|
|
} else {
|
|
// We found the class. Now do we have the instance?
|
|
// =================================================
|
|
pTmp = pCacheEl->FindInst(pszInstName);
|
|
if (pTmp == 0) {
|
|
bReturn = FALSE; // Didn't have it.
|
|
} else {
|
|
*pInst = pTmp;
|
|
*pClsMap = pCacheEl->m_pClassMap;
|
|
bReturn = TRUE;
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Not found
|
|
// =========
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNt5Refresher::GetObjectIds
|
|
//
|
|
// Gets a list of all the perf object Ids corresponding to the instances
|
|
// in the refresher.
|
|
//
|
|
// Caller uses operator delete to deallocate the returned array.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
BOOL CNt5Refresher::GetObjectIds(
|
|
DWORD *pdwNumIds,
|
|
DWORD **pdwIdList
|
|
)
|
|
{
|
|
DWORD *pdwIds;
|
|
int nNumElements;
|
|
BOOL bReturn;
|
|
|
|
nNumElements = m_aCache.Size();
|
|
|
|
pdwIds = new DWORD[nNumElements ];
|
|
|
|
if (pdwIds != NULL) {
|
|
for (int i = 0; i < nNumElements; i++) {
|
|
pdwIds[i] = PRefresherCacheEl(m_aCache[i])->m_dwPerfObjIx;
|
|
}
|
|
|
|
*pdwIdList = pdwIds;
|
|
*pdwNumIds = nNumElements;
|
|
bReturn = TRUE;
|
|
} else {
|
|
// unable to create buffer
|
|
bReturn = FALSE;
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNt5Refresher::FindUnusedId
|
|
//
|
|
// Finds an ID not in use for new objects to be added to the refresher.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
LONG CNt5Refresher::FindUnusedId()
|
|
{
|
|
PRefresherCacheEl pEl;
|
|
PCachedInst pInst;
|
|
int nRetries = 0x100000; // A hundred thousand retries
|
|
LONG lReturn = -1;
|
|
int i;
|
|
int i2;
|
|
int nNumElements;
|
|
int nNumInstances;
|
|
// assume the object is locked for access
|
|
|
|
Restart:
|
|
while (nRetries--) {
|
|
i = 0;
|
|
nNumElements = m_aCache.Size();
|
|
while(i < nNumElements) {
|
|
pEl = PRefresherCacheEl(m_aCache[i]);
|
|
// test enum Id first
|
|
if (pEl->m_lEnumId == m_lProbableId) {
|
|
m_lProbableId++;
|
|
goto Restart;
|
|
}
|
|
i2 = 0;
|
|
nNumInstances = pEl->m_aInstances.Size();
|
|
while (i2 < nNumInstances) {
|
|
pInst = (PCachedInst) pEl->m_aInstances[i2];
|
|
if (pInst->m_lId == m_lProbableId) {
|
|
m_lProbableId++;
|
|
goto Restart;
|
|
}
|
|
i2++;
|
|
}
|
|
i++;
|
|
}
|
|
|
|
lReturn = m_lProbableId;
|
|
break;
|
|
}
|
|
|
|
return lReturn;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// RefresherCacheEl::RemoveInst
|
|
//
|
|
// Removes the requested instances from the cache element for a particular
|
|
// class.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
|
|
BOOL RefresherCacheEl::RemoveInst(LONG lId)
|
|
{
|
|
BOOL bReturn = FALSE;
|
|
int i;
|
|
PCachedInst pInst;
|
|
int nNumInstances;
|
|
|
|
if (lId == m_lEnumId) {
|
|
// then clean out the enumerator for this object
|
|
nNumInstances = m_aEnumInstances.Size();
|
|
if (nNumInstances> 0) {
|
|
IWbemObjectAccess *pAccess;
|
|
for (i = 0; i < nNumInstances ; i++) {
|
|
pAccess = (IWbemObjectAccess *)(m_aEnumInstances.GetAt(i));
|
|
if (pAccess != NULL) {
|
|
pAccess->Release();
|
|
}
|
|
}
|
|
m_aEnumInstances.Empty();
|
|
}
|
|
|
|
if (m_plIds != NULL) {
|
|
delete (m_plIds);
|
|
m_plIds = NULL;
|
|
m_lEnumArraySize = 0;
|
|
}
|
|
|
|
if (m_pHiPerfEnum != NULL) {
|
|
m_pHiPerfEnum->Release();
|
|
m_pHiPerfEnum = NULL;
|
|
}
|
|
|
|
// Now, if this is a singleton (m_pSingleton != NULL),
|
|
// then check if m_aInstances is empty. If so, then
|
|
// no instances are referencing the singleton object
|
|
// so we can free up its resources.
|
|
|
|
if ( NULL != m_pSingleton && 0 == m_aInstances.Size() )
|
|
{
|
|
m_pSingleton->Release();
|
|
m_pSingleton = NULL;
|
|
}
|
|
|
|
return TRUE;
|
|
} else {
|
|
// walk the instances to find a match
|
|
nNumInstances = m_aInstances.Size();
|
|
for (i = 0; i < nNumInstances; i++) {
|
|
pInst = (PCachedInst) m_aInstances[i];
|
|
if (lId == pInst->m_lId) {
|
|
delete pInst;
|
|
m_aInstances.RemoveAt(i);
|
|
bReturn = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
// Now, if we removed an instance, m_aInstances is empty
|
|
// and this is a singleton (m_pSingleton != NULL), then
|
|
// check if m_pHiPerfEnum is NULL, meaning no Enumerator
|
|
// exists, so none of its instances will be referencing
|
|
// the singleton object, so we can free up its resources.
|
|
|
|
if ( NULL != m_pSingleton
|
|
&& bReturn
|
|
&& 0 == m_aInstances.Size()
|
|
&& NULL == m_pHiPerfEnum )
|
|
{
|
|
m_pSingleton->Release();
|
|
m_pSingleton = NULL;
|
|
}
|
|
|
|
if ( bReturn )
|
|
{
|
|
}
|
|
}
|
|
return bReturn;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNt5Refresher::AddEnum
|
|
//
|
|
// Creates an enumerator for the specified class
|
|
// to it.
|
|
//
|
|
//***************************************************************************
|
|
// ?
|
|
BOOL CNt5Refresher::AddEnum (
|
|
IN IWbemHiPerfEnum *pEnum, // enum interface pointer
|
|
IN CClassMapInfo *pClsMap, // Class of object
|
|
OUT LONG *plId // id for new enum
|
|
)
|
|
{
|
|
BOOL bRes = FALSE;
|
|
LONG lStatus;
|
|
LONG lNewId;
|
|
PRefresherCacheEl pWorkEl;
|
|
int iReturn;
|
|
|
|
// Make sure we get access to the mutex before we continue. If we can't
|
|
// get to it, something's wrong, so we'll just assume we are busy.
|
|
|
|
if ( WaitForSingleObject( m_hAccessMutex, REFRESHER_MUTEX_WAIT_TIMEOUT ) == WAIT_OBJECT_0 )
|
|
{
|
|
// This will auto-release the mutex in case something bad happens
|
|
CMutexReleaseMe mrm( m_hAccessMutex );
|
|
|
|
lNewId = FindUnusedId();
|
|
|
|
if (lNewId != -1) {
|
|
// First, find the cache element corresponding to this object.
|
|
// ===========================================================
|
|
pWorkEl = GetCacheEl(pClsMap);
|
|
|
|
// If <pWorkEl> is NULL, we didn't have anything in the cache
|
|
// and have to add a new one.
|
|
// ==========================================================
|
|
|
|
if (pWorkEl == NULL) {
|
|
bRes = AddNewCacheEl(pClsMap, &pWorkEl);
|
|
}
|
|
|
|
if (pWorkEl != NULL) {
|
|
if (pWorkEl->m_pHiPerfEnum == NULL) {
|
|
// then we can init it as it hasn't been opened
|
|
pEnum->AddRef();
|
|
pWorkEl->m_pHiPerfEnum = pEnum;
|
|
pWorkEl->m_lEnumId = lNewId;
|
|
|
|
assert (pWorkEl->m_aEnumInstances.Size() == 0L);
|
|
bRes = TRUE;
|
|
|
|
if (pClsMap->IsSingleton()) {
|
|
LONG lNumObjInstances;
|
|
// then create the singleton IWbemObjectAccess entry here
|
|
|
|
lNumObjInstances = 1;
|
|
|
|
// If we do NOT have a singleton pointer, make it so.
|
|
if ( NULL == pWorkEl->m_pSingleton )
|
|
{
|
|
// add the new IWbemObjectAccess pointers
|
|
IWbemClassObject *pClsObj;
|
|
|
|
pWorkEl->m_pClassMap->m_pClassDef->SpawnInstance(0, &pClsObj);
|
|
pClsObj->QueryInterface(IID_IWbemObjectAccess, (LPVOID *) &pWorkEl->m_pSingleton);
|
|
pClsObj->Release(); // We only need the IWbemObjectAccess pointer
|
|
|
|
// We don't really care about the singleton id anymore
|
|
// pWorkEl->m_lSingletonId = pWorkEl->m_plIds[0];
|
|
|
|
}
|
|
|
|
if (pWorkEl->m_aEnumInstances.Size() < lNumObjInstances) {
|
|
// alloc and init the ID array
|
|
if (pWorkEl->m_plIds != NULL) {
|
|
delete (pWorkEl->m_plIds);
|
|
}
|
|
|
|
pWorkEl->m_lEnumArraySize = lNumObjInstances;
|
|
pWorkEl->m_plIds = new LONG[lNumObjInstances];
|
|
|
|
if (pWorkEl->m_plIds != NULL) {
|
|
pWorkEl->m_plIds[0] = 0;
|
|
|
|
// AddRef the singleton class and place it in the enuminstances array
|
|
pWorkEl->m_pSingleton->AddRef();
|
|
iReturn = pWorkEl->m_aEnumInstances.Add (pWorkEl->m_pSingleton);
|
|
if (iReturn == CFlexArray::no_error) {
|
|
// Add the singleton object to the enumerator. Then, all we have to
|
|
// do is update this object and we will, by default update the
|
|
// enumerator, since the number of objects in it will always
|
|
// be one.
|
|
|
|
pWorkEl->m_pHiPerfEnum->AddObjects(
|
|
0,
|
|
1,
|
|
pWorkEl->m_plIds,
|
|
(IWbemObjectAccess __RPC_FAR *__RPC_FAR *)pWorkEl->m_aEnumInstances.GetArrayPtr());
|
|
}
|
|
else {
|
|
SetLastError((ULONG) WBEM_E_OUT_OF_MEMORY);
|
|
bRes = FALSE;
|
|
}
|
|
|
|
} else {
|
|
SetLastError ((ULONG)WBEM_E_OUT_OF_MEMORY);
|
|
bRes = FALSE;
|
|
}
|
|
}
|
|
assert (pWorkEl->m_aEnumInstances.Size() >= lNumObjInstances);
|
|
|
|
}
|
|
|
|
// load provider library since all went OK so far
|
|
lStatus = m_PerfObj.AddClass (pClsMap->m_pClassDef, FALSE);
|
|
if (lStatus == ERROR_SUCCESS) {
|
|
// return new ID & successful status
|
|
*plId = lNewId;
|
|
bRes = TRUE;
|
|
} else {
|
|
// set error: Class or library failed to load
|
|
SetLastError ((ULONG)WBEM_E_PROVIDER_FAILURE);
|
|
bRes = FALSE;
|
|
}
|
|
} else {
|
|
// this class already has an enumerator
|
|
// what to do here?
|
|
// for now we'll return the id of the existing one
|
|
SetLastError ((ULONG)WBEM_E_ILLEGAL_OPERATION);
|
|
bRes = FALSE;
|
|
}
|
|
}
|
|
}
|
|
|
|
} // IF WaitForSingleObject
|
|
else
|
|
{
|
|
bRes = FALSE;
|
|
// We're locked out of the mutex
|
|
SetLastError ((ULONG)WBEM_E_REFRESHER_BUSY);
|
|
}
|
|
|
|
return bRes;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNt5Refresher::AddObject
|
|
//
|
|
// Adds the requested object to the refresher and assigns an ID
|
|
// to it.
|
|
//
|
|
//***************************************************************************
|
|
// ?
|
|
BOOL CNt5Refresher::AddObject(
|
|
IN IWbemObjectAccess **ppObj, // Object to add
|
|
IN CClassMapInfo *pClsMap, // Class of object
|
|
OUT LONG *plId // The id of the object added
|
|
)
|
|
{
|
|
BOOL bRes = FALSE;
|
|
LONG lStatus;
|
|
LONG lNewId;
|
|
PRefresherCacheEl pWorkEl;
|
|
|
|
// Make sure we get access to the mutex before we continue. If we can't
|
|
// get to it, something's wrong, so we'll just assume we are busy.
|
|
|
|
if ( WaitForSingleObject( m_hAccessMutex, REFRESHER_MUTEX_WAIT_TIMEOUT ) == WAIT_OBJECT_0 )
|
|
{
|
|
// This will auto-release the mutex in case something bad happens
|
|
CMutexReleaseMe mrm( m_hAccessMutex );
|
|
|
|
lNewId = FindUnusedId();
|
|
|
|
if (lNewId != -1) {
|
|
// First, find the cache element corresponding to this object.
|
|
// ===========================================================
|
|
pWorkEl = GetCacheEl(pClsMap);
|
|
|
|
// If <pWorkEl> is NULL, we didn't have anything in the cache
|
|
// and have to add a new one.
|
|
// ==========================================================
|
|
|
|
if (pWorkEl == NULL) {
|
|
bRes = AddNewCacheEl(pClsMap, &pWorkEl);
|
|
}
|
|
|
|
if (pWorkEl != NULL) {
|
|
// If here, we have successfully added a new cache element.
|
|
// ========================================================
|
|
bRes = pWorkEl->InsertInst(ppObj, lNewId);
|
|
|
|
if (bRes) {
|
|
// load provider library since all went OK so far
|
|
lStatus = m_PerfObj.AddClass (pClsMap->m_pClassDef, FALSE);
|
|
|
|
if (lStatus == ERROR_SUCCESS) {
|
|
// return new ID & successful status
|
|
*plId = lNewId;
|
|
bRes = TRUE;
|
|
} else {
|
|
// set error: Class or library failed to load
|
|
SetLastError ((ULONG)WBEM_E_PROVIDER_FAILURE);
|
|
bRes = FALSE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
} // IF acquired mutex
|
|
else
|
|
{
|
|
bRes = FALSE;
|
|
// Return a busy error
|
|
SetLastError ((ULONG)WBEM_E_REFRESHER_BUSY);
|
|
}
|
|
|
|
return bRes;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNt5Refresher::AddNewCacheEl
|
|
//
|
|
// Adds a new cache element in the proper position so that a binary
|
|
// search on perf object id can occur later.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
BOOL CNt5Refresher::AddNewCacheEl(
|
|
IN CClassMapInfo *pClsMap,
|
|
PRefresherCacheEl *pOutput
|
|
)
|
|
{
|
|
// assumes the object is locked for access
|
|
|
|
PRefresherCacheEl pWorkEl;
|
|
PRefresherCacheEl pNew = 0;
|
|
|
|
int i;
|
|
int nNumElements;
|
|
BOOL bReturn = FALSE;
|
|
|
|
* pOutput = NULL;
|
|
pNew = new RefresherCacheEl;
|
|
|
|
if (pNew != NULL) {
|
|
pNew->m_dwPerfObjIx = pClsMap->GetObjectId();
|
|
pNew->m_pClassMap = pClsMap->CreateDuplicate();
|
|
|
|
if (pNew->m_pClassMap != NULL) {
|
|
nNumElements = m_aCache.Size();
|
|
for (i = 0; i < nNumElements; i++) {
|
|
// walk through the list of cache elements
|
|
// and find the first entry that has a
|
|
// larger index then the one we are adding
|
|
pWorkEl = PRefresherCacheEl(m_aCache[i]);
|
|
if (pNew->m_dwPerfObjIx < pWorkEl->m_dwPerfObjIx) {
|
|
m_aCache.InsertAt(i, pNew);
|
|
*pOutput = pNew;
|
|
bReturn = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (i == nNumElements) {
|
|
// this entry is larger than anyone in the list
|
|
// so Add it to the end.
|
|
// =====-===============
|
|
m_aCache.Add(pNew);
|
|
*pOutput = pNew;
|
|
bReturn = TRUE;
|
|
}
|
|
}
|
|
else {
|
|
// cannot duplicate ClassMap,
|
|
// delte allocated object and return false
|
|
delete pNew;
|
|
}
|
|
|
|
} else {
|
|
// return false
|
|
}
|
|
|
|
return bReturn;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// CNt5Refresher::GetCacheEl
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
PRefresherCacheEl CNt5Refresher::GetCacheEl(
|
|
CClassMapInfo *pClsMap
|
|
)
|
|
{
|
|
// assumes the structure is locked for access
|
|
PRefresherCacheEl pReturn = NULL;
|
|
PRefresherCacheEl pWorkEl;
|
|
int i;
|
|
int nNumElements;
|
|
DWORD dwObjectIdToFind;
|
|
|
|
if (pClsMap != NULL) {
|
|
dwObjectIdToFind = pClsMap->GetObjectId();
|
|
nNumElements = m_aCache.Size();
|
|
for (i = 0; i < nNumElements; i++) {
|
|
pWorkEl = PRefresherCacheEl(m_aCache[i]);
|
|
if (pWorkEl->m_pClassMap->GetObjectId() == dwObjectIdToFind) {
|
|
pReturn = pWorkEl;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
return pReturn;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// RefresherCacheEl::FindInstance
|
|
//
|
|
// Finds an instance in the current cache element for a particular instance.
|
|
// For this to work, the instances have to be sorted by name.
|
|
//
|
|
//***************************************************************************
|
|
// ok
|
|
IWbemObjectAccess *RefresherCacheEl::FindInst(
|
|
LPWSTR pszInstName
|
|
)
|
|
{
|
|
// Binary search the cache.
|
|
// ========================
|
|
|
|
int l = 0;
|
|
int u = m_aInstances.Size() - 1;
|
|
int m;
|
|
CachedInst *pInst;
|
|
|
|
while (l <= u) {
|
|
m = (l + u) / 2;
|
|
|
|
pInst = PCachedInst(m_aInstances[m]);
|
|
|
|
if (_wcsicmp(pszInstName, pInst->m_pName) < 0) {
|
|
u = m - 1;
|
|
} else if (_wcsicmp(pszInstName, pInst->m_pName) > 0) {
|
|
l = m + 1;
|
|
} else {
|
|
// We found the instance.
|
|
// ======================
|
|
return pInst->m_pInst;
|
|
}
|
|
}
|
|
|
|
// Not found
|
|
// =========
|
|
|
|
return NULL;
|
|
}
|
|
|
|
//***************************************************************************
|
|
//
|
|
// Inserts a new instance.
|
|
//
|
|
//***************************************************************************
|
|
//
|
|
BOOL RefresherCacheEl::InsertInst(IWbemObjectAccess **ppNew, LONG lNewId)
|
|
{
|
|
// Save the value passed in
|
|
IWbemObjectAccess* pNew = *ppNew;
|
|
|
|
IWbemClassObject *pObj;
|
|
VARIANT v;
|
|
PCachedInst pNewInst;
|
|
DWORD dwInstanceNameLength;
|
|
PCachedInst pTest;
|
|
BOOL bReturn = FALSE;
|
|
HRESULT hRes;
|
|
int nNumInstances;
|
|
|
|
// Check for singleton.
|
|
// ====================
|
|
if (m_pClassMap->IsSingleton()) {
|
|
|
|
// If we don't already have an object, use the one passed in. Otherwise
|
|
// we will replace it.
|
|
|
|
if ( NULL == m_pSingleton )
|
|
{
|
|
m_pSingleton = pNew;
|
|
m_pSingleton->AddRef();
|
|
// We don't really need the id anymore
|
|
// m_lSingletonId = lNewId;
|
|
}
|
|
else
|
|
{
|
|
// Now we're sneaking around by replacing *ppNew with the
|
|
// singleton we already have. We must release *ppNew in
|
|
// order to get away with this.
|
|
|
|
(*ppNew)->Release();
|
|
|
|
// We need to AddRef() this because *ppNew is now referencing it
|
|
m_pSingleton->AddRef();
|
|
*ppNew = m_pSingleton;
|
|
pNew = m_pSingleton;
|
|
}
|
|
|
|
// Now we will Add this instance just like any other
|
|
|
|
pNewInst = new CachedInst;
|
|
// assert (pNewInst != NULL);
|
|
|
|
if ( pNewInst != NULL )
|
|
{
|
|
// For singletons, none of the other pointers
|
|
// should matter.
|
|
|
|
pNewInst->m_lId = lNewId;
|
|
pNewInst->m_pInst = pNew;
|
|
pNewInst->m_pInst->AddRef();
|
|
|
|
// We are saving the name just to be safe (It will
|
|
// really only be an "@", and I don't believe it
|
|
// will be accessed anywhere else. I hope...)
|
|
pNewInst->m_pName = Macro_CloneLPWSTR(L"@");
|
|
// assert (pNewInst->m_pName != NULL);
|
|
|
|
if ( NULL != pNewInst->m_pName )
|
|
{
|
|
// We can just add this in, since any entries will all be the
|
|
// same anyway.
|
|
|
|
m_aInstances.Add(pNewInst);
|
|
bReturn = TRUE;
|
|
}
|
|
else // Memory Allocation failed
|
|
{
|
|
bReturn = FALSE;
|
|
SetLastError ((DWORD)WBEM_E_OUT_OF_MEMORY);
|
|
delete(pNewInst);
|
|
}
|
|
|
|
}
|
|
else // Memory allocation failed
|
|
{
|
|
bReturn = FALSE;
|
|
SetLastError ((DWORD)WBEM_E_OUT_OF_MEMORY);
|
|
}
|
|
|
|
} else {
|
|
VariantInit(&v);
|
|
|
|
// For multi-instance, get the instance name.
|
|
// ==========================================
|
|
hRes = pNew->QueryInterface(IID_IWbemClassObject, (LPVOID *) &pObj);
|
|
assert (hRes == NO_ERROR);
|
|
|
|
if (hRes == NO_ERROR) {
|
|
hRes = pObj->Get(CBSTR(cszName), 0, &v, 0, 0);
|
|
assert (hRes == NO_ERROR);
|
|
if (hRes == NO_ERROR) {
|
|
if (v.vt == VT_BSTR) {
|
|
bReturn = TRUE;
|
|
} else {
|
|
bReturn = FALSE;
|
|
// the object passed in should have an instance name
|
|
SetLastError ((DWORD)WBEM_E_INVALID_OBJECT_PATH);
|
|
}
|
|
}
|
|
|
|
pObj->Release();
|
|
|
|
if (bReturn) {
|
|
// Construct the new instance.
|
|
// ===========================
|
|
pNewInst = new CachedInst;
|
|
// assert (pNewInst != NULL);
|
|
|
|
if (pNewInst != NULL) {
|
|
pNewInst->m_lId = lNewId;
|
|
pNewInst->m_pInst = pNew;
|
|
pNewInst->m_pInst->AddRef();
|
|
pNewInst->m_pName = Macro_CloneLPWSTR(V_BSTR(&v));
|
|
// assert (pNewInst->m_pName != NULL);
|
|
|
|
if (pNewInst->m_pName != NULL) {
|
|
dwInstanceNameLength = lstrlenW (pNewInst->m_pName) + 1;
|
|
|
|
// parse the instance string now to save processing time later
|
|
pNewInst->m_szParentName = new WCHAR[dwInstanceNameLength];
|
|
// assert (pNewInst->m_szParentName != NULL);
|
|
|
|
pNewInst->m_szInstanceName = new WCHAR[dwInstanceNameLength];
|
|
// assert (pNewInst->m_szInstanceName != NULL);
|
|
|
|
if ((pNewInst->m_szParentName != NULL) &&
|
|
(pNewInst->m_szInstanceName != NULL)) {
|
|
|
|
// break the instance name into components
|
|
bReturn = PerfHelper::ParseInstanceName (pNewInst->m_pName,
|
|
pNewInst->m_szInstanceName,
|
|
pNewInst->m_szParentName ,
|
|
&pNewInst->m_dwIndex);
|
|
|
|
if (bReturn) {
|
|
bReturn = FALSE; // to prime it.
|
|
// Now place the name in the instance cache element.
|
|
// =================================================
|
|
nNumInstances = m_aInstances.Size();
|
|
for (int i = 0; i < nNumInstances; i++) {
|
|
// see if it belongs in the list
|
|
pTest = PCachedInst(m_aInstances[i]);
|
|
if (_wcsicmp(V_BSTR(&v), pTest->m_pName) < 0) {
|
|
m_aInstances.InsertAt(i, pNewInst);
|
|
bReturn = TRUE;
|
|
// once it's been added,
|
|
// there's no point in continuing
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!bReturn) {
|
|
// this goes at the end of the list
|
|
m_aInstances.Add(pNewInst);
|
|
bReturn = TRUE;
|
|
} else {
|
|
// unable to create instance
|
|
SetLastError ((DWORD)WBEM_E_INVALID_OBJECT_PATH);
|
|
}
|
|
}
|
|
}
|
|
// clean up if there's an error
|
|
if (!bReturn) {
|
|
if (pNewInst->m_szParentName != NULL) {
|
|
delete (pNewInst->m_szParentName);
|
|
}
|
|
if (pNewInst->m_szInstanceName != NULL) {
|
|
delete pNewInst->m_szInstanceName;
|
|
}
|
|
delete (pNewInst->m_pName);
|
|
bReturn = FALSE;
|
|
delete (pNewInst);
|
|
}
|
|
} else {
|
|
// unable to alloc memory
|
|
bReturn = FALSE;
|
|
SetLastError ((DWORD)WBEM_E_OUT_OF_MEMORY);
|
|
delete (pNewInst);
|
|
}
|
|
} else {
|
|
// unable to alloc memory
|
|
bReturn = FALSE;
|
|
SetLastError ((DWORD)WBEM_E_OUT_OF_MEMORY);
|
|
}
|
|
} else {
|
|
// return FALSE
|
|
}
|
|
} else {
|
|
// return FALSE
|
|
}
|
|
VariantClear(&v);
|
|
}
|
|
return bReturn;
|
|
}
|