/*++ Copyright (c) Microsoft Corporation. All rights reserved. Module Name: perfhelp.cpp Abstract: Registry-based performance counter reading helper --*/ #include "wpheader.h" #include BOOL PerfHelper::IsMatchingInstance ( PERF_INSTANCE_DEFINITION *pInstanceDef, DWORD dwCodePage, LPWSTR szInstanceNameToMatch, DWORD dwInstanceNameLength ) // compares pInstanceName to the name in the instance { DWORD dwThisInstanceNameLength; LPWSTR szThisInstanceName; size_t cchBufferSize = PDH_MAX_INSTANCE_NAME; WCHAR szBufferForANSINames[PDH_MAX_INSTANCE_NAME]; BOOL bReturn = FALSE; if (dwInstanceNameLength == 0) { // get the length to compare dwInstanceNameLength = lstrlenW (szInstanceNameToMatch); } if (dwCodePage == 0) { // try to take a shortcut here if it's a unicode string // compare to the length of the shortest string // get the pointer to this string szThisInstanceName = GetInstanceName(pInstanceDef); // convert instance Name from bytes to chars dwThisInstanceNameLength = pInstanceDef->NameLength / sizeof(WCHAR); // see if this length includes the term. null. If so shorten it if (szThisInstanceName[dwThisInstanceNameLength-1] == 0) { dwThisInstanceNameLength--; } } else { // go the long way and read/translate/convert the string dwThisInstanceNameLength =GetInstanceNameStr (pInstanceDef, szBufferForANSINames, cchBufferSize, dwCodePage); if (dwThisInstanceNameLength > 0) { szThisInstanceName = &szBufferForANSINames[0]; } else { szThisInstanceName = (LPWSTR)cszSpace; } } // if the lengths are not equal then the names can't be either if (dwInstanceNameLength == dwThisInstanceNameLength) { if (lstrcmpiW(szInstanceNameToMatch, szThisInstanceName) == 0) { // this is a match bReturn = TRUE; } else { // this is not a match } } return bReturn; } BOOL PerfHelper::ParseInstanceName ( IN LPCWSTR szInstanceString, IN LPWSTR szInstanceName, IN size_t cchInstanceName, IN LPWSTR szParentName, IN size_t cchParentName, IN LPDWORD lpIndex ) /* parses the instance name formatted as follows [parent/]instance[#index] parent is optional and if present, is delimited by a forward slash index is optional and if present, is delimited by a colon parent and instance may be any legal file name character except a delimeter character "/#\()" Index must be a string composed of decimal digit characters (0-9), less than 10 characters in length, and equate to a value between 0 and 2**32-1 (inclusive). This function assumes that the instance name and parent name buffers are of sufficient size. NOTE: szInstanceName and szInstanceString can be the same buffer */ { LPWSTR szSrcChar, szDestChar; BOOL bReturn = FALSE; WCHAR szIndexBuffer[WBEMPERF_STRING_SIZE]; // just to be safe DWORD dwIndex = 0; size_t cchSize = 0; szDestChar = (LPWSTR)szInstanceName; szSrcChar = (LPWSTR)szInstanceString; __try { do { *szDestChar++ = *szSrcChar++; cchSize++; } while ((*szSrcChar != 0) && (*szSrcChar != wcSlash) && (*szSrcChar != wcPoundSign) && cchSize < cchInstanceName ); // see if that was really the parent or not if (*szSrcChar == wcSlash) { // terminate destination after test in case they are the same buffer *szDestChar = 0; szSrcChar++; // and move source pointer past delimter // it was the parent name so copy it to the parent StringCchCopyW (szParentName, cchParentName, szInstanceName); // and copy the rest of the string after the "/" to the // instance name field cchSize = 0; szDestChar = szInstanceName; do { *szDestChar++ = *szSrcChar++; cchSize++; } while ((*szSrcChar != 0) && (*szSrcChar != wcPoundSign) && cchSize < cchInstanceName ); } else { // that was the only element so load an empty string for the parent *szParentName = 0; } // *szSrcChar will either be pointing to the end of the input string // in which case the "0" index is assumed or it will be pointing // to the # delimiting the index argument in the string. if (*szSrcChar == wcPoundSign) { *szDestChar = 0; // terminate the destination string szSrcChar++; // move past delimter szDestChar = &szIndexBuffer[0]; StringCchCopyW( szDestChar, WBEMPERF_STRING_SIZE, szSrcChar ); dwIndex = wcstoul (szIndexBuffer, NULL, 10); } else { *szDestChar = 0; // terminate the destination string dwIndex = 0; } *lpIndex = dwIndex; bReturn = TRUE; } __except (EXCEPTION_EXECUTE_HANDLER) { // unable to move strings bReturn = FALSE; } return bReturn; } #pragma warning ( disable : 4127) // while (TRUE) error PERF_OBJECT_TYPE * PerfHelper::GetObjectDefByTitleIndex( IN PERF_DATA_BLOCK *pDataBlock, IN DWORD ObjectTypeTitleIndex ) { DWORD NumTypeDef; PERF_OBJECT_TYPE *pObjectDef = NULL; PERF_OBJECT_TYPE *pReturnObject = NULL; PERF_OBJECT_TYPE *pEndOfBuffer = NULL; __try { pObjectDef = FirstObject(pDataBlock); pEndOfBuffer = (PPERF_OBJECT_TYPE) ((DWORD_PTR)pDataBlock + pDataBlock->TotalByteLength); if (pObjectDef != NULL) { NumTypeDef = 0; while (1) { if ( pObjectDef->ObjectNameTitleIndex == ObjectTypeTitleIndex ) { pReturnObject = pObjectDef; break; } else { NumTypeDef++; if (NumTypeDef < pDataBlock->NumObjectTypes) { pObjectDef = NextObject(pObjectDef); //make sure next object is legit if (pObjectDef >= pEndOfBuffer) { // looks like we ran off the end of the data buffer assert (pObjectDef < pEndOfBuffer); break; } else { if (pObjectDef != NULL) { if (pObjectDef->TotalByteLength == 0) { // 0-length object buffer returned assert (pObjectDef->TotalByteLength > 0); break; } } else { // and continue assert (pObjectDef != NULL); break; } } } else { // no more data objects in this data block break; } } } } // else no object found } __except (EXCEPTION_EXECUTE_HANDLER) { pReturnObject = NULL; } return pReturnObject; } #pragma warning ( default : 4127) // while (TRUE) error #pragma warning ( disable : 4127) // while (TRUE) error PERF_OBJECT_TYPE * PerfHelper::GetObjectDefByName ( IN PERF_DATA_BLOCK *pDataBlock, IN DWORD dwLastNameIndex, IN LPCWSTR *NameArray, IN LPCWSTR szObjectName ) { DWORD NumTypeDef; PERF_OBJECT_TYPE *pReturnObject = NULL; PERF_OBJECT_TYPE *pObjectDef = NULL; PERF_OBJECT_TYPE *pEndOfBuffer = NULL; __try { pObjectDef = FirstObject(pDataBlock); pEndOfBuffer = (PPERF_OBJECT_TYPE) ((DWORD_PTR)pDataBlock + pDataBlock->TotalByteLength); if (pObjectDef != NULL) { NumTypeDef = 0; while (1) { if ( pObjectDef->ObjectNameTitleIndex < dwLastNameIndex ) { // look up name of object & compare if (lstrcmpiW(NameArray[pObjectDef->ObjectNameTitleIndex], szObjectName) == 0) { pReturnObject = pObjectDef; break; } } NumTypeDef++; if (NumTypeDef < pDataBlock->NumObjectTypes) { pObjectDef = NextObject(pObjectDef); // get next //make sure next object is legit if (pObjectDef != NULL) { if (pObjectDef->TotalByteLength > 0) { if (pObjectDef >= pEndOfBuffer) { // looks like we ran off the end of the data buffer assert (pObjectDef < pEndOfBuffer); break; } } else { // 0-length object buffer returned assert (pObjectDef->TotalByteLength > 0); break; } } else { // null pointer assert (pObjectDef != NULL); break; } } else { // end of data block break; } } } // else no object found } __except (EXCEPTION_EXECUTE_HANDLER) { pReturnObject = NULL; } return pReturnObject; } #pragma warning ( default : 4127) // while (TRUE) error PERF_INSTANCE_DEFINITION * PerfHelper::GetInstance( IN PERF_OBJECT_TYPE *pObjectDef, IN LONG InstanceNumber ) { PERF_INSTANCE_DEFINITION *pInstanceDef; PERF_INSTANCE_DEFINITION *pReturnDef = NULL; PERF_INSTANCE_DEFINITION *pEndOfBuffer = NULL; LONG NumInstance; if (pObjectDef != NULL) { pInstanceDef = FirstInstance(pObjectDef); if (pInstanceDef != NULL) { pEndOfBuffer = (PERF_INSTANCE_DEFINITION *)EndOfObject(pObjectDef); for ( NumInstance = 0; NumInstance < pObjectDef->NumInstances; NumInstance++ ) { if ( InstanceNumber == NumInstance ) { pReturnDef = pInstanceDef; } pInstanceDef = NextInstance(pInstanceDef); // go to next instance in object and check for buffer overrun if (pInstanceDef >= pEndOfBuffer) { // something doesn't add up so bail out and return NULL break; } } } } return pReturnDef; } PERF_INSTANCE_DEFINITION * PerfHelper::GetInstanceByUniqueId( IN PERF_OBJECT_TYPE *pObjectDef, IN LONG InstanceUniqueId ) { PERF_INSTANCE_DEFINITION *pInstanceDef; PERF_INSTANCE_DEFINITION *pReturnDef = NULL; PERF_INSTANCE_DEFINITION *pEndOfBuffer = NULL; LONG NumInstance; if (pObjectDef != NULL) { pInstanceDef = FirstInstance(pObjectDef); if (pInstanceDef != NULL) { pEndOfBuffer = (PERF_INSTANCE_DEFINITION *)EndOfObject(pObjectDef); for ( NumInstance = 0; NumInstance < pObjectDef->NumInstances; NumInstance++ ) { if ( InstanceUniqueId == pInstanceDef->UniqueID ) { pReturnDef = pInstanceDef; } pInstanceDef = NextInstance(pInstanceDef); // go to next instance in object and check for buffer overrun if (pInstanceDef >= pEndOfBuffer) { // something doesn't add up so bail out and return NULL break; } } } } return pReturnDef; } DWORD PerfHelper::GetAnsiInstanceName (PPERF_INSTANCE_DEFINITION pInstance, LPWSTR lpszInstance, size_t cchBufferSize, DWORD dwCodePage) { LPSTR szSource; DWORD_PTR dwLength; UNREFERENCED_PARAMETER(dwCodePage); szSource = (LPSTR)GetInstanceName(pInstance); // the locale should be set here // pInstance->NameLength == the number of bytes (chars) in the string dwLength = mbstowcs (lpszInstance, szSource, cchBufferSize ); if( dwLength < cchBufferSize ){ lpszInstance[dwLength] = 0; // null terminate string buffer } return (DWORD)dwLength; } DWORD PerfHelper::GetUnicodeInstanceName (PPERF_INSTANCE_DEFINITION pInstance, LPWSTR lpszInstance, size_t cchBufferSize ) { LPWSTR wszSource; DWORD dwLength; wszSource = GetInstanceName(pInstance) ; // pInstance->NameLength == length of string in BYTES so adjust to // number of wide characters here dwLength = pInstance->NameLength / sizeof(WCHAR); StringCchCopyW (lpszInstance, cchBufferSize, (LPWSTR)wszSource); // add null termination if string length does not include the null if ((dwLength > 0) && (lpszInstance[dwLength-1] != 0)) { // i.e. it's the last character of the string lpszInstance[dwLength] = 0; // then add a terminating null char to the string } else { // assume that the length value includes the terminating NULL // so adjust value to indicate chars only dwLength--; } return (dwLength); // just incase there's null's in the string } DWORD PerfHelper::GetInstanceNameStr (PPERF_INSTANCE_DEFINITION pInstance, LPWSTR lpszInstance, size_t cchBufferSize, DWORD dwCodePage) { DWORD dwCharSize; DWORD dwLength = 0; if (pInstance != NULL) { if (lpszInstance != NULL) { if (dwCodePage > 0) { dwCharSize = sizeof(CHAR); dwLength = GetAnsiInstanceName (pInstance, lpszInstance, cchBufferSize, dwCodePage); } else { // it's a UNICODE name dwCharSize = sizeof(WCHAR); dwLength = GetUnicodeInstanceName (pInstance, lpszInstance, cchBufferSize); } // sanity check here... // the returned string length (in characters) plus the terminating NULL // should be the same as the specified length in bytes divided by the // character size. If not then the codepage and instance data type // don't line up so test that here if ((dwLength + 1) != (pInstance->NameLength / dwCharSize)) { // something isn't quite right so try the "other" type of string type if (dwCharSize == sizeof(CHAR)) { // then we tried to read it as an ASCII string and that didn't work // so try it as a UNICODE (if that doesn't work give up and return // it any way. dwLength = GetUnicodeInstanceName (pInstance, lpszInstance, cchBufferSize ); } else if (dwCharSize == sizeof(WCHAR)) { // then we tried to read it as a UNICODE string and that didn't work // so try it as an ASCII string (if that doesn't work give up and return // it any way. dwLength = GetAnsiInstanceName (pInstance, lpszInstance, cchBufferSize, dwCodePage); } } } // else return buffer is null } else { // no instance def object is specified so return an empty string *lpszInstance = 0; } return dwLength; } PERF_INSTANCE_DEFINITION * PerfHelper::GetInstanceByNameUsingParentTitleIndex( PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectDef, LPWSTR pInstanceName, LPWSTR pParentName, DWORD dwIndex ) { PERF_OBJECT_TYPE *pParentObj; PERF_INSTANCE_DEFINITION *pParentInst; PERF_INSTANCE_DEFINITION *pInstanceDef; PERF_INSTANCE_DEFINITION *pReturnDef = NULL; LONG NumInstance; DWORD dwLocalIndex; DWORD dwInstanceNameLength; pInstanceDef = FirstInstance(pObjectDef); assert (pInstanceDef != NULL); dwLocalIndex = dwIndex; dwInstanceNameLength = lstrlenW(pInstanceName); for ( NumInstance = 0; NumInstance < pObjectDef->NumInstances; NumInstance++ ) { if (IsMatchingInstance (pInstanceDef, pObjectDef->CodePage, pInstanceName, dwInstanceNameLength )) { // this is the correct instance, so see if we need to find a parent instance if ( pParentName == NULL ) { // No parent, we're done if this is the right "copy" if (dwLocalIndex == 0) { pReturnDef = pInstanceDef; break; } else { --dwLocalIndex; } } else { // Must match parent as well pParentObj = GetObjectDefByTitleIndex( pDataBlock, pInstanceDef->ParentObjectTitleIndex); if (!pParentObj) { // can't locate the parent, forget it break; } // Object type of parent found; now find parent // instance pParentInst = GetInstance(pParentObj, pInstanceDef->ParentObjectInstance); if (!pParentInst) { // can't locate the parent instance, forget it break ; } if (IsMatchingInstance (pParentInst, pParentObj->CodePage, pParentName, 0)) { // Parent Instance Name matches that passed in if (dwLocalIndex == 0) { pReturnDef = pInstanceDef; break; } else { --dwLocalIndex; } } } } // get the next one pInstanceDef = NextInstance(pInstanceDef); } return pReturnDef; } PERF_INSTANCE_DEFINITION * PerfHelper::GetInstanceByName( PERF_DATA_BLOCK *pDataBlock, PERF_OBJECT_TYPE *pObjectDef, LPWSTR pInstanceName, LPWSTR pParentName, DWORD dwIndex ) { PERF_OBJECT_TYPE *pParentObj; PERF_INSTANCE_DEFINITION *pParentInst; PERF_INSTANCE_DEFINITION *pInstanceDef; PERF_INSTANCE_DEFINITION *pReturnDef = NULL; PERF_INSTANCE_DEFINITION *pEndOfBuffer = NULL; LONG NumInstance; DWORD dwLocalIndex; DWORD dwInstanceNameLength; pInstanceDef = FirstInstance(pObjectDef); if (pInstanceDef != NULL) { dwLocalIndex = dwIndex; dwInstanceNameLength = lstrlenW(pInstanceName); pEndOfBuffer = (PERF_INSTANCE_DEFINITION *)EndOfObject(pObjectDef); for ( NumInstance = 0; NumInstance < pObjectDef->NumInstances; NumInstance++ ) { if (IsMatchingInstance (pInstanceDef, pObjectDef->CodePage, pInstanceName, dwInstanceNameLength)) { // Instance name matches if ( !pInstanceDef->ParentObjectTitleIndex ) { // No parent, we're done if (dwLocalIndex == 0) { pReturnDef = pInstanceDef; break; } else { --dwLocalIndex; } } else { // Must match parent as well pParentObj = GetObjectDefByTitleIndex( pDataBlock, pInstanceDef->ParentObjectTitleIndex); if (pParentObj != NULL) { // Object type of parent found; now find parent // instance pParentInst = GetInstance(pParentObj, pInstanceDef->ParentObjectInstance); if (pParentInst != NULL) { if (IsMatchingInstance (pParentInst, pParentObj->CodePage, pParentName, 0)) { // Parent Instance Name matches that passed in if (dwLocalIndex == 0) { pReturnDef = pInstanceDef; break; } else { --dwLocalIndex; } } } } else { // keep trying } } } // go to next instance in object and check for buffer overrun pInstanceDef = NextInstance(pInstanceDef); if (pInstanceDef >= pEndOfBuffer) { // something doesn't add up so bail out and return NULL break; } } } return pReturnDef; } // GetInstanceByName DWORD PerfHelper::GetFullInstanceNameStr ( PERF_DATA_BLOCK *pPerfData, PERF_OBJECT_TYPE *pObjectDef, PERF_INSTANCE_DEFINITION *pInstanceDef, LPWSTR szInstanceName, size_t cchBufferSize ) // compile instance name. // the instance name can either be just // the instance name itself or it can be // the concatenation of the parent instance, // a delimiting char (backslash) followed by // the instance name { WCHAR szInstanceNameString[PDH_MAX_INSTANCE_NAME]; WCHAR szParentNameString[PDH_MAX_INSTANCE_NAME]; DWORD dwLength = 0; PERF_OBJECT_TYPE *pParentObjectDef; PERF_INSTANCE_DEFINITION *pParentInstanceDef; szInstanceNameString[0] = UNICODE_NULL; szParentNameString[0] = UNICODE_NULL; if (pInstanceDef->UniqueID == PERF_NO_UNIQUE_ID) { dwLength = GetInstanceNameStr (pInstanceDef, szInstanceNameString, PDH_MAX_INSTANCE_NAME, pObjectDef->CodePage); } else { // make a string out of the unique ID _ltow (pInstanceDef->UniqueID, szInstanceNameString, 10); dwLength = lstrlenW (szInstanceNameString); } if (pInstanceDef->ParentObjectTitleIndex > 0) { // then add in parent instance name pParentObjectDef = GetObjectDefByTitleIndex ( pPerfData, pInstanceDef->ParentObjectTitleIndex); if (pParentObjectDef != NULL) { pParentInstanceDef = GetInstance ( pParentObjectDef, pInstanceDef->ParentObjectInstance); assert ((UINT_PTR)pParentObjectDef != (DWORD)0xFFFFFFFF); if (pParentInstanceDef != NULL) { if (pParentInstanceDef->UniqueID == PERF_NO_UNIQUE_ID) { dwLength += GetInstanceNameStr (pParentInstanceDef, szParentNameString, PDH_MAX_INSTANCE_NAME, pParentObjectDef->CodePage); } else { // make a string out of the unique ID _ltow (pParentInstanceDef->UniqueID, szParentNameString, 10); dwLength += lstrlenW (szParentNameString); } StringCchCatW( szParentNameString, PDH_MAX_INSTANCE_NAME, cszSlash ); dwLength += 1; StringCchCatW(szParentNameString, PDH_MAX_INSTANCE_NAME, szInstanceNameString); StringCchCopyW( szInstanceName, cchBufferSize, szParentNameString); } else { StringCchCopyW( szInstanceName, cchBufferSize, szInstanceNameString); } } else { StringCchCopyW( szInstanceName, cchBufferSize, szInstanceNameString); } } else { StringCchCopyW( szInstanceName, cchBufferSize, szInstanceNameString); } return dwLength; } //*************************************************************************** // // PerfHelper::GetInstances // // This is called to retrieve all instances of a given class. // // Parameters: // The perf blob retrieved from HKEY_PERFORMANCE_DATA. // A map object of the class required. // The sink to which to deliver the objects. // //*************************************************************************** // void PerfHelper::GetInstances( LPBYTE pBuf, CClassMapInfo *pClassMap, IWbemObjectSink *pSink ) { PPERF_OBJECT_TYPE PerfObj = 0; PPERF_OBJECT_TYPE pEndOfBuffer = 0; PPERF_INSTANCE_DEFINITION PerfInst = 0; PPERF_INSTANCE_DEFINITION pEndOfObject = 0; PPERF_COUNTER_DEFINITION PerfCntr = 0, CurCntr = 0; PPERF_COUNTER_BLOCK PtrToCntr = 0; PPERF_DATA_BLOCK PerfData = (PPERF_DATA_BLOCK) pBuf; DWORD i, j, k; IWbemObjectAccess *pNewInst = 0; IWbemClassObject *pClsObj = 0; WCHAR pName[PDH_MAX_INSTANCE_NAME]; LONG lStatus = 0; LONG hPropHandle; LPDWORD pdwVal; ULONGLONG *pullVal; HRESULT hRes; LONG64 llVal; // Get the first object type. // ========================== PerfObj = (PPERF_OBJECT_TYPE) ((PBYTE)PerfData + PerfData->HeaderLength); if (PerfObj != NULL) { // get end of buffer pEndOfBuffer = (PPERF_OBJECT_TYPE) ((DWORD_PTR)PerfData + PerfData->TotalByteLength); // Process all objects. // ==================== for (i = 0; i < PerfData->NumObjectTypes; i++ ) { // Within each PERF_OBJECT_TYPE is a series of // PERF_COUNTER_DEFINITION blocks. // ========================================== PerfCntr = (PPERF_COUNTER_DEFINITION) ((PBYTE)PerfObj + PerfObj->HeaderLength); // If the current object isn't of the class we requested, // simply skip over it. I am not sure if this can really // happen or not in practice. // ====================================================== if (PerfObj->ObjectNameTitleIndex != pClassMap->m_dwObjectId) { PerfObj = (PPERF_OBJECT_TYPE)((PBYTE)PerfObj + PerfObj->TotalByteLength); if (PerfObj >= pEndOfBuffer) { // looks like we ran off the end of the data buffer break; } else { continue; } } if (PerfObj->NumInstances > 0) { // Get the first instance. // ======================= PerfInst = (PPERF_INSTANCE_DEFINITION)((PBYTE)PerfObj + PerfObj->DefinitionLength); if (PerfInst < (PPERF_INSTANCE_DEFINITION)pEndOfBuffer) { // make sure we are still within the caller's buffer // then find the end of this object pEndOfObject = (PERF_INSTANCE_DEFINITION *)EndOfObject(PerfObj); // Retrieve all instances. // ======================= for (k = 0; k < DWORD(PerfObj->NumInstances); k++ ) { CurCntr = PerfCntr; pClsObj = NULL; pNewInst = NULL; HRESULT hr; // Get the first counter. // ====================== PtrToCntr = (PPERF_COUNTER_BLOCK)((PBYTE)PerfInst + PerfInst->ByteLength); // Quickly clone a new instance to send back to the user. // Since SpawnInstance() returns an IWbemClassObject and // we really need an IWbemObjectAccess,we have to QI // after the spawn. We need to fix this, as this number // of calls is too time consuming. // ====================================================== hr = pClassMap->m_pClassDef->SpawnInstance(0, &pClsObj); if (SUCCEEDED(hr)) { hr = pClsObj->QueryInterface(IID_IWbemObjectAccess, (LPVOID *) &pNewInst); pClsObj->Release(); // We only need the IWbemObjectAccess pointer if( NULL == pNewInst ){ break; } } else { break; } // Locate the instance name. // ========================== lStatus = GetFullInstanceNameStr ( PerfData, PerfObj, PerfInst, pName, PDH_MAX_INSTANCE_NAME ); // Retrieve all counters. // ====================== for(j = 0; j < PerfObj->NumCounters; j++ ) { // Find the WBEM property handle based on the counter title index. // This function does a quick binary search of the class map object // to find the handle that goes with this counter. // ================================================================ hPropHandle = pClassMap->GetPropHandle( CM_MAKE_PerfObjectId(CurCntr->CounterNameTitleIndex, CurCntr->CounterType)); if (hPropHandle != 0) { // update value according to data type if ((CurCntr->CounterType & 0x300) == 0) { pdwVal = LPDWORD((LPVOID)((PBYTE)PtrToCntr + CurCntr->CounterOffset)); hRes = pNewInst->WriteDWORD(hPropHandle, *pdwVal); } else if ((CurCntr->CounterType & 0x300) == 0x100){ pullVal = (ULONGLONG *)((LPVOID)((PBYTE)PtrToCntr + CurCntr->CounterOffset)); llVal = Assign64((PLARGE_INTEGER) pullVal); hRes = pNewInst->WriteQWORD(hPropHandle, llVal); } else { //this shouldn't happen assert (FALSE); } } // Get next counter. // ================= CurCntr = (PPERF_COUNTER_DEFINITION)((PBYTE)CurCntr + CurCntr->ByteLength); } // Write the instance 'name' // ========================= if (pName && pClassMap->m_dwNameHandle) { pNewInst->WritePropertyValue( pClassMap->m_dwNameHandle, (DWORD)(((DWORD)(wcslen(pName)) + 1) * 2), LPBYTE(pName) ); } // update the timestamp if (pClassMap->m_dwPerfTimeStampHandle) { UpdateTimers(pClassMap, pNewInst, PerfData, PerfObj); } // Deliver the instance to the user. // ================================= pSink->Indicate(1, (IWbemClassObject **) &pNewInst); pNewInst->Release(); // Move to the next perf instance. // ================================ PerfInst = (PPERF_INSTANCE_DEFINITION)((PBYTE)PtrToCntr + PtrToCntr->ByteLength); if (PerfInst >= pEndOfObject) { // something doesn't add up so bail out of this object break; } } } } else if (PerfObj->NumInstances == PERF_NO_INSTANCES) { HRESULT hr; pClsObj = NULL; pNewInst = NULL; // Cases where the counters have one and only one instance. // ======================================================== // Get the first counter. // ====================== PtrToCntr = (PPERF_COUNTER_BLOCK) ((PBYTE)PerfObj + PerfObj->DefinitionLength ); // Quickly clone a new instance to send back to the user. // Since SpawnInstance() returns an IWbemClassObject and // we really need an IWbemObjectAccess,we have to QI // after the spawn. We need to fix this, as this number // of calls is too time consuming. // ====================================================== hr = pClassMap->m_pClassDef->SpawnInstance(0, &pClsObj); if (SUCCEEDED(hr)) { pClsObj->QueryInterface(IID_IWbemObjectAccess, (LPVOID *) &pNewInst); pClsObj->Release(); // Retrieve all counters. // ====================== for( j=0; j < PerfObj->NumCounters; j++ ) { // Find the WBEM property handle based on the counter title index. // This function does a quick binary search of the class map object // to find the handle that goes with this counter. // ================================================================ hPropHandle = pClassMap->GetPropHandle( CM_MAKE_PerfObjectId(PerfCntr->CounterNameTitleIndex, PerfCntr->CounterType)); if (hPropHandle != 0) { if ((PerfCntr->CounterType & 0x300) == 0) { pdwVal = LPDWORD((LPVOID)((PBYTE)PtrToCntr + PerfCntr->CounterOffset)); hRes = pNewInst->WriteDWORD(hPropHandle, *pdwVal); } else if ((PerfCntr->CounterType & 0x300) == 0x100) { pullVal = (ULONGLONG *)((LPVOID)((PBYTE)PtrToCntr + PerfCntr->CounterOffset)); llVal = Assign64((PLARGE_INTEGER) pullVal); hRes = pNewInst->WriteQWORD(hPropHandle, llVal); } else { // this shouldn't happen assert (FALSE); } } PerfCntr = (PPERF_COUNTER_DEFINITION)((PBYTE)PerfCntr + PerfCntr->ByteLength); } // update the timestamp if (pClassMap->m_dwPerfTimeStampHandle) { UpdateTimers(pClassMap, pNewInst, PerfData, PerfObj); } pSink->Indicate(1, (IWbemClassObject **) &pNewInst); pNewInst->Release(); } } else { // this object can have instances, but currently doesn't // so there's nothing to report } break; } } } void PerfHelper::RefreshEnumeratorInstances ( IN RefresherCacheEl *pThisCacheEl, IN PPERF_DATA_BLOCK PerfData, IN PPERF_OBJECT_TYPE PerfObj ) { LONG lNumObjInstances; LONG lStatus; HRESULT hRes; PPERF_INSTANCE_DEFINITION PerfInst = 0; PPERF_INSTANCE_DEFINITION pEndOfObject = 0; PPERF_COUNTER_DEFINITION PerfCntr = 0, CurCntr = 0; PPERF_COUNTER_BLOCK PtrToCntr = 0; WCHAR pName[PDH_MAX_INSTANCE_NAME]; LONG hPropHandle; LPDWORD pdwVal; ULONGLONG *pullVal; LONG64 llVal; IWbemObjectAccess *pNewInst = 0; assert (PerfObj != NULL); assert (pThisCacheEl != NULL); if (pThisCacheEl == NULL) return; // make sure we have enough pointers // handle the singleton object case if (PerfObj->NumInstances == PERF_NO_INSTANCES) { lNumObjInstances = 1; } else { lNumObjInstances = PerfObj->NumInstances; } if (pThisCacheEl->m_aEnumInstances.Size() < lNumObjInstances) { LONG i; // alloc and init the ID array if (pThisCacheEl->m_plIds != NULL) { delete (pThisCacheEl->m_plIds); } pThisCacheEl->m_lEnumArraySize = lNumObjInstances; pThisCacheEl->m_plIds = new LONG[lNumObjInstances]; if (pThisCacheEl->m_plIds == NULL) return; for (i = 0; i < lNumObjInstances; i++) pThisCacheEl->m_plIds[i] = i; // add the new IWbemObjectAccess pointers for (i = pThisCacheEl->m_aEnumInstances.Size(); i < PerfObj->NumInstances; i++) { IWbemClassObject * pClsObj = NULL; HRESULT hr; hr = pThisCacheEl->m_pClassMap->m_pClassDef->SpawnInstance(0, &pClsObj); if (SUCCEEDED(hr)) { pClsObj->QueryInterface(IID_IWbemObjectAccess, (LPVOID *) &pNewInst); pClsObj->Release(); // We only need the IWbemObjectAccess pointer pThisCacheEl->m_aEnumInstances.Add (pNewInst); } } } assert (pThisCacheEl->m_aEnumInstances.Size() >= lNumObjInstances); // release enumerator items to prepare a new batch hRes = pThisCacheEl->m_pHiPerfEnum->RemoveAll(0); assert (hRes == S_OK); // update new instance list if (PerfObj->NumInstances == PERF_NO_INSTANCES) { //handle the singleton case } else if (PerfObj->NumInstances > 0) { // Get the first instance. // ======================= PerfInst = (PPERF_INSTANCE_DEFINITION)((PBYTE)PerfObj + PerfObj->DefinitionLength); // get pointer to the end of this object buffer pEndOfObject = (PERF_INSTANCE_DEFINITION *)EndOfObject(PerfObj); // point to the first counter definition in the object PerfCntr = (PPERF_COUNTER_DEFINITION) ((PBYTE)PerfObj + PerfObj->HeaderLength); // Retrieve all instances. // ======================= for (LONG k = 0; k < PerfObj->NumInstances; k++ ) { CurCntr = PerfCntr; // Get the first counter. // ====================== PtrToCntr = (PPERF_COUNTER_BLOCK)((PBYTE)PerfInst + PerfInst->ByteLength); // get the IWbemObjectAccess pointer from our // cached array of pointers pNewInst = (IWbemObjectAccess *)(pThisCacheEl->m_aEnumInstances.GetAt(k)); // Locate the instance name. // ========================== lStatus = GetFullInstanceNameStr ( PerfData, PerfObj, PerfInst, pName, PDH_MAX_INSTANCE_NAME ); // Retrieve all counters. // ====================== if( NULL != pNewInst ){ for(DWORD j = 0; j < PerfObj->NumCounters; j++ ) { // Find the WBEM property handle based on the counter title index. // This function does a quick binary search of the class map object // to find the handle that goes with this counter. // ================================================================ hPropHandle = pThisCacheEl->m_pClassMap->GetPropHandle( CM_MAKE_PerfObjectId(CurCntr->CounterNameTitleIndex, CurCntr->CounterType)); if (hPropHandle != 0) { // update value according to data type if ((CurCntr->CounterType & 0x300) == 0) { pdwVal = LPDWORD((LPVOID)((PBYTE)PtrToCntr + CurCntr->CounterOffset)); hRes = pNewInst->WriteDWORD(hPropHandle, *pdwVal); } else if ((CurCntr->CounterType & 0x300) == 0x100){ pullVal = (ULONGLONG *)((LPVOID)((PBYTE)PtrToCntr + CurCntr->CounterOffset)); llVal = Assign64((PLARGE_INTEGER) pullVal); hRes = pNewInst->WriteQWORD(hPropHandle, llVal); } else { //this shouldn't happen assert (FALSE); } } // Get next counter. // ================= CurCntr = (PPERF_COUNTER_DEFINITION)((PBYTE)CurCntr + CurCntr->ByteLength); } // Write the instance 'name' // ========================= if (pName && pThisCacheEl->m_pClassMap->m_dwNameHandle) { pNewInst->WritePropertyValue( pThisCacheEl->m_pClassMap->m_dwNameHandle, (DWORD)(((DWORD)(wcslen(pName)) + 1) * 2), LPBYTE(pName) ); } // update the timestamp if (pThisCacheEl->m_pClassMap->m_dwPerfTimeStampHandle) { UpdateTimers(pThisCacheEl->m_pClassMap, pNewInst, PerfData, PerfObj); } // Move to the next perf instance. // ================================ PerfInst = (PPERF_INSTANCE_DEFINITION)((PBYTE)PtrToCntr + PtrToCntr->ByteLength); if (PerfInst >= pEndOfObject) { // something doesn't add up so bail out of this object break; } } } } else { // no instances so there's nothing to do } if (lNumObjInstances > 0) { // update the hiperf enumerator object hRes = pThisCacheEl->m_pHiPerfEnum->AddObjects( 0, lNumObjInstances, pThisCacheEl->m_plIds, (IWbemObjectAccess __RPC_FAR *__RPC_FAR *)pThisCacheEl->m_aEnumInstances.GetArrayPtr()); } else { // nothing to do since we've already cleared the enumerator above } } //*************************************************************************** // // PerfHelper::RefreshInstances // // searches the refresher's list first then // looks up the corresponding items in the perf data structure // //*************************************************************************** // void PerfHelper::RefreshInstances( LPBYTE pBuf, CNt5Refresher *pRef ) { PPERF_OBJECT_TYPE PerfObj = 0; PPERF_INSTANCE_DEFINITION PerfInst = 0; PPERF_COUNTER_DEFINITION PerfCntr = 0, CurCntr = 0; PPERF_COUNTER_BLOCK PtrToCntr = 0; PPERF_DATA_BLOCK PerfData = (PPERF_DATA_BLOCK) pBuf; // for each refreshable object PRefresherCacheEl pThisCacheEl; DWORD dwNumCacheEntries = pRef->m_aCache.Size(); DWORD dwThisCacheEntryIndex = 0; DWORD dwThisCounter; DWORD dwThisInstanceIndex = 0; DWORD dwNumInstancesInCache = 0; IWbemObjectAccess *pInst = 0; LONG hPropHandle; LPDWORD pdwVal; HRESULT hRes; ULONGLONG *pullVal; LONG64 llVal; while (dwThisCacheEntryIndex < dwNumCacheEntries) { // get this entry from the cache pThisCacheEl = (PRefresherCacheEl) pRef->m_aCache[dwThisCacheEntryIndex]; // get class map from this entry CClassMapInfo *pClassMap = pThisCacheEl->m_pClassMap; // get perf object pointer from the perf data block PerfObj = GetObjectDefByTitleIndex ( PerfData, pThisCacheEl->m_dwPerfObjIx); if (PerfObj != NULL) { // found the object so do each of the instances // loaded in this refresher PerfCntr = (PPERF_COUNTER_DEFINITION) ((PBYTE)PerfObj + PerfObj->HeaderLength); // found so update the properties if (PerfObj->NumInstances > 0) { // see if they have an enumerator interface and refresh it if they do if (pThisCacheEl->m_pHiPerfEnum != NULL) { // refresh enum RefreshEnumeratorInstances (pThisCacheEl, PerfData, PerfObj); } //do each instance in this class dwThisInstanceIndex = 0; dwNumInstancesInCache = pThisCacheEl->m_aInstances.Size(); while (dwThisInstanceIndex < dwNumInstancesInCache ) { pInst = 0; // get the pointer to this instance in the refresher CachedInst *pInstInfo = PCachedInst(pThisCacheEl->m_aInstances[dwThisInstanceIndex]); // get the pointer to the instance block in the current object PerfInst = GetInstanceByName( PerfData, PerfObj, pInstInfo->m_szInstanceName, pInstInfo->m_szParentName, pInstInfo->m_dwIndex); pInst = pInstInfo->m_pInst; // Get the first counter. // ====================== CurCntr = PerfCntr; if (PerfInst != NULL) { PtrToCntr = (PPERF_COUNTER_BLOCK)((PBYTE)PerfInst + PerfInst->ByteLength); // Retrieve all counters for the instance if it was one of the instances // we are supposed to be refreshing. // ===================================================================== for (dwThisCounter = 0; dwThisCounter < PerfObj->NumCounters; dwThisCounter++ ) { hPropHandle = pClassMap->GetPropHandle( CM_MAKE_PerfObjectId(CurCntr->CounterNameTitleIndex, CurCntr->CounterType)); if (hPropHandle != 0) { // Data is (LPVOID)((PBYTE)PtrToCntr + CurCntr->CounterOffset); if ((CurCntr->CounterType & 0x300) == 0) { pdwVal = LPDWORD((LPVOID)((PBYTE)PtrToCntr + CurCntr->CounterOffset)); hRes = pInst->WriteDWORD(hPropHandle, *pdwVal); } else if ((CurCntr->CounterType & 0x300) == 0x100) { pullVal = (ULONGLONG *)((LPVOID)((PBYTE)PtrToCntr + CurCntr->CounterOffset)); llVal = Assign64((PLARGE_INTEGER) pullVal); hRes = pInst->WriteQWORD(hPropHandle, llVal); } else { // This shouldn't happen assert (FALSE); } } // Get next counter. // ================= CurCntr = (PPERF_COUNTER_DEFINITION)((PBYTE)CurCntr + CurCntr->ByteLength); } // update the timestamp if (pClassMap->m_dwPerfTimeStampHandle) { UpdateTimers(pClassMap, pInst, PerfData, PerfObj); } // else no timestamp handle present } else { // then there's no data for this // instance anymore so zero out the values and continue for (dwThisCounter = 0; dwThisCounter < PerfObj->NumCounters; dwThisCounter++ ) { hPropHandle = pClassMap->GetPropHandle( CM_MAKE_PerfObjectId(CurCntr->CounterNameTitleIndex, CurCntr->CounterType)); if (hPropHandle != 0) { if ((CurCntr->CounterType & 0x300) == 0) { hRes = pInst->WriteDWORD(hPropHandle, 0); } else if ((CurCntr->CounterType & 0x300) == 0x100) { hRes = pInst->WriteQWORD(hPropHandle, 0); } else { // This shouldn't happen assert (FALSE); } } // Get next counter. // ================= CurCntr = (PPERF_COUNTER_DEFINITION)((PBYTE)CurCntr + CurCntr->ByteLength); } // update the timestamp if (pClassMap->m_dwPerfTimeStampHandle) { // save system timer tick pInst->WriteQWORD(pClassMap->m_dwPerfTimeStampHandle , 0); // use system 100 NS timer pInst->WriteQWORD(pClassMap->m_dw100NsTimeStampHandle, 0); // use timer from object pInst->WriteQWORD(pClassMap->m_dwObjectTimeStampHandle, 0); } } // Get the next instance. // ===================== dwThisInstanceIndex++; } } else if (PerfObj->NumInstances == PERF_NO_INSTANCES && NULL != pThisCacheEl->m_pSingleton ) { // Check that the singleton instance did not get cleared // due to no references. // only a single instance so get the properties and // update them // Get the first counter. // Find the singleton WBEM instance which correponds to the singleton perf instance // along with its class def so that we have the property handles. // // Note that since the perf object index translates to a WBEM class and there // can only be one instance, all that is required to find the instance in the // refresher is the perf object title index. // ================================================================================= pInst = pThisCacheEl->m_pSingleton; // ====================== PtrToCntr = (PPERF_COUNTER_BLOCK) ((PBYTE)PerfObj + PerfObj->DefinitionLength ); // Retrieve all counters if the instance is one we are supposed to be refreshing. // ============================================================================== for( dwThisCounter=0; dwThisCounter < PerfObj->NumCounters; dwThisCounter++ ) { // Get the property handle for the counter. // ======================================== hPropHandle = pClassMap->GetPropHandle( CM_MAKE_PerfObjectId(PerfCntr->CounterNameTitleIndex, PerfCntr->CounterType)); if (hPropHandle != 0) { // update the data values based on the datatype if ((PerfCntr->CounterType & 0x300) == 0) { pdwVal = LPDWORD((LPVOID)((PBYTE)PtrToCntr + PerfCntr->CounterOffset)); hRes = pInst->WriteDWORD(hPropHandle, *pdwVal); } else if ((PerfCntr->CounterType & 0x300) == 0x100){ pullVal = (ULONGLONG *)((LPVOID)((PBYTE)PtrToCntr + PerfCntr->CounterOffset)); llVal = Assign64((PLARGE_INTEGER) pullVal); hRes = pInst->WriteQWORD(hPropHandle, llVal); } else { // this shouldn't happen assert (FALSE); } } // get next counter definition PerfCntr = (PPERF_COUNTER_DEFINITION)((PBYTE)PerfCntr + PerfCntr->ByteLength); } // update the timestamp if (pClassMap->m_dwPerfTimeStampHandle) { UpdateTimers(pClassMap, pInst, PerfData, PerfObj); } } else { // this object could have instances but doesn't so // skip } } else { // desired object not found in data } // Get the next refresher object // ========================= dwThisCacheEntryIndex++; } } //*************************************************************************** // // QueryInstances // // Used to send back all instances of a perf counter. The counter // is specified by the object, which is tightly bound to // a particular counter. // //*************************************************************************** // BOOL PerfHelper::QueryInstances( CPerfObjectAccess *pPerfObj, CClassMapInfo *pClassMap, IWbemObjectSink *pSink ) { DWORD dwBufSize = 0; LPBYTE pBuf = NULL; LONG lStatus; BOOL bReturn = FALSE; WCHAR szValueNum[WBEMPERF_STRING_SIZE]; for (;;) { dwBufSize += 0x10000; // 64K assert (dwBufSize< 0x100000); // make sure we don't do this forever pBuf = new BYTE[dwBufSize]; assert (pBuf != NULL); if (pBuf != NULL) { // either do a global or a costly query depending on the // object being queried if (pClassMap->GetObjectId() > 0) { _ultow (pClassMap->GetObjectId(), (LPWSTR)szValueNum, 10); } else if (pClassMap->IsCostly()) { StringCchCopyW( szValueNum, WBEMPERF_STRING_SIZE, cszCostly); } else { StringCchCopyW(szValueNum, WBEMPERF_STRING_SIZE, cszGlobal); } lStatus = pPerfObj->CollectData (pBuf, &dwBufSize, szValueNum); if (lStatus == ERROR_MORE_DATA) { // toss the old buffer as it's not useful delete pBuf; continue; } else if (lStatus == ERROR_SUCCESS) { bReturn = TRUE; } break; } else { // memory allocation failure break; } } if (bReturn && (pBuf != NULL)) { // a good buffer was returned so // Decode the instances and send them back. // ======================================== GetInstances(pBuf, pClassMap, pSink); } // Cleanup. // ======== if (pBuf != NULL) delete pBuf; return bReturn; } //*************************************************************************** // // RefreshInstances // // Used to refresh a set of instances. // //*************************************************************************** // BOOL PerfHelper::RefreshInstances( CNt5Refresher *pRef ) { DWORD dwBufSize = 0; LPBYTE pBuf = NULL; LONG lStatus; BOOL bReturn = FALSE; for (;;) { dwBufSize += 0x10000; // 64K assert (dwBufSize< 0x100000); // make sure we don't do this forever pBuf = new BYTE[dwBufSize]; assert (pBuf != NULL); if (pBuf != NULL) { lStatus = pRef->m_PerfObj.CollectData (pBuf, &dwBufSize); if (lStatus == ERROR_MORE_DATA) { // toss the old buffer as it's not useful delete pBuf; continue; } else if (lStatus == ERROR_SUCCESS) { bReturn = TRUE; } break; } else { // memory allocation failure break; } } if (bReturn && (pBuf != NULL)) { // update the instances and send them back. // ======================================== RefreshInstances(pBuf, pRef); } // Cleanup. // ======== if (pBuf != NULL) delete pBuf; return bReturn; } VOID PerfHelper::UpdateTimers( CClassMapInfo *pClassMap, IWbemObjectAccess *pInst, PPERF_DATA_BLOCK PerfData, PPERF_OBJECT_TYPE PerfObj ) { LONG64 llVal; // save system timer tick llVal = Assign64(&PerfData->PerfTime); pInst->WriteQWORD( pClassMap->m_dwPerfTimeStampHandle , llVal ); // use timer from object llVal = Assign64(&PerfObj->PerfTime); pInst->WriteQWORD( pClassMap->m_dwObjectTimeStampHandle, llVal ); // use system 100 NS timer llVal = Assign64(&PerfData->PerfTime100nSec); pInst->WriteQWORD( pClassMap->m_dw100NsTimeStampHandle, llVal ); // save system timer freq llVal = Assign64(&PerfData->PerfFreq); pInst->WriteQWORD( pClassMap->m_dwPerfFrequencyHandle , llVal ); // use timer from object llVal = Assign64(&PerfObj->PerfFreq); pInst->WriteQWORD( pClassMap->m_dwObjectFrequencyHandle, llVal ); // use system 100 NS Freq pInst->WriteQWORD( pClassMap->m_dw100NsFrequencyHandle, (LONGLONG)10000000); }