2025-11-28 00:35:46 +09:00

1846 lines
47 KiB
C++

////////////////////////////////////////////////////////////////////////
//
// BasicHiPerf.cpp
//
// Module: WMI high performance provider sample code
//
// This is the skeleton code implementation of a high performance
// provider. This file includes the provider and refresher code.
//
// History:
//
//
// Copyright (c) Microsoft Corporation, All Rights Reserved
//
////////////////////////////////////////////////////////////////////////
#define _UNICODE
#define UNICODE
#define STRSAFE_NO_DEPRECATE
//#define _WIN32_WINNT 0x0500
#include <windows.h>
#include <stdio.h>
#include <process.h>
#include <objbase.h>
#include "BasicHiPerf.h"
#include <strsafe.h>
//////////////////////////////////////////////////////////////
//
//
// Global, external and static variables
//
//
//////////////////////////////////////////////////////////////
// This structure quickly defines the properties and types of
// the counters. It is used for simplicity; actual providers
// should enumerate the property names and types
// ==========================================================
struct tagProperties
{
_TCHAR m_tcsPropertyName[128];
DWORD m_dwType;
} g_aCounterProps[] =
{
_T("Counter1"), PROP_DWORD,
_T("Counter2"), PROP_DWORD,
_T("Counter3"), PROP_DWORD,
_T("Counter4"), PROP_DWORD,
_T("Counter5"), PROP_DWORD
};
// The access handle for the object's ID
// =====================================
long g_hID;
// The COM object counter (declared in server.cpp)
// ===============================================
extern long g_lObjects;
// The mock data source (declared as static in CHiPerfProvider)
// ============================================================
CSampleDataSource* CHiPerfProvider::m_pSampleDS = NULL;
// The unique ID generator in CCacheMap - values from 0 to
// (MAX_ENUMERATORS - 1) are reserved for the enumerators
// =======================================================
long CCacheMapEl::m_lGenID = MAX_ENUMERATORS;
//////////////////////////////////////////////////////////////
//
//
// Helper Functions
//
//
//////////////////////////////////////////////////////////////
HRESULT CloneAccess(IWbemObjectAccess* pOriginal, IWbemObjectAccess** ppClone)
//////////////////////////////////////////////////////////////
//
// Returns a IWbemObjectAccess clone of an IWbemObjectAccess
// object.
//
// Parameters:
// pOriginal - the original object
// ppClone - the cloned object
//
//////////////////////////////////////////////////////////////
//ok
{
HRESULT hRes = WBEM_NO_ERROR;
IWbemClassObject* pOurClassObj = NULL;
IWbemClassObject* pClonedClassObj = NULL;
IWbemObjectAccess* pClonedAccessObj = NULL;
// Get the IWbemClassObject interface for the object
// =================================================
hRes = pOriginal->QueryInterface(IID_IWbemClassObject, (PVOID*)&pOurClassObj);
if (FAILED(hRes))
return hRes;
// Clone the object
// ================
hRes = pOurClassObj->Clone(&pClonedClassObj);
pOurClassObj->Release();
if (FAILED(hRes))
return hRes;
// Get the IWbemObjectAccess interface for the cloned object
// =========================================================
hRes = pClonedClassObj->QueryInterface(IID_IWbemObjectAccess, (PVOID*)&pClonedAccessObj);
pClonedClassObj->Release();
if (FAILED(hRes))
return hRes;
// Copy the cloned object into the returned parameter (refcount == 1)
// ==================================================================
*ppClone = pClonedAccessObj;
return WBEM_NO_ERROR;
}
//////////////////////////////////////////////////////////////
//
// Name: GetCurrentImpersonationLevel
//
//
// Description:
//
// Get COM impersonation level of caller.
//
//////////////////////////////////////////////////////////////
DWORD GetCurrentImpersonationLevel ()
{
DWORD t_ImpersonationLevel = RPC_C_IMP_LEVEL_ANONYMOUS ;
HANDLE t_ThreadToken = NULL ;
BOOL t_Status = OpenThreadToken (
GetCurrentThread() ,
TOKEN_QUERY,
TRUE,
&t_ThreadToken
) ;
if ( t_Status )
{
SECURITY_IMPERSONATION_LEVEL t_Level = SecurityAnonymous ;
DWORD t_Returned = 0 ;
t_Status = GetTokenInformation (
t_ThreadToken ,
TokenImpersonationLevel ,
& t_Level ,
sizeof ( SECURITY_IMPERSONATION_LEVEL ) ,
& t_Returned
) ;
CloseHandle ( t_ThreadToken ) ;
if ( t_Status == FALSE )
{
t_ImpersonationLevel = RPC_C_IMP_LEVEL_ANONYMOUS ;
}
else
{
switch ( t_Level )
{
case SecurityAnonymous:
{
t_ImpersonationLevel = RPC_C_IMP_LEVEL_ANONYMOUS ;
}
break ;
case SecurityIdentification:
{
t_ImpersonationLevel = RPC_C_IMP_LEVEL_IDENTIFY ;
}
break ;
case SecurityImpersonation:
{
t_ImpersonationLevel = RPC_C_IMP_LEVEL_IMPERSONATE ;
}
break ;
case SecurityDelegation:
{
t_ImpersonationLevel = RPC_C_IMP_LEVEL_DELEGATE ;
}
break ;
default:
{
t_ImpersonationLevel = RPC_C_IMP_LEVEL_ANONYMOUS ;
}
break ;
}
}
}
else
{
ULONG t_LastError = GetLastError () ;
if ( t_LastError == ERROR_NO_IMPERSONATION_TOKEN || t_LastError == ERROR_NO_TOKEN )
{
t_ImpersonationLevel = RPC_C_IMP_LEVEL_DELEGATE ;
}
else
{
if ( t_LastError == ERROR_CANT_OPEN_ANONYMOUS )
{
t_ImpersonationLevel = RPC_C_IMP_LEVEL_ANONYMOUS ;
}
else
{
t_ImpersonationLevel = RPC_C_IMP_LEVEL_ANONYMOUS ;
}
}
}
return t_ImpersonationLevel ;
}
//////////////////////////////////////////////////////////////
//
//
// CHiPerfProvider
//
//
//////////////////////////////////////////////////////////////
CHiPerfProvider::CHiPerfProvider() : m_lRef(0), m_pTemplate(NULL)
//////////////////////////////////////////////////////////////
//
// Constructor
//
//////////////////////////////////////////////////////////////
//ok
{
// Increment the global COM object counter
// =======================================
InterlockedIncrement(&g_lObjects);
}
CHiPerfProvider::~CHiPerfProvider()
//////////////////////////////////////////////////////////////
//
// Destructor
//
//////////////////////////////////////////////////////////////
//ok
{
long lObjCount = 0;
// Release the template
// ====================
if (NULL != m_pTemplate)
m_pTemplate->Release();
// Decrement the global COM object counter
// =======================================
lObjCount = InterlockedDecrement(&g_lObjects);
// If this is the last object, delete our data source
// ==================================================
if ((0 == lObjCount) && (NULL != m_pSampleDS))
{
delete m_pSampleDS;
m_pSampleDS = NULL;
}
}
//////////////////////////////////////////////////////////////
//
// COM methods
//
//////////////////////////////////////////////////////////////
STDMETHODIMP CHiPerfProvider::QueryInterface(REFIID riid, void** ppv)
//////////////////////////////////////////////////////////////
//
// Standard QueryInterface
//
// Parameters:
// riid - the ID of the requested interface
// ppv - a pointer to the interface pointer
//
//////////////////////////////////////////////////////////////
//ok
{
if(riid == IID_IUnknown)
*ppv = (LPVOID)(IUnknown*)(IWbemProviderInit*)this;
else if(riid == IID_IWbemProviderInit)
*ppv = (LPVOID)(IWbemProviderInit*)this;
else if (riid == IID_IWbemHiPerfProvider)
*ppv = (LPVOID)(IWbemHiPerfProvider*)this;
else return E_NOINTERFACE;
((IUnknown*)*ppv)->AddRef();
return WBEM_NO_ERROR;
}
STDMETHODIMP_(ULONG) CHiPerfProvider::AddRef()
//////////////////////////////////////////////////////////////
//
// Standard COM AddRef
//
//////////////////////////////////////////////////////////////
//ok
{
return InterlockedIncrement(&m_lRef);
}
STDMETHODIMP_(ULONG) CHiPerfProvider::Release()
//////////////////////////////////////////////////////////////
//
// Standard COM Release
//
//////////////////////////////////////////////////////////////
//ok
{
long lRef = InterlockedDecrement(&m_lRef);
if(lRef == 0)
delete this;
return lRef;
}
STDMETHODIMP CHiPerfProvider::Initialize(
/* [unique][in] */ LPWSTR wszUser,
/* [in] */ long lFlags,
/* [in] */ LPWSTR wszNamespace,
/* [unique][in] */ LPWSTR wszLocale,
/* [in] */ IWbemServices __RPC_FAR *pNamespace,
/* [in] */ IWbemContext __RPC_FAR *pCtx,
/* [in] */ IWbemProviderInitSink __RPC_FAR *pInitSink)
//////////////////////////////////////////////////////////////////////
//
// Called once during startup for any one-time initialization. The
// final call to Release() is for any cleanup.
//
// The parameters indicate to the provider which namespace it is being
// invoked for and which User. It also supplies a back pointer to
// WINMGMT so that class definitions can be retrieved.
//
// Initialize will create a single template object that can be used
// by the provider to spawn instances for QueryInstances. It will
// also initialize our mock data source and set the global ID access
// handle.
//
// Parameters:
// wszUser - The current user.
// lFlags - Reserved.
// wszNamespace - The namespace for which we are being activated.
// wszLocale - The locale under which we are to be running.
// pNamespace - An active pointer back into the current namespace
// from which we can retrieve schema objects.
// pCtx - The user's context object. We simply reuse this
// during any reentrant operations into WINMGMT.
// pInitSink - The sink to which we indicate our readiness.
//
//////////////////////////////////////////////////////////////////////
//ok
{
HRESULT hRes = WBEM_NO_ERROR;
IWbemObjectAccess *pAccess = NULL;
BSTR strObject = NULL;
BSTR strPropName = NULL;
if (wszNamespace == 0 || pNamespace == 0 || pInitSink == 0)
{
pInitSink->SetStatus(WBEM_E_FAILED , 0);
return WBEM_NO_ERROR;
}
// Get a sample class object for initialization purposes
// =====================================================
strObject = SysAllocString(SAMPLE_CLASS);
if (strObject == NULL)
{
pInitSink->SetStatus(WBEM_E_OUT_OF_MEMORY , 0);
return WBEM_NO_ERROR;
}
hRes = pNamespace->GetObject(strObject, 0, pCtx, &m_pTemplate, 0);
SysFreeString(strObject);
if (FAILED(hRes))
{
pInitSink->SetStatus(WBEM_E_FAILED , 0);
return WBEM_NO_ERROR;
}
// Initialize the datasource. If the static member
// is not null, then it has already been setup.
// ================================================
if (NULL == m_pSampleDS)
{
m_pSampleDS = new CSampleDataSource;
if (NULL == m_pSampleDS)
{
pInitSink->SetStatus(WBEM_E_FAILED , 0);
return WBEM_NO_ERROR;
}
m_pSampleDS->Initialize(m_pTemplate);
}
// Initialize the global access handle for the object ID.
// If it is not null, then it has already been setup.
// ======================================================
if (NULL == g_hID)
{
// Get the IWbemObjectAccess interface to the object
// =================================================
hRes = m_pTemplate->QueryInterface(IID_IWbemObjectAccess, (PVOID*)&pAccess);
if (FAILED(hRes))
return hRes;
// Get the name property access handle
// ===================================
strPropName = SysAllocString(L"Name");
if (strPropName == NULL)
{
pInitSink->SetStatus(WBEM_E_OUT_OF_MEMORY , 0);
return WBEM_NO_ERROR;
}
hRes = pAccess->GetPropertyHandle(strPropName, 0, &g_hID);
pAccess->Release();
SysFreeString(strPropName);
if (FAILED(hRes))
{
pInitSink->SetStatus(WBEM_E_FAILED , 0);
return WBEM_NO_ERROR;
}
}
// We now have all the instances ready to go and the name handle
// stored. Tell WINMGMT that we're ready to start 'providing'
// =============================================================
pInitSink->SetStatus(WBEM_S_INITIALIZED, 0);
return WBEM_NO_ERROR;
}
STDMETHODIMP CHiPerfProvider::QueryInstances(
/* [in] */ IWbemServices __RPC_FAR *pNamespace,
/* [string][in] */ WCHAR __RPC_FAR *wszClass,
/* [in] */ long lFlags,
/* [in] */ IWbemContext __RPC_FAR *pCtx,
/* [in] */ IWbemObjectSink __RPC_FAR *pSink )
//////////////////////////////////////////////////////////////////////
//
// Called whenever a complete, fresh list of instances for a given
// class is required. The objects are constructed and sent back to the
// caller through the sink. The sink can be used in-line as here, or
// the call can return and a separate thread could be used to deliver
// the instances to the sink.
//
// Parameters:
// pNamespace - A pointer to the relevant namespace. This
// should not be AddRef'ed.
// wszClass - The class name for which instances are required.
// lFlags - Reserved.
// pCtx - The user-supplied context (not used here).
// pSink - The sink to which to deliver the objects. The objects
// can be delivered synchronously through the duration
// of this call or asynchronously (assuming we
// had a separate thread). A IWbemObjectSink::SetStatus
// call is required at the end of the sequence.
//
//////////////////////////////////////////////////////////////////////
//ok
{
HRESULT hRes = WBEM_NO_ERROR;
//Impersonate the client
hRes = CoImpersonateClient () ;
if ( FAILED ( hRes ) && hRes != RPC_E_CALL_COMPLETE)
{
return hRes ;
}
// Check to see if call is at lower than RPC_C_IMP_LEVEL_IMPERSONATE level. If that's the case,
// the provider will not be able to impersonate the client to access the protected resources.
DWORD t_CurrentImpersonationLevel = GetCurrentImpersonationLevel () ;
if ( t_CurrentImpersonationLevel < RPC_C_IMP_LEVEL_IMPERSONATE )
{
// Revert before we perform any operations
CoRevertToSelf () ;
hRes = WBEM_E_ACCESS_DENIED;
return hRes ;
}
IWbemClassObject* pObjectCopy = NULL;
IWbemObjectAccess* pAccessCopy = NULL;
if (pNamespace == 0 || wszClass == 0 || pSink == 0)
return WBEM_E_INVALID_PARAMETER;
// Loop through all instances, indicating an updated version
// to the object sink
// =========================================================
for (int i = 0; i < NUM_INSTANCES; i++)
{
// Create a new instance from the template
// =======================================
hRes = m_pTemplate->SpawnInstance(0, &pObjectCopy);
if (FAILED(hRes))
break;
// Obtain the IWbemObjectAccess interface
// ======================================
hRes = pObjectCopy->QueryInterface(IID_IWbemObjectAccess, (PVOID*)&pAccessCopy);
if (FAILED(hRes))
{
pObjectCopy->Release();
break;
}
// Set the ID
// ==========
WCHAR wszName[20];
StringCbPrintfW(wszName, sizeof(wszName), L"%i", i);
pAccessCopy->WritePropertyValue(g_hID, (long)(wcslen(wszName)+1)*sizeof(WCHAR), (byte *)wszName);
// Initialize the counters
// =======================
hRes = m_pSampleDS->UpdateInstance(pAccessCopy);
// Release the IWbemObjectAccess handle (values are maintained in IWbemClassObject interface)
// ==========================================================================================
pAccessCopy->Release();
if (FAILED(hRes))
{
pObjectCopy->Release();
break;
}
// Send a copy back to the caller
// ==============================
pSink->Indicate(1, &pObjectCopy);
pObjectCopy->Release(); // Don't need this any more
}
// Tell WINMGMT we are all finished supplying objects
// ==================================================
pSink->SetStatus(0, WBEM_NO_ERROR, 0, 0);
return WBEM_NO_ERROR;
}
STDMETHODIMP CHiPerfProvider::CreateRefresher(
/* [in] */ IWbemServices __RPC_FAR *pNamespace,
/* [in] */ long lFlags,
/* [out] */ IWbemRefresher __RPC_FAR *__RPC_FAR *ppRefresher )
//////////////////////////////////////////////////////////////////////
//
// Called whenever a new refresher is needed by the client.
//
// Parameters:
// pNamespace - A pointer to the relevant namespace. Not used.
// lFlags - Reserved.
// ppRefresher - Receives the requested refresher.
//
//////////////////////////////////////////////////////////////////////
//ok
{
//Impersonate the client
HRESULT hRes = CoImpersonateClient () ;
if ( FAILED ( hRes ) && hRes != RPC_E_CALL_COMPLETE)
{
return hRes ;
}
// Check to see if call is at lower than RPC_C_IMP_LEVEL_IMPERSONATE level. If that's the case,
// the provider will not be able to impersonate the client to access the protected resources.
DWORD t_CurrentImpersonationLevel = GetCurrentImpersonationLevel () ;
if ( t_CurrentImpersonationLevel < RPC_C_IMP_LEVEL_IMPERSONATE )
{
// Revert before we perform any operations
CoRevertToSelf () ;
hRes = WBEM_E_ACCESS_DENIED;
return hRes ;
}
if (pNamespace == 0 || ppRefresher == 0)
return WBEM_E_INVALID_PARAMETER;
// Construct and initialize a new empty refresher
// ==============================================
CRefresher* pNewRefresher = new CRefresher(this, m_pSampleDS);
if (pNewRefresher == NULL)
return WBEM_E_OUT_OF_MEMORY;
pNewRefresher->Initialize(m_pTemplate);
// Follow COM rules and AddRef() the thing before sending it back
// ==============================================================
pNewRefresher->AddRef();
*ppRefresher = pNewRefresher;
return WBEM_NO_ERROR;
}
STDMETHODIMP CHiPerfProvider::CreateRefreshableObject(
/* [in] */ IWbemServices __RPC_FAR *pNamespace,
/* [in] */ IWbemObjectAccess __RPC_FAR *pTemplate,
/* [in] */ IWbemRefresher __RPC_FAR *pRefresher,
/* [in] */ long lFlags,
/* [in] */ IWbemContext __RPC_FAR *pContext,
/* [out] */ IWbemObjectAccess __RPC_FAR *__RPC_FAR *ppRefreshable,
/* [out] */ long __RPC_FAR *plId )
//////////////////////////////////////////////////////////////////////
//
// Called whenever a user wants to include an object in a refresher.
//
// Note that the object returned in ppRefreshable is a clone of the
// actual instance maintained by the provider. If refreshers shared
// a copy of the same instance, then a refresh call on one of the
// refreshers would impact the state of both refreshers. This would
// break the refresher rules. Instances in a refresher are only
// allowed to be updated when 'Refresh' is called.
//
// Parameters:
// pNamespace - A pointer to the relevant namespace in WINMGMT.
// pTemplate - A pointer to a copy of the object which is to be
// added. This object itself cannot be used, as
// it not owned locally.
// pRefresher - The refresher to which to add the object.
// lFlags - Not used.
// pContext - Not used here.
// ppRefreshable - A pointer to the internal object which was added
// to the refresher.
// plId - The Object Id (for identification during removal).
//
//////////////////////////////////////////////////////////////////////
//ok
{
HRESULT hRes = WBEM_NO_ERROR;
//Impersonate the client
hRes = CoImpersonateClient () ;
if ( FAILED ( hRes ) && hRes != RPC_E_CALL_COMPLETE)
{
return hRes ;
}
// Check to see if call is at lower than RPC_C_IMP_LEVEL_IMPERSONATE level. If that's the case,
// the provider will not be able to impersonate the client to access the protected resources.
DWORD t_CurrentImpersonationLevel = GetCurrentImpersonationLevel () ;
if ( t_CurrentImpersonationLevel < RPC_C_IMP_LEVEL_IMPERSONATE )
{
// Revert before we perform any operations
CoRevertToSelf () ;
hRes = WBEM_E_ACCESS_DENIED;
return hRes ;
}
// Find out which instance is being requested for addition
// =======================================================
if (pNamespace == 0 || pTemplate == 0 || pRefresher == 0)
return WBEM_E_INVALID_PARAMETER;
// The refresher being supplied by the caller is actually
// one of our own refreshers, so a simple cast is convenient
// so that we can access private members.
// =========================================================
CRefresher *pOurRefresher = (CRefresher *) pRefresher;
// Add the object to the refresher. The ID is set by AddObject
// ===========================================================
hRes = pOurRefresher->AddObject(pTemplate, ppRefreshable, plId);
if (FAILED(hRes))
return hRes;
return WBEM_NO_ERROR;
}
STDMETHODIMP CHiPerfProvider::CreateRefreshableEnum(
/* [in] */ IWbemServices* pNamespace,
/* [in, string] */ LPCWSTR wszClass,
/* [in] */ IWbemRefresher* pRefresher,
/* [in] */ long lFlags,
/* [in] */ IWbemContext* pContext,
/* [in] */ IWbemHiPerfEnum* pHiPerfEnum,
/* [out] */ long* plId )
//////////////////////////////////////////////////////////////////////
//
// Called when an enumerator is being added to a refresher. The
// enumerator will obtain a fresh set of instances of the specified
// class every time that refresh is called.
//
// Since this example provider only provides one class, then any
// enumerator in this refresher will only return instances from this
// class. In the case where there is more than one class, then
// wszClass must be examined to determine which class the enumerator
// is being assigned.
//
// Parameters:
// pNamespace - A pointer to the relevant namespace.
// wszClass - The class name for the requested enumerator.
// pRefresher - The refresher object for which we will add
// the enumerator
// lFlags - Reserved.
// pContext - Not used here.
// pHiPerfEnum - The enumerator to add to the refresher.
// plId - A provider specified ID for the enumerator.
//
//////////////////////////////////////////////////////////////////////
//ok
{
//Impersonate the client
HRESULT hr = CoImpersonateClient () ;
if ( FAILED ( hr ) && hr != RPC_E_CALL_COMPLETE)
{
return hr ;
}
// Check to see if call is at lower than RPC_C_IMP_LEVEL_IMPERSONATE level. If that's the case,
// the provider will not be able to impersonate the client to access the protected resources.
DWORD t_CurrentImpersonationLevel = GetCurrentImpersonationLevel () ;
if ( t_CurrentImpersonationLevel < RPC_C_IMP_LEVEL_IMPERSONATE )
{
// Revert before we perform any operations
CoRevertToSelf () ;
hr = WBEM_E_ACCESS_DENIED;
return hr ;
}
// The refresher being supplied by the caller is actually
// one of our own refreshers, so a simple cast is convenient
// so that we can access private members.
CRefresher *pOurRefresher = (CRefresher *) pRefresher;
// Add the enumerator to the refresher
// The ID is generated by AddEnum.
// ===================================
hr = pOurRefresher->AddEnum(pHiPerfEnum, plId);
return hr;
}
STDMETHODIMP CHiPerfProvider::StopRefreshing(
/* [in] */ IWbemRefresher __RPC_FAR *pRefresher,
/* [in] */ long lId,
/* [in] */ long lFlags )
//////////////////////////////////////////////////////////////////////
//
// Called whenever a user wants to remove an object from a refresher.
//
// Parameters:
// pRefresher - The refresher object from which we are to
// remove the perf object.
// lId - The ID of the object.
// lFlags - Not used.
//
//////////////////////////////////////////////////////////////////////
//ok
{
HRESULT hRes = WBEM_NO_ERROR;
//Impersonate the client
hRes = CoImpersonateClient () ;
if ( FAILED ( hRes ) && hRes != RPC_E_CALL_COMPLETE)
{
return hRes ;
}
// Check to see if call is at lower than RPC_C_IMP_LEVEL_IMPERSONATE level. If that's the case,
// the provider will not be able to impersonate the client to access the protected resources.
DWORD t_CurrentImpersonationLevel = GetCurrentImpersonationLevel () ;
if ( t_CurrentImpersonationLevel < RPC_C_IMP_LEVEL_IMPERSONATE )
{
// Revert before we perform any operations
CoRevertToSelf () ;
hRes = WBEM_E_ACCESS_DENIED;
return hRes ;
}
// The refresher being supplied by the caller is actually
// one of our own refreshers, so a simple cast is convenient
// so that we can access private members.
// =========================================================
CRefresher *pOurRefresher = (CRefresher *) pRefresher;
// An ID that is less than MAX_ENUMERATORS is an enumerator ID.
// Everything else is an object ID
// ============================================================
if (lId < MAX_ENUMERATORS)
hRes = pOurRefresher->RemoveEnum(lId);
else
hRes = pOurRefresher->RemoveObject(lId);
return hRes;
}
STDMETHODIMP CHiPerfProvider::GetObjects(
/* [in] */ IWbemServices* pNamespace,
/* [in] */ long lNumObjects,
/* [in,size_is(lNumObjects)] */ IWbemObjectAccess** apObj,
/* [in] */ long lFlags,
/* [in] */ IWbemContext* pContext)
//////////////////////////////////////////////////////////////////////
//
// Called when a request is made to provide all instances currently
// being managed by the provider in the specified namespace.
//
// Parameters:
// pNamespace - A pointer to the relevant namespace.
// lNumObjects - The number of instances being returned.
// apObj - The array of instances being returned.
// lFlags - Reserved.
// pContext - Not used here.
//
//////////////////////////////////////////////////////////////////////
//ok
{
HRESULT hRes = WBEM_NO_ERROR;
//Impersonate the client
hRes = CoImpersonateClient () ;
if ( FAILED ( hRes ) && hRes != RPC_E_CALL_COMPLETE)
{
return hRes ;
}
// Check to see if call is at lower than RPC_C_IMP_LEVEL_IMPERSONATE level. If that's the case,
// the provider will not be able to impersonate the client to access the protected resources.
DWORD t_CurrentImpersonationLevel = GetCurrentImpersonationLevel () ;
if ( t_CurrentImpersonationLevel < RPC_C_IMP_LEVEL_IMPERSONATE )
{
// Revert before we perform any operations
CoRevertToSelf () ;
hRes = WBEM_E_ACCESS_DENIED;
return hRes ;
}
int nIndex = 0;
DWORD dwID = 0;
IWbemObjectAccess* pCurrObj;
// Cycle through the list of objects, updating each one
// ====================================================
for (nIndex = 0; nIndex < lNumObjects; nIndex++)
{
pCurrObj = apObj[nIndex];
hRes = m_pSampleDS->UpdateInstance(pCurrObj);
if (FAILED(hRes))
break;
}
return hRes;
}
//////////////////////////////////////////////////////////////
//
//
// CRefresher
//
//
//////////////////////////////////////////////////////////////
CRefresher::CRefresher(CHiPerfProvider* pProvider, CSampleDataSource* pDS) : m_lRef(0), m_pDS(pDS)
//////////////////////////////////////////////////////////////////////
//
// Constructor
//
//////////////////////////////////////////////////////////////////////
//ok
{
int nIndex = 0;
// Retain a copy of the provider
// =============================
m_pProvider = pProvider;
if (m_pProvider)
m_pProvider->AddRef();
// Initialize the instance map
// ===========================
for (nIndex = 0; nIndex < MAX_INSTANCES; nIndex++)
m_apInstMap[nIndex] = NULL;
// Initialize the instance cache
// =============================
for (nIndex = 0; nIndex < NUM_INSTANCES; nIndex++)
m_apInstances[nIndex] = NULL;
// Initialize the enumerator cache
// ===============================
for (nIndex = 0; nIndex < MAX_ENUMERATORS; nIndex++)
m_apEnumerator[nIndex] = NULL;
// Increment the global COM object counter
// =======================================
InterlockedIncrement(&g_lObjects);
}
CRefresher::~CRefresher()
//////////////////////////////////////////////////////////////////////
//
// Destructor
//
//////////////////////////////////////////////////////////////////////
//ok
{
int nIndex = 0;
// Delete the instance cache
// =========================
for (nIndex = 0; nIndex < MAX_INSTANCES; nIndex++)
{
if (NULL != m_apInstMap[nIndex])
delete m_apInstMap[nIndex];
}
// Release the cached IWbemObjectAccess instances
// ==============================================
for (nIndex = 0; nIndex < NUM_INSTANCES; nIndex++)
{
if (NULL != m_apInstances[nIndex])
m_apInstances[nIndex]->Release();
}
// Release all of the enumerators
// ==============================
for (nIndex = 0; nIndex < MAX_ENUMERATORS; nIndex++)
{
if (NULL != m_apEnumerator[nIndex])
m_apEnumerator[nIndex]->Release();
}
// Release the provider
// ====================
if (m_pProvider)
m_pProvider->Release();
// Decrement the global COM object counter
// =======================================
InterlockedDecrement(&g_lObjects);
}
DWORD CRefresher::Initialize(IWbemClassObject* pSampleClass)
//////////////////////////////////////////////////////////////////////
//
// Initializes the instances cache.
//
// Parameters:
// pSampleClass - A template of the WMI object from which we
// will create our instances
//
//////////////////////////////////////////////////////////////////////
//ok
{
DWORD dwRes = WBEM_NO_ERROR;
int nIndex = 0;
// Precreate and initialize 5 instances of our sample class, and store
// them in the m_apInstances array as IWbemObjectAccess pointers
// =====================================================================
for (DWORD dwIndex = 0; dwIndex < NUM_INSTANCES; dwIndex++)
{
IWbemClassObject* pClone = NULL;
IWbemObjectAccess* pAccess = NULL;
// Create the instance
// ===================
dwRes = pSampleClass->SpawnInstance(0, &pClone);
if (FAILED(dwRes))
return dwRes;
// Get the IWbemObjectAccess interface
// ===================================
dwRes = pClone->QueryInterface(IID_IWbemObjectAccess, (LPVOID *)&pAccess);
pClone->Release();
if (FAILED(dwRes))
return dwRes;
// Set the ID
// ==========
WCHAR wszName[20];
StringCbPrintfW(wszName, sizeof(wszName), L"%i", dwIndex);
dwRes = pAccess->WritePropertyValue(g_hID, (long)(wcslen(wszName)+1)*sizeof(WCHAR), (byte *)wszName);
if (FAILED(dwRes))
{
pAccess->Release();
return dwRes;
}
// Initialize the counters
// =======================
dwRes = m_pDS->UpdateInstance(pAccess);
if (FAILED(dwRes))
{
pAccess->Release();
return dwRes;
}
// Add to the instance array (refcount == 1)
// =========================================
m_apInstances[dwIndex] = pAccess;
}
return WBEM_NO_ERROR;
}
DWORD CRefresher::AddObject(IWbemObjectAccess *pObj, IWbemObjectAccess **ppReturnObj, long *plId)
//////////////////////////////////////////////////////////////////////
//
// Adds an object to the refresher. This is a private mechanism
// used by CHiPerfProvider and not part of the COM interface.
//
// All of the objects are maintained by the m_apInstances array. This
// is set up during a call to Initialize. Multiple additions of the same
// object to a refresher is legal, so to optimize the refresh, the
// same object is passed back but with a different ID. The ID:object
// relationships are maintained by m_apInstMap.
//
// Parameters:
// pObj - the object to add
// lID - the ID
//
//////////////////////////////////////////////////////////////////////
//ok
{
DWORD dwRes = WBEM_NO_ERROR;
DWORD dwID = 0;
int nIndex = 0;
bool bDone = false;
if (NULL == pObj)
return DWORD(WBEM_E_INVALID_PARAMETER);
// Grab the instance ID
// ====================
WCHAR wszName[20];
long lNumBytes = 0;
dwRes = pObj->ReadPropertyValue(g_hID, sizeof(wszName), &lNumBytes, (byte *)wszName);
if (FAILED(dwRes))
return dwRes;
dwID = _wtoi(wszName);
// Search the map array for an empty spot
// ======================================
for (nIndex = 0; (nIndex < MAX_INSTANCES) && !bDone; nIndex++)
{
if (NULL == m_apInstMap[nIndex])
{
// Create a new cache element
// ==========================
CCacheMapEl* pEl = new CCacheMapEl(dwID);
if (pEl == NULL)
return DWORD(WBEM_E_OUT_OF_MEMORY);
// Set the return parameters
// =========================
*plId = pEl->GetUID();
*ppReturnObj = m_apInstances[dwID];
(*ppReturnObj)->AddRef();
// Set the cache element
// =====================
m_apInstMap[nIndex] = pEl;
bDone = true;
}
}
// If array was full, report an error
// ==================================
if (!bDone)
dwRes = DWORD(WBEM_E_OUT_OF_MEMORY);
return dwRes;
}
DWORD CRefresher::RemoveObject(long lId)
//////////////////////////////////////////////////////////////////////
//
// Removes an object from the refresher. This is a private mechanism
// used by CHiPerfProvider and not part of the COM interface.
//
// Removes an object from the refresher by ID. Use a simple linear
// search to locate the unique ID in the m_apInstMap. Leave the
// cache instance intact until the refrehser is destroyed.
//
// Parameters:
// plID - the unique ID
//
//////////////////////////////////////////////////////////////////////
//ok
{
DWORD dwRes = DWORD(WBEM_E_NOT_FOUND);
int nIndex = 0;
bool bDone = false;
// Loop through the map array looking of the ID
// ============================================
for (nIndex = 0; (nIndex < MAX_INSTANCES) && !bDone; nIndex++)
{
CCacheMapEl* pEl = m_apInstMap[nIndex];
if (NULL != pEl)
{
if (pEl->GetUID() == lId)
{
// Found! So remove map element
// =============================
delete pEl;
m_apInstMap[nIndex] = NULL;
dwRes = WBEM_NO_ERROR;
break;
}
}
}
return dwRes;
}
DWORD CRefresher::AddEnum(IWbemHiPerfEnum *pHiPerfEnum, long *plId)
//////////////////////////////////////////////////////////////////////
//
// Adds an enumerator to the refresher. This is a private mechanism
// used by CHiPerfProvider and not part of the COM interface.
//
// The ID we return for future identification is simply
// the array index.
//
// Note that the the refresher uses an array of enumerators. For a
// hiperf provider that only supports one class, having more than one
// enumerator does not really make sense since a single enumerator will
// manage all instances of a single class for a given refresher.
// NUM_ENUMERATORS may be set to reflect the number of classes if the
// proper class / enumerator management code is added. The implementation
// is up to the provider.
//
// Parameters:
// pHiPerfEnum - the enumerator
// plID - the ID
//
//////////////////////////////////////////////////////////////////////
//ok
{
DWORD dwRes = DWORD(WBEM_E_FAILED);
if (NULL == pHiPerfEnum)
return DWORD(WBEM_E_INVALID_PARAMETER);
// Cycle through the enumerator array, and look for an empty slot
// ==============================================================
for (int nIndex = 0; nIndex < MAX_ENUMERATORS; nIndex++)
{
if (NULL == m_apEnumerator[nIndex])
{
// Add all the objects at once into the enumerator
// ===============================================
long alIDs[NUM_INSTANCES];
for ( int nInst = 0; nInst < NUM_INSTANCES; nInst++ )
alIDs[nInst] = nInst;
dwRes = pHiPerfEnum->AddObjects( 0L, NUM_INSTANCES, alIDs, m_apInstances );
if (FAILED(dwRes))
return dwRes;
// Add the enumerator to the refresher (released by the refresher's destructor)
// ============================================================================
m_apEnumerator[nIndex] = pHiPerfEnum;
pHiPerfEnum->AddRef();
*plId = nIndex;
dwRes = WBEM_NO_ERROR;
break;
}
}
return dwRes;
}
DWORD CRefresher::RemoveEnum(long lId)
//////////////////////////////////////////////////////////////////////
//
// Removes an enumerator from the refresher. This is a private mechanism
// used by CHiPerfProvider and not part of the COM interface.
//
// Removes an enumerator from the refresher by ID. In our case, the ID
// is actually the array index we used internally, so it is simple
// to locate and remove the object.
//
// Parameters:
// plID - the ID
//
//////////////////////////////////////////////////////////////////////
//ok
{
// Verify the element
// ==================
if (NULL != m_apEnumerator[lId])
{
// And remove the enumerator
// =========================
m_apEnumerator[lId]->Release();
m_apEnumerator[lId] = NULL;
return WBEM_NO_ERROR;
}
return DWORD(WBEM_E_FAILED);
}
STDMETHODIMP CRefresher::QueryInterface(REFIID riid, void** ppv)
//////////////////////////////////////////////////////////////////////
//
// Standard COM QueryInterface
//
//////////////////////////////////////////////////////////////////////
//ok
{
if (riid == IID_IUnknown)
*ppv = (LPVOID)(IUnknown*)this;
else if (riid == IID_IWbemRefresher)
*ppv = (LPVOID)(IWbemRefresher*)this;
else return E_NOINTERFACE;
((IUnknown*)*ppv)->AddRef();
return S_OK;
}
STDMETHODIMP_(ULONG) CRefresher::AddRef()
//////////////////////////////////////////////////////////////////////
//
// Standard COM AddRef
//
//////////////////////////////////////////////////////////////////////
//ok
{
return InterlockedIncrement(&m_lRef);
}
STDMETHODIMP_(ULONG) CRefresher::Release()
//////////////////////////////////////////////////////////////////////
//
// Standard COM Release
//
//////////////////////////////////////////////////////////////////////
//ok
{
long lRef = InterlockedDecrement(&m_lRef);
if(lRef == 0)
delete this;
return lRef;
}
STDMETHODIMP CRefresher::Refresh(/* [in] */ long lFlags)
//////////////////////////////////////////////////////////////////////
//
// Executed to refresh a set of instances bound to the particular
// refresher.
//
// In most situations the instance data, such as counter values and
// the set of current instances within any existing enumerators, would
// be updated whenever Refresh was called. For this example, we are
// going to or data source to update the counter data. Since we are
// using a fixed set of instances, we are not adding or removing anything
// from the enumerator.
//
// Parameters:
// lFlags - not used
//
//////////////////////////////////////////////////////////////////////
//ok
{
HRESULT hRes = WBEM_NO_ERROR;
//Impersonate the client
hRes = CoImpersonateClient () ;
if ( FAILED ( hRes ) && hRes != RPC_E_CALL_COMPLETE)
{
return hRes ;
}
// Check to see if call is at lower than RPC_C_IMP_LEVEL_IMPERSONATE level. If that's the case,
// the provider will not be able to impersonate the client to access the protected resources.
DWORD t_CurrentImpersonationLevel = GetCurrentImpersonationLevel () ;
if ( t_CurrentImpersonationLevel < RPC_C_IMP_LEVEL_IMPERSONATE )
{
// Revert before we perform any operations
CoRevertToSelf () ;
hRes = WBEM_E_ACCESS_DENIED;
return hRes ;
}
IWbemObjectAccess* pObj;
DWORD dwVal = 0;
long hCtr = 0;
// A simple loop that iterates through all instances that have been
// added to the refresher, and updates their counter values
// ================================================================
for (int nInst = 0; nInst < NUM_INSTANCES; nInst++)
{
pObj = m_apInstances[nInst];
if (NULL != pObj)
{
hRes = m_pDS->UpdateInstance(pObj);
if (FAILED(hRes))
break;
}
}
return hRes;
}
//////////////////////////////////////////////////////////////
//
//
// CSampleDataSource
//
//
//////////////////////////////////////////////////////////////
CSampleDataSource::CSampleDataSource() : m_hThread(NULL)
//////////////////////////////////////////////////////////////////////
//
// Constructor
//
//////////////////////////////////////////////////////////////////////
//ok
{
m_hQuit = CreateEvent( NULL, TRUE, FALSE, NULL );
}
CSampleDataSource::~CSampleDataSource()
//////////////////////////////////////////////////////////////////////
//
// Destructor
//
//////////////////////////////////////////////////////////////////////
//ok
{
if (NULL != m_hThread)
{
// Signal the thread to terminate
// ==============================
SetEvent( m_hQuit );
DWORD dwRet = WaitForSingleObject( m_hThread, 60000 );
switch ( dwRet )
{
case WAIT_OBJECT_0:
{
}break;
case WAIT_TIMEOUT:
{
// Something is wrong - handle appropriately
}break;
}
m_hThread = NULL;
}
}
DWORD CSampleDataSource::Initialize(IWbemClassObject* pSampleClass)
//////////////////////////////////////////////////////////////
//
// Initializes the data source.
//
// Set the access handles for the properties of the WMI objects
// and create a thread that will independantly update the
// counter values.
//
// Parameters:
// pSampleClass - a template of the class for which
// handles are required
//
//////////////////////////////////////////////////////////////
//ok
{
DWORD dwRes = DWORD(WBEM_E_FAILED);
unsigned uThread = 0;
// Set the handles for the object (valid for all instances)
// ========================================================
dwRes = SetHandles(pSampleClass);
if (FAILED(dwRes))
return dwRes;
// Create the simulation thread
// ============================
m_hThread = (PVOID)_beginthreadex(NULL, 0, CSampleDataSource::ThreadEntry, this, 0, &uThread);
if (0 == m_hThread)
return DWORD(WBEM_E_FAILED);
return WBEM_NO_ERROR;
}
DWORD CSampleDataSource::SetHandles(IWbemClassObject* pSampleClass)
//////////////////////////////////////////////////////////////
//
// Called by the initialization method.
//
// Get the access handles for the well-known properties in
// the WMI object. These handles are the same for all instances
// of the object.
//
// If the method fails, the property handle values may be in
// an indeterminite state.
//
// Parameters:
// pSampleClass - a template of the class for which
// handles are required
//
//////////////////////////////////////////////////////////////
//ok
{
DWORD dwRes = WBEM_NO_ERROR;
IWbemObjectAccess *pAccess = NULL;
BSTR strPropName = NULL;
if (NULL == pSampleClass)
return DWORD(WBEM_E_FAILED);
// Get the IWbemAccess interface to the class
// ==========================================
dwRes = pSampleClass->QueryInterface(IID_IWbemObjectAccess, (LPVOID *)&pAccess);
if (FAILED(dwRes))
return dwRes;
// Get the counter properties access handles
// =========================================
for (int i = ctr1; i < NumCtrs; i++)
{
strPropName = SysAllocString(g_aCounterProps[i].m_tcsPropertyName);
if (!strPropName)
{
pAccess->Release();
return DWORD(WBEM_E_OUT_OF_MEMORY);
}
dwRes = pAccess->GetPropertyHandle(strPropName, 0, &m_alHandle[i]);
SysFreeString(strPropName);
if (FAILED(dwRes))
{
pAccess->Release();
return dwRes;
}
}
pAccess->Release();
return WBEM_NO_ERROR;
}
DWORD CSampleDataSource::UpdateInstance(IWbemObjectAccess* pAccess)
//////////////////////////////////////////////////////////////
//
// Updates a given instance with the stored counter values.
//
// Fetched the ID of the object, retrieves the corresponding
// CSampleInstance object, and copies the counter values
// into the object counter properties.
//
// Parameters:
// pAccess - the object to be updated
//
//////////////////////////////////////////////////////////////
//ok
{
DWORD dwRes = DWORD(WBEM_E_FAILED);
DWORD dwID = 0;
// Get the ID of the object
// ========================
WCHAR wszName[20];
long lNumBytes = 0;
dwRes = pAccess->ReadPropertyValue(g_hID, sizeof(wszName), &lNumBytes, (byte *)wszName);
if (FAILED(dwRes))
return dwRes;
dwID = _wtoi(wszName);
// Get the counter data
// ====================
CSampleInstance* pInst = &(m_aInstance[dwID]);
// And set the counter properties
// ==============================
pInst->Lock(true);
for (int nProp = ctr1; nProp < NumCtrs; nProp++)
{
DWORD dwVal = 0;
dwRes = pInst->GetCounter(nProp, &dwVal);
if (FAILED(dwRes))
break;
dwRes = pAccess->WriteDWORD(m_alHandle[nProp], dwVal);
if (FAILED(dwRes))
break;
}
pInst->Lock(false);
return dwRes;
}
DWORD CSampleDataSource::Simulate()
//////////////////////////////////////////////////////////////
//
// This will simulate real changes to the data set. The
// thread will periodically cycle through the instances
// modifying their counter values. The thread will be killed
// by the calling thread.
//
//////////////////////////////////////////////////////////////
//ok
{
CSampleInstance* pCurrInst = NULL;
DWORD dwVal = 0;
// Loop until the thread is killed
// ===============================
while ( WaitForSingleObject( m_hQuit, 0 ) == WAIT_TIMEOUT )
{
// Loop through the instances
// ==========================
for (int nInst = 0; nInst < NUM_INSTANCES; nInst++)
{
// Get the current instance and lock it
// ====================================
pCurrInst = &(m_aInstance[nInst]);
pCurrInst->Lock(true);
// Update all of the counters
// ==========================
for (int nCtr = ctr1; nCtr < NumCtrs; nCtr++)
{
dwVal = GetTickCount() / (nCtr + 1);
pCurrInst->SetCounter(nCtr, dwVal);
}
pCurrInst->Lock(false);
}
// Wait a while
// ============
Sleep(100);
}
return WBEM_NO_ERROR;
}
unsigned CSampleDataSource::ThreadEntry(void* pArgs)
//////////////////////////////////////////////////////////////
//
// The entry point for the simulation. Calls Simulate.
//
// Parameters:
// pArgs - contains the this pointer of the calling
// object
//
//////////////////////////////////////////////////////////////
//ok
{
CSampleDataSource* pThis = (CSampleDataSource*)pArgs;
pThis->Simulate();
return 0;
}
//////////////////////////////////////////////////////////////
//
//
// CSampleInstance
//
//
//////////////////////////////////////////////////////////////
CSampleInstance::CSampleInstance()
//////////////////////////////////////////////////////////////
//
// Constructor
//
//////////////////////////////////////////////////////////////
//ok
{
InitializeCriticalSection(&CS);
}
CSampleInstance::CSampleInstance(const CSampleInstance &aInst)
//////////////////////////////////////////////////////////////
//
// Copy Constructor
//
//////////////////////////////////////////////////////////////
//ok
{
InitializeCriticalSection(&CS);
for (int i = ctr1; i < NumCtrs; i++)
m_aCounter[i] = aInst.m_aCounter[i];
}
CSampleInstance::~CSampleInstance()
//////////////////////////////////////////////////////////////
//
// Destructor
//
//////////////////////////////////////////////////////////////
//ok
{
DeleteCriticalSection(&CS);
}
DWORD CSampleInstance::Lock(bool bLock)
//////////////////////////////////////////////////////////////
//
// Locks or unlocks the instance.
//
// Parameters:
// bLock - true = lock; false = unlock
//
//////////////////////////////////////////////////////////////
//ok
{
if (bLock)
EnterCriticalSection(&CS);
else
LeaveCriticalSection(&CS);
return WBEM_NO_ERROR;
}
DWORD CSampleInstance::SetCounter(long lCtrIndex, DWORD dwVal)
//////////////////////////////////////////////////////////////
//
// Sets the counter value for an indexed counter.
//
// Parameters:
// lCtrIndex - the index of the counter
// dwVal - the value of the counter
//
//////////////////////////////////////////////////////////////
//ok
{
DWORD dwRes = DWORD(WBEM_E_FAILED);
// Verify counter index
// ====================
if ((lCtrIndex >= 0) && (lCtrIndex < NumCtrs))
{
// Update the counter value
// ========================
m_aCounter[lCtrIndex] = dwVal;
dwRes = WBEM_NO_ERROR;
}
return dwRes;
}
DWORD CSampleInstance::GetCounter(long lCtrIndex, DWORD* pdwVal)
//////////////////////////////////////////////////////////////
//
// Returns the counter value for an indexed counter.
//
// Parameters:
// lCtrIndex - the index of the counter
// dwVal - the return parameter for the value
//
//////////////////////////////////////////////////////////////
//ok
{
DWORD dwRes = DWORD(WBEM_E_FAILED);
// Verify counter index
// ====================
if ((lCtrIndex >= 0) && (lCtrIndex < NumCtrs))
{
// Set return values
// =================
*pdwVal = m_aCounter[lCtrIndex];
dwRes = WBEM_NO_ERROR;
}
return dwRes;
}