/*++ Copyright (C) 1997-2001 Microsoft Corporation Module Name: Abstract: History: --*/ //*************************************************************************** // // NTPERF.CPP // // Sample NT5 Perf Counter Provider // // raymcc 02-Dec-97 Created // raymcc 20-Feb-98 Updated to use new initializer // //*************************************************************************** #include "precomp.h" #include #include #include #include "ntperf.h" //*************************************************************************** // // CNt5Refresher constructor // //*************************************************************************** // ok CNt5Refresher::CNt5Refresher() { m_lRef = 0; // COM Ref Count // Set the instance cache to all zeros. // As objects are added to the refresher // we simply put them in unused slots in the array. // ================================================ for (int i = 0; i < NUM_SAMPLE_INSTANCES; i++) { m_aInstances[i] = 0; } // Set the values of the property handles to zero. // =============================================== m_hName = 0; m_hCounter1 = 0; m_hCounter2 = 0; m_hCounter3 = 0; } //*************************************************************************** // // CNt5Refresher destructor // //*************************************************************************** // ok CNt5Refresher::~CNt5Refresher() { // Release the cached IWbemObjectAccess instances. // =============================================== for (DWORD i = 0; i < NUM_SAMPLE_INSTANCES; i++) { if (m_aInstances[i]) m_aInstances[i]->Release(); } } //*************************************************************************** // // CNt5Refresher::Refresh // // Executed to refresh a set of instances bound to the particular // refresher. // //*************************************************************************** // ok HRESULT CNt5Refresher::Refresh(/* [in] */ long lFlags) { // Zip through all the objects and increment the values. // ===================================================== for (DWORD i = 0; i < NUM_SAMPLE_INSTANCES; i++) { // Get the object at this location. // ================================ IWbemObjectAccess *pAccess = m_aInstances[i]; // If there is no object in this array slot (a NULL pointer) // there is nothing to refresh. // ========================================================= if (pAccess == 0) continue; // Increment all the counter values to simulate an update. // The client already has a pointer to this object, so // all we have to do is update the values. // ======================================================= DWORD dwVal; pAccess->ReadDWORD(m_hCounter1, &dwVal); dwVal++; pAccess->WriteDWORD(m_hCounter1, dwVal); pAccess->ReadDWORD(m_hCounter3, &dwVal); dwVal++; pAccess->WriteDWORD(m_hCounter3, dwVal); unsigned __int64 qwVal; pAccess->ReadQWORD(m_hCounter2, &qwVal); qwVal++; pAccess->WriteQWORD(m_hCounter2, qwVal); } return NO_ERROR; } //*************************************************************************** // // CNt5Refresher::TransferPropHandles // // This is a private mechanism used by CNt5PerfProvider. // It is used to copy the property handles from the // hi-perf provider object to the refresher. We need these handles to // quickly access the properties in each instance. The same handles are // used for all instances. // //*************************************************************************** // ok void CNt5Refresher::TransferPropHandles(CNt5PerfProvider *pSrc) { m_hName = pSrc->m_hName; m_hCounter1 = pSrc->m_hCounter1; m_hCounter2 = pSrc->m_hCounter2; m_hCounter3 = pSrc->m_hCounter3; } //*************************************************************************** // // 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::AddObject // // Adds an object to the refresher. This is a private mechanism // used by CNt5PerfProvider and not part of the COM interface. // // The ID we return for future identification is simply // the array index. // //*************************************************************************** // ok BOOL CNt5Refresher::AddObject( IWbemObjectAccess *pObj, LONG *plId ) { for (DWORD i = 0; i < NUM_SAMPLE_INSTANCES; i++) { if (m_aInstances[i] == 0) { pObj->AddRef(); m_aInstances[i] = pObj; // The ID we return for future identification is simply // the array index. // ==================================================== *plId = i; return TRUE; } } return FALSE; } //*************************************************************************** // // CNt5Refresher::RemoveObject // // This is a private mechanism used by CNt5PerfProvider and not // part of the COM interface. // // Removes an object 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. // //*************************************************************************** BOOL CNt5Refresher::RemoveObject(LONG lId) { if (m_aInstances[lId] == 0) return FALSE; m_aInstances[lId]->Release(); m_aInstances[lId] = 0; return TRUE; } //*************************************************************************** // // CNt5PerfProvider constructor // //*************************************************************************** // ok CNt5PerfProvider::CNt5PerfProvider() { m_lRef = 0; m_pSampleClass = 0; // All the instances we work with are cached internally. // ===================================================== for (int i = 0; i < NUM_SAMPLE_INSTANCES; i++) m_aInstances[i] = 0; // Property value handles. // ======================= m_hName = 0; // "Name" property in the MOF m_hCounter1 = 0; // "Counter1" in the MOF m_hCounter2 = 0; // "Counter2" in the MOF m_hCounter3 = 0; // "Counter3" in the MOF } //*************************************************************************** // // CNt5PerfProvider destructor // //*************************************************************************** // ok CNt5PerfProvider::~CNt5PerfProvider() { // Release all the objects which have been added to the array. // =========================================================== for (int i = 0; i < NUM_SAMPLE_INSTANCES; i++) if (m_aInstances[i]) m_aInstances[i]->Release(); if (m_pSampleClass) m_pSampleClass->Release(); } //*************************************************************************** // // CNt5Refresher::AddRef // // Standard COM AddRef(). // //*************************************************************************** // ok ULONG CNt5PerfProvider::AddRef() { return InterlockedIncrement(&m_lRef); } //*************************************************************************** // // CNt5Refresher::Release // // Standard COM Release(). // //*************************************************************************** // ok ULONG CNt5PerfProvider::Release() { long lRef = InterlockedDecrement(&m_lRef); if(lRef == 0) delete this; return lRef; } //*************************************************************************** // // CNt5Refresher::QueryInterface // // Standard COM QueryInterface(). We have to support two interfaces, // the IWbemHiPerfProvider interface itself to provide the objects and // the IWbemProviderInit interface to initialize the provider. // //*************************************************************************** // ok HRESULT CNt5PerfProvider::QueryInterface(REFIID riid, void** ppv) { if(riid == IID_IUnknown || riid == IID_IWbemHiPerfProvider) { *ppv = (IWbemHiPerfProvider*) this; AddRef(); return S_OK; } else if (riid == IID_IWbemProviderInit) { *ppv = (IWbemProviderInit *) this; AddRef(); return S_OK; } else return E_NOINTERFACE; } //*************************************************************************** // // CNt5Refresher::Initialize // // Called once during startup. Insdicates 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. // // We perform any one-time initialization in this routine. The // final call to Release() is for any cleanup. // // The current user. // Reserved. // The namespace for which we are being activated. // The locale under which we are to be running. // An active pointer back into the current namespace // from which we can retrieve schema objects. // The user's context object. We simply reuse this // during any reentrant operations into WINMGMT. // The sink to which we indicate our readiness. // //*************************************************************************** // ok HRESULT CNt5PerfProvider::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 ) { BSTR PropName = 0; IWbemObjectAccess *pAccess = 0; // Get a copy of our sample class def so that we can create & maintain // instances of it. // =================================================================== HRESULT hRes = pNamespace->GetObject(BSTR(L"Win32_Nt5PerfTest"), 0, pCtx, &m_pSampleClass, 0 ); if (hRes) return hRes; // Precreate 10 instances, and set them up in an array which // is a member of this C++ class. // // We only store the IWbemObjectAccess pointers, since // we are updating 'well-known' properties and already // know their names. // ========================================================== for (int i = 0; i < NUM_SAMPLE_INSTANCES; i++) { IWbemClassObject *pInst = 0; m_pSampleClass->SpawnInstance(0, &pInst); // Write out the instance name. // ============================ wchar_t buf[128]; swprintf(buf, L"Inst_%d", i); VARIANT vName; VariantInit(&vName); V_BSTR(&vName) = SysAllocString(buf); V_VT(&vName) = VT_BSTR; BSTR PropName = SysAllocString(L"Name"); pInst->Put(PropName, 0, &vName, 0); SysFreeString(PropName); VariantClear(&vName); pInst->QueryInterface(IID_IWbemObjectAccess, (LPVOID *) &pAccess); m_aInstances[i] = pAccess; pInst->Release(); } // Get the property handles for the well-known properties in // this counter type. We cache the property handles // for each property so that we can transfer them to the // refresher later on. // ========================================================= m_pSampleClass->QueryInterface(IID_IWbemObjectAccess, (LPVOID *) &pAccess); PropName = SysAllocString(L"Name"); hRes = pAccess->GetPropertyHandle(PropName, 0, &m_hName); SysFreeString(PropName); PropName = SysAllocString(L"Counter1"); hRes = pAccess->GetPropertyHandle(PropName, 0, &m_hCounter1); SysFreeString(PropName); PropName = SysAllocString(L"Counter2"); hRes = pAccess->GetPropertyHandle(PropName, 0, &m_hCounter2); SysFreeString(PropName); PropName = SysAllocString(L"Counter3"); hRes = pAccess->GetPropertyHandle(PropName, 0, &m_hCounter3); SysFreeString(PropName); pAccess->Release(); // Now let's set all the instance to some default values. // ====================================================== for (i = 0; i < NUM_SAMPLE_INSTANCES; i++) { IWbemObjectAccess *pAccess = m_aInstances[i]; hRes = pAccess->WriteDWORD(m_hCounter1, DWORD(i)); hRes = pAccess->WriteQWORD(m_hCounter2, (_int64) + 100 + i); hRes = pAccess->WriteDWORD(m_hCounter3, DWORD(i + 1000)); } // We now have all the instances ready to go and all the // property handles cached. Tell WINMGMT that we're // ready to start 'providing'. // ===================================================== pInitSink->SetStatus(0, WBEM_S_INITIALIZED); return NO_ERROR; } //*************************************************************************** // // CNt5Refresher::QueryInstances // // 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: // A pointer to the relevant namespace. This // should not be AddRef'ed. // The class name for which instances are required. // Reserved. // The user-supplied context (not used here). // 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 CNt5PerfProvider::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 ) { if (pNamespace == 0 || wszClass == 0 || pSink == 0) return WBEM_E_INVALID_PARAMETER; // Quickly zip through the instances and update the values before // returning them. This is just a dummy operation to make it // look like the instances are continually changing like real // perf counters. // ============================================================== for (int i = 0; i < NUM_SAMPLE_INSTANCES; i++) { IWbemObjectAccess *pAccess = m_aInstances[i]; // Every object can be access one of two ways. In this case // we get the 'other' (primary) interface to this same object. // =========================================================== IWbemClassObject *pOtherFormat = 0; pAccess->QueryInterface(IID_IWbemClassObject, (LPVOID *) &pOtherFormat); // Send a copy back to the caller. // =============================== pSink->Indicate(1, &pOtherFormat); pOtherFormat->Release(); // Don't need this any more } // Tell WINMGMT we are all finished supplying objects. // ================================================= pSink->SetStatus(0, WBEM_NO_ERROR, 0, 0); return NO_ERROR; } //*************************************************************************** // // CNt5Refresher::CreateRefresher // // Called whenever a new refresher is needed by the client. // // Parameters: // A pointer to the relevant namespace. Not used. // Not used. // Receives the requested refresher. // //*************************************************************************** // ok HRESULT CNt5PerfProvider::CreateRefresher( /* [in] */ IWbemServices __RPC_FAR *pNamespace, /* [in] */ long lFlags, /* [out] */ IWbemRefresher __RPC_FAR *__RPC_FAR *ppRefresher ) { if (pNamespace == 0 || ppRefresher == 0) return WBEM_E_INVALID_PARAMETER; // Construct a new empty refresher. // ================================ CNt5Refresher *pNewRefresher = new CNt5Refresher(); // Move copies of the property handles to the refresher // so that it can quickly update property values during // a refresh operation. // ==================================================== pNewRefresher->TransferPropHandles(this); // Follow COM rules and AddRef() the thing before sending it back. // =============================================================== pNewRefresher->AddRef(); *ppRefresher = pNewRefresher; return NO_ERROR; } //*************************************************************************** // // CNt5Refresher::CreateRefreshableObject // // Called whenever a user wants to include an object in a refresher. // // Parameters: // A pointer to the relevant namespace in WINMGMT. // A pointer to a copy of the object which is to be // added. This object itself cannot be used, as // it not owned locally. // The refresher to which to add the object. // Not used. // Not used here. // A pointer to the internal object which was added // to the refresher. // The Object Id (for identification during removal). // //*************************************************************************** // ok HRESULT CNt5PerfProvider::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 ) { // The object supplied by must not be copied. // Instead, we want to find out which object the caller is after // and return a pointer to *our* own private instance which is // already set up internally. This value will be sent back to the // caller so that everyone is sharing the same exact instance // in memory. // =============================================================== // Find out which object is being requested for addition. // ====================================================== wchar_t buf[128]; *buf = 0; LONG lNameLength = 0; pTemplate->ReadPropertyValue(m_hName, 128, &lNameLength, LPBYTE(buf)); // Scan out the index from the instance name. We only do this // because the instance name is a string. // =========================================================== DWORD dwIndex = 0; swscanf(buf, L"Inst_%u", &dwIndex); // Now we know which object is desired. // ==================================== IWbemObjectAccess *pOurCopy = m_aInstances[dwIndex]; // 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. // ========================================================= CNt5Refresher *pOurRefresher = (CNt5Refresher *) pRefresher; pOurRefresher->AddObject(pOurCopy, plId); // Return a copy of the internal object. // ===================================== pOurCopy->AddRef(); *ppRefreshable = pOurCopy; *plId = LONG(dwIndex); return NO_ERROR; } //*************************************************************************** // // CNt5Refresher::StopRefreshing // // Called whenever a user wants to remove an object from a refresher. // // Parameters: // The refresher object from which we are to // remove the perf object. // The ID of the object. // Not used. // //*************************************************************************** // ok HRESULT CNt5PerfProvider::StopRefreshing( /* [in] */ IWbemRefresher __RPC_FAR *pRefresher, /* [in] */ long lId, /* [in] */ long lFlags ) { // 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. // ========================================================= CNt5Refresher *pOurRefresher = (CNt5Refresher *) pRefresher; pOurRefresher->RemoveObject(lId); return NO_ERROR; } //*************************************************************************** // // CNt5Refresher::CreateRefreshableEnum // // Called whenever a user wants to create an enumeration in a refresher. // // Parameters: // The namespace this is for // Name of the class we are enumerating // The refresher object from which we are to // remove the perf object. // Not used. // Wbem Context object // Enumerator object into which refresher should place // its results // The enum id (for identification during removal) // //*************************************************************************** // ok HRESULT CNt5PerfProvider::CreateRefreshableEnum( /* [in] */ IWbemServices* pNamespace, /* [in, string] */ LPCWSTR wszClass, /* [in] */ IWbemRefresher* pRefresher, /* [in] */ long lFlags, /* [in] */ IWbemContext* pContext, /* [in] */ IWbemHiPerfEnum* pHiPerfEnum, /* [out] */ long* plId ) { // Just a placeholder for now return E_NOTIMPL; } //*************************************************************************** // // CNt5Refresher::CreateRefreshableEnum // // Called whenever a user wants to create an enumeration in a refresher. // // Parameters: // The namespace this is for // Number of objects in the array // Objects to retrieve (keys are set) // Not used. // Wbem Context object // Enumerator object into which refresher should place // its results // The enum id (for identification during removal) // //*************************************************************************** // ok HRESULT CNt5PerfProvider::GetObjects( /* [in] */ IWbemServices* pNamespace, /* [in] */ long lNumObjects, /* [in,size_is(lNumObjects)] */ IWbemObjectAccess** apObj, /* [in] */ long lFlags, /* [in] */ IWbemContext* pContext) { // Just a placeholder for now return E_NOTIMPL; }