/*++ Copyright (C) 1996-1999 Microsoft Corporation Module Name: grphitem.cpp Abstract: --*/ #ifndef _LOG_INCLUDE_DATA #define _LOG_INCLUDE_DATA 0 #endif #include "polyline.h" #include #include #include // for INT_MAX #include #include "visuals.h" #include "grphitem.h" #include "unihelpr.h" #include "utils.h" #include "pdhmsg.h" #define MAX_DOUBLE_TEXT_SIZE (64) // From Pdh calc functions #define PERF_DOUBLE_RAW (PERF_SIZE_DWORD | 0x00002000 | PERF_TYPE_NUMBER | PERF_NUMBER_DECIMAL) // Construction/Destruction CGraphItem::CGraphItem ( CSysmonControl *pCtrl ) : m_cRef ( 0 ), m_pCtrl ( pCtrl ), m_hCounter ( NULL ), m_hPen ( NULL ), m_hBrush ( NULL ), m_pCounter ( NULL ), m_pInstance ( NULL), m_pRawCtr ( NULL ), m_pFmtCtr ( NULL ), m_dFmtMax ( 0 ), m_dFmtMin ( 0 ), m_dFmtAvg ( 0 ), m_lFmtStatus ( 0 ), m_dLogMin ( 0 ), m_dLogMax ( 0 ), m_dLogAvg ( 0 ), m_lLogStatsStatus ( PDH_CSTATUS_INVALID_DATA ), m_pLogData ( NULL ), m_pImpIDispatch ( NULL ), m_rgbColor ( RGB(0,0,0) ), m_iWidth ( 1 ), m_iStyle ( 0 ), m_iScaleFactor ( INT_MAX ), m_dScale ( (double)1.0 ), m_pNextItem ( NULL ), m_bUpdateLog ( TRUE ), m_fGenerated ( FALSE ) /*++ Routine Description: Constructor for the CGraphItem class. It initializes the member variables. Arguments: None. Return Value: None. --*/ { ZeroMemory ( &m_CounterInfo, sizeof (m_CounterInfo ) ); m_CounterInfo.CStatus = PDH_CSTATUS_INVALID_DATA; } CGraphItem::~CGraphItem ( VOID ) /*++ Routine Description: Destructor for the CGraphItem class. It frees any objects, storage, and interfaces that were created. If the item is part of a query it is removed from the query. Arguments: None. Return Value: None. --*/ { if (m_hCounter != NULL) RemoveFromQuery(); if (m_hPen != NULL) DeleteObject(m_hPen); if (m_hBrush != NULL) DeleteObject(m_hBrush); if (m_pImpIDispatch != NULL) delete m_pImpIDispatch; } HRESULT CGraphItem::SaveToStream ( IN LPSTREAM pIStream, IN BOOL fWildCard, IN INT iVersMaj, IN INT // iVersMin ) /*++ Routine Description: This function writes the properties of the graph item into the provided stream. Arguments: pIStream - Pointer to stream interface fWildCard - iVersMaj - Major version Return Value: HRESULT - S_OK or stream error --*/ { LPWSTR szPath = NULL; LPWSTR szBuf = NULL; DWORD cchBufLen; LPWSTR pszTranslatedPath; HRESULT hr = S_OK; PDH_STATUS pdhStatus; // // Get the full path of the counter. (machine\object\instance\counter format) // szPath = FormPath( fWildCard ); if (szPath == NULL) { return E_OUTOFMEMORY; } pszTranslatedPath = szPath; // // Preallocate a buffer for locale path // cchBufLen = PDH_MAX_COUNTER_PATH + 1; szBuf = new WCHAR [ cchBufLen ]; if (szBuf != NULL) { // // Translate counter name from Localization into English // pdhStatus = PdhTranslate009Counter( szPath, szBuf, &cchBufLen); if (pdhStatus == PDH_MORE_DATA) { delete [] szBuf; szBuf = new WCHAR [ cchBufLen ]; if (szBuf != NULL) { pdhStatus = PdhTranslate009Counter( szPath, szBuf, &cchBufLen); } } if (pdhStatus == ERROR_SUCCESS) { pszTranslatedPath = szBuf; } } if ( SMONCTRL_MAJ_VERSION == iVersMaj ) { GRAPHITEM_DATA3 ItemData; // Move properties to storage structure ItemData.m_rgbColor = m_rgbColor; ItemData.m_iWidth = m_iWidth; ItemData.m_iStyle = m_iStyle; ItemData.m_iScaleFactor = m_iScaleFactor; assert( 0 < lstrlen(pszTranslatedPath ) ); ItemData.m_nPathLength = lstrlen(pszTranslatedPath); // Write structure to stream hr = pIStream->Write(&ItemData, sizeof(ItemData), NULL); if (FAILED(hr)) { goto ErrorOut; } // Write path name to stream hr = pIStream->Write(pszTranslatedPath, ItemData.m_nPathLength*sizeof(WCHAR), NULL); if (FAILED(hr)) { goto ErrorOut; } } ErrorOut: if (szBuf != NULL) { delete [] szBuf; } if (szPath != NULL) { delete [] szPath; } return hr; } HRESULT CGraphItem::NullItemToStream ( IN LPSTREAM pIStream, IN INT,// iVersMaj, IN INT // iVersMin ) /*++ Routine Description: NulItemToStream writes a graph item structiure with a null path name to the stream. This is used to marked the end of the counter data in the control's saved state. Arguments: pIStream - Pointer to stream interface Return Value: HRESULT - S_OK or stream error --*/ { GRAPHITEM_DATA3 ItemData; // Zero path length, other fields needn't be initialized ItemData.m_nPathLength = 0; // Write structure to stream return pIStream->Write(&ItemData, sizeof(ItemData), NULL); } HRESULT CGraphItem::SaveToPropertyBag ( IN IPropertyBag* pIPropBag, IN INT iIndex, IN BOOL bUserMode, IN INT, // iVersMaj, IN INT // iVersMin ) /*++ Routine Description: SaveToPropertyBag writes the graph item's properties to the provided property bag interface. The history data is saved as part of the properties. Arguments: pIPropBag - Pointer to property bag interface fWildCard iVersMaj iVersMin Return Value: HRESULT - S_OK or property bag error --*/ { HRESULT hr = S_OK; LPWSTR szPath = NULL; PHIST_CONTROL pHistCtrl; VARIANT vValue; WCHAR szCounterName[16]; WCHAR szPropertyName[16+16]; DWORD dwCounterNameLength; DWORD dwRemainingLen; LPWSTR pszNext = NULL; LPWSTR szBuf = NULL; DWORD cchBufLen; LPWSTR pszTranslatedPath; PDH_STATUS pdhStatus; // // Get the full path of the counter. (machine\object\instance\counter format) // szPath = FormPath( FALSE ); if (szPath == NULL) { return E_OUTOFMEMORY; } pszTranslatedPath = szPath; // // Preallocate a buffer for locale path // cchBufLen = PDH_MAX_COUNTER_PATH + 1; szBuf = new WCHAR [ cchBufLen ]; if (szBuf != NULL) { // // Translate counter name from Localization into English // pdhStatus = PdhTranslate009Counter( szPath, szBuf, &cchBufLen); if (pdhStatus == PDH_MORE_DATA) { delete [] szBuf; szBuf = new WCHAR [ cchBufLen ]; if (szBuf != NULL) { pdhStatus = PdhTranslate009Counter( szPath, szBuf, &cchBufLen); } } if (pdhStatus == ERROR_SUCCESS) { pszTranslatedPath = szBuf; } } // // Write properties // StringCchPrintf( szCounterName, 16, L"%s%05d.", L"Counter", iIndex ); dwCounterNameLength = lstrlen (szCounterName); // // Save the counter path into property bag // StringCchCopy(szPropertyName, 32, szCounterName); pszNext = szPropertyName + dwCounterNameLength; dwRemainingLen = 32 - dwCounterNameLength; StringCchCopy(pszNext, dwRemainingLen, L"Path" ); hr = StringToPropertyBag ( pIPropBag, szPropertyName, pszTranslatedPath ); // // Free the temporary buffer never to be used any more. // if (szBuf != NULL) { delete [] szBuf; } if (szPath != NULL) { delete [] szPath; } // // Write visual properties // if ( SUCCEEDED( hr ) ) { StringCchCopy(pszNext, dwRemainingLen, L"Color" ); hr = IntegerToPropertyBag ( pIPropBag, szPropertyName, m_rgbColor ); } if ( SUCCEEDED( hr ) ) { StringCchCopy(pszNext, dwRemainingLen, L"Width" ); hr = IntegerToPropertyBag ( pIPropBag, szPropertyName, m_iWidth ); } if ( SUCCEEDED( hr ) ) { StringCchCopy(pszNext, dwRemainingLen, L"LineStyle" ); hr = IntegerToPropertyBag ( pIPropBag, szPropertyName, m_iStyle ); } if ( SUCCEEDED( hr ) ) { INT iLocalFactor = m_iScaleFactor; StringCchCopy(pszNext, dwRemainingLen, L"ScaleFactor" ); if ( INT_MAX == iLocalFactor ) { // Save actual scale factor in case the counter cannot be // validated when the property bag file is opened. // lDefaultScale is 0 if never initialized. iLocalFactor = m_CounterInfo.lDefaultScale; } hr = IntegerToPropertyBag ( pIPropBag, szPropertyName, iLocalFactor ); } // // Write history data only if live display, data exists and not in design mode. // Log data is rebuilt from the log file. // pHistCtrl = m_pCtrl->HistoryControl(); if ( ( pHistCtrl->nSamples > 0) #if !_LOG_INCLUDE_DATA && ( !pHistCtrl->bLogSource ) #endif && bUserMode ) { LPWSTR pszData = NULL; DWORD dwMaxStrLen; // Add 1 for null. dwMaxStrLen = (pHistCtrl->nMaxSamples * MAX_DOUBLE_TEXT_SIZE) + 1; pszData = new WCHAR[ dwMaxStrLen ]; if ( NULL == pszData ) { hr = E_OUTOFMEMORY; } // Write the current statistics. if ( SUCCEEDED(hr) ) { double dMin; double dMax; double dAvg; LONG lStatus; hr = GetStatistics ( &dMax, &dMin, &dAvg, &lStatus ); if (SUCCEEDED(hr) && IsSuccessSeverity(lStatus)) { StringCchCopy(pszNext, dwRemainingLen, L"Minimum" ); hr = DoubleToPropertyBag ( pIPropBag, szPropertyName, dMin ); if ( SUCCEEDED(hr) ) { StringCchCopy(pszNext, dwRemainingLen, L"Maximum" ); hr = DoubleToPropertyBag ( pIPropBag, szPropertyName, dMax ); } if ( SUCCEEDED(hr) ) { StringCchCopy(pszNext, dwRemainingLen, L"Average" ); hr = DoubleToPropertyBag ( pIPropBag, szPropertyName, dAvg ); } if ( SUCCEEDED(hr) ) { StringCchCopy(pszNext, dwRemainingLen, L"StatisticStatus" ); hr = IntegerToPropertyBag ( pIPropBag, szPropertyName, lStatus ); } } } if ( SUCCEEDED(hr) ) { INT i; HRESULT hrConvert = S_OK; double dblValue; DWORD dwTmpStat; DWORD dwCurrentStrLength; DWORD dwDataLength; LPWSTR pszDataNext; pszData[0] = L'\0'; dwCurrentStrLength = 0; pszDataNext = pszData; for ( i = 0; ( S_OK == hrConvert ) && ( i < pHistCtrl->nSamples ); i++ ) { if ( ERROR_SUCCESS != HistoryValue(i, &dblValue, &dwTmpStat) ) { dblValue = -1.0; } else if (!IsSuccessSeverity(dwTmpStat)) { dblValue = -1.0; } VariantInit( &vValue ); vValue.vt = VT_R8; vValue.dblVal = dblValue; hrConvert = VariantChangeTypeEx( &vValue, &vValue, LCID_SCRIPT, VARIANT_NOUSEROVERRIDE, VT_BSTR ); dwDataLength = SysStringLen(vValue.bstrVal); // // If we dont have enough memory, reallocate it // // Extra WCHAR for NULL terminator if ( dwDataLength + dwCurrentStrLength + 1> dwMaxStrLen ) { WCHAR* pszNewData; dwMaxStrLen *= 2; pszNewData = new WCHAR[ dwMaxStrLen ]; if ( NULL != pszNewData ) { memcpy ( pszNewData, pszData, dwCurrentStrLength * sizeof (WCHAR) ); delete [] pszData; pszData = pszNewData; pszDataNext = pszData; } else { hr = E_OUTOFMEMORY; } } if ( SUCCEEDED(hr)) { if ( i > 0 ) { *pszDataNext = L'\t'; dwCurrentStrLength += 1; // char count for L"\t"; pszDataNext ++; } StringCchCopy(pszDataNext, dwMaxStrLen - dwCurrentStrLength, vValue.bstrVal); dwCurrentStrLength += dwDataLength; pszDataNext += dwDataLength; } VariantClear( &vValue ); } } StringCchCopy(pszNext, 32 - dwCounterNameLength, L"Data" ); hr = StringToPropertyBag ( pIPropBag, szPropertyName, pszData ); if ( NULL != pszData ) { delete [] pszData; } } return hr; } HRESULT CGraphItem::LoadFromPropertyBag ( IN IPropertyBag* pIPropBag, IN IErrorLog* pIErrorLog, IN INT iIndex, IN INT, // iVersMaj, IN INT, // iVersMin IN INT iSampleCount ) /*++ Routine Description: LoadFromPropertyBag loads the graph item's properties from the provided property bag interface. Arguments: pIPropBag - Pointer to property bag interface iVersMaj iVersMin Return Value: HRESULT - S_OK or property bag error --*/ { HRESULT hr = S_OK; WCHAR szCounterName[16]; WCHAR szPropertyName[16+16]; OLE_COLOR clrValue; INT iValue; LPWSTR pszData = NULL; int iBufSizeCurrent = 0; int iBufSize; LPWSTR pszNext; DWORD dwCounterNameLength; StringCchPrintf( szCounterName, 16, L"%s%05d.", L"Counter", iIndex ); dwCounterNameLength = lstrlen (szCounterName); // Read visual properties assert( 32 > dwCounterNameLength); StringCchCopy(szPropertyName, 32, szCounterName ); pszNext = szPropertyName + dwCounterNameLength; StringCchCopy(pszNext, 32 - dwCounterNameLength, L"Color" ); hr = OleColorFromPropertyBag ( pIPropBag, pIErrorLog, szPropertyName, clrValue ); if ( SUCCEEDED(hr) ) { hr = put_Color ( clrValue ); } StringCchCopy(pszNext, 32 - dwCounterNameLength, L"Width" ); hr = IntegerFromPropertyBag ( pIPropBag, pIErrorLog, szPropertyName, iValue ); if ( SUCCEEDED(hr) ) { hr = put_Width ( iValue ); } StringCchCopy(pszNext, 32 - dwCounterNameLength, L"LineStyle" ); hr = IntegerFromPropertyBag ( pIPropBag, pIErrorLog, szPropertyName, iValue ); if ( SUCCEEDED(hr) ) { hr = put_LineStyle ( iValue ); } StringCchCopy(pszNext, 32 - dwCounterNameLength, L"ScaleFactor" ); hr = IntegerFromPropertyBag ( pIPropBag, pIErrorLog, szPropertyName, iValue ); if ( SUCCEEDED(hr) ) { hr = put_ScaleFactor ( iValue ); } if ( 0 < iSampleCount ) { if ( NULL != m_pFmtCtr ) { delete [] m_pFmtCtr; } m_pFmtCtr = new double[MAX_GRAPH_SAMPLES]; if ( NULL == m_pFmtCtr ) { hr = E_OUTOFMEMORY; } else { INT iFmtIndex; for (iFmtIndex = 0; iFmtIndex < MAX_GRAPH_SAMPLES; iFmtIndex++ ) { m_pFmtCtr[iFmtIndex] = -1.0; } } if ( SUCCEEDED(hr) ) { StringCchCopy(pszNext, 32 - dwCounterNameLength, L"Data" ); iBufSize = iBufSizeCurrent; hr = StringFromPropertyBag ( pIPropBag, pIErrorLog, szPropertyName, pszData, iBufSize ); // // StringFromPropertyBag return SUCCESS status even if the buffer is too small // Design defect?? // if ( SUCCEEDED(hr) && iBufSize > iBufSizeCurrent ) { if ( NULL != pszData ) { delete [] pszData; } pszData = new WCHAR[ iBufSize ]; if ( NULL == pszData ) { hr = E_OUTOFMEMORY; } else { pszData[0] = L'\0'; iBufSizeCurrent = iBufSize; hr = StringFromPropertyBag ( pIPropBag, pIErrorLog, szPropertyName, pszData, iBufSize ); } } } // Read the samples in buffer order. if ( NULL != pszData && SUCCEEDED ( hr ) ) { INT iDataIndex; double dValue = 0; WCHAR* pNextData; WCHAR* pDataEnd; pNextData = pszData; pDataEnd = pszData + lstrlen(pszData); for ( iDataIndex = 0; iDataIndex < iSampleCount; iDataIndex++ ) { if ( pNextData < pDataEnd ) { hr = GetNextValue ( pNextData, dValue ); } else { hr = E_FAIL; } if ( SUCCEEDED(hr) ) { SetHistoryValue ( iDataIndex, dValue ); } else { SetHistoryValue ( iDataIndex, -1.0 ); // iSampleCount = 0; // Control loaded fine, just no data. hr = NOERROR; } } } if ( NULL != pszData ) { delete [] pszData; } // Read the current statistics. StringCchCopy(pszNext, 32 - dwCounterNameLength, L"Maximum" ); hr = DoubleFromPropertyBag ( pIPropBag, pIErrorLog, szPropertyName, m_dFmtMax ); StringCchCopy(pszNext, 32 - dwCounterNameLength, L"Minimum" ); hr = DoubleFromPropertyBag ( pIPropBag, pIErrorLog, szPropertyName, m_dFmtMin ); StringCchCopy(pszNext, 32 - dwCounterNameLength, L"Average" ); hr = DoubleFromPropertyBag ( pIPropBag, pIErrorLog, szPropertyName, m_dFmtAvg ); StringCchCopy(pszNext, 32 - dwCounterNameLength, L"StatisticStatus" ); hr = IntegerFromPropertyBag ( pIPropBag, pIErrorLog, szPropertyName, (INT&)m_lFmtStatus ); } return hr; } HRESULT CGraphItem::AddToQuery ( IN HQUERY hQuery ) /*++ Routine Description: AddToQuery adds a counter to the provided query based on the item's pathname. It also allocates an array of raw counter value structures for holding the counter's sample history. Arguments: hQuery - Handle to query Return Value: Boolean status - TRUE = success --*/ { HCOUNTER hCounter; INT i; HRESULT hr = NO_ERROR; LPWSTR szPath = NULL; DWORD size; PDH_COUNTER_INFO ci; PHIST_CONTROL pHistCtrl = m_pCtrl->HistoryControl(); // Can't add if already in query if (m_hCounter != NULL) return E_FAIL; // // Generate the full path of the counter // szPath = FormPath( FALSE); if (szPath == NULL) { return E_FAIL; } // // We use do {} while (0) here to act like a switch statement // do { // Allocate memory for maximum sample count if (pHistCtrl->nMaxSamples > 0) { // if log data if (pHistCtrl->bLogSource) { // allocate space for formatted values m_pLogData = new LOG_ENTRY_DATA[pHistCtrl->nMaxSamples]; if (m_pLogData == NULL) { hr = E_OUTOFMEMORY; break; } // Clear the statistics m_dLogMax = 0.0; m_dLogMin = 0.0; m_dLogAvg = 0.0; m_lLogStatsStatus = PDH_CSTATUS_INVALID_DATA; } else { // else allocate raw value space m_pRawCtr = new PDH_RAW_COUNTER[pHistCtrl->nMaxSamples]; if ( NULL == m_pRawCtr ) { hr = E_OUTOFMEMORY; break; } // Clear all status flags for (i=0; i < pHistCtrl->nMaxSamples; i++) m_pRawCtr[i].CStatus = PDH_CSTATUS_INVALID_DATA; } } // Create the counter object hr = PdhAddCounter(hQuery, szPath, 0, &hCounter); if (IsErrorSeverity(hr)) { if (pHistCtrl->bLogSource) { delete [] m_pLogData; m_pLogData = NULL; } else { delete [] m_pRawCtr; m_pRawCtr = NULL; } break; } size = sizeof(ci); hr = PdhGetCounterInfo (hCounter, FALSE, &size, &ci); if (hr == ERROR_SUCCESS) { m_CounterInfo = ci; if ( INT_MAX == m_iScaleFactor ) { m_dScale = pow ((double)10.0f, (double)ci.lDefaultScale); } } m_hCounter = hCounter; } while (0); delete [] szPath; return hr; } HRESULT CGraphItem::RemoveFromQuery ( VOID ) /*++ Routine Description: RemoveFromQuery deletes the item's counter and releases its history array. Arguments: None. Return Value: Boolean status - TRUE = success --*/ { // If no counter handle, not attached to query if (m_hCounter == NULL) return S_FALSE; // Delete the counter PdhRemoveCounter(m_hCounter); m_hCounter = NULL; // Free the buffers if (m_pLogData) { delete [] m_pLogData; m_pLogData = NULL; } if (m_pRawCtr) { delete [] m_pRawCtr; m_pRawCtr = NULL; } if (m_pFmtCtr) { delete [] m_pFmtCtr; m_pFmtCtr = NULL; } return NOERROR; } void CGraphItem::ClearHistory ( void ) /*++ Routine Description: ClearHistory resets the raw counter buffer values to Invalid. Arguments: None. Return Value: None. --*/ { INT i; // Clear all status flags if ( NULL != m_pRawCtr ) { for (i=0; i < m_pCtrl->HistoryControl()->nMaxSamples; i++) { m_pRawCtr[i].CStatus = PDH_CSTATUS_INVALID_DATA; } } } VOID CGraphItem::UpdateHistory ( IN BOOL bValidSample ) /*++ Routine Description: UpdateHistory reads the raw value for the counter and stores it in the history slot specified by the history control. Arguments: bValidSample - True if raw value is available, False if missed sample Return Value: None. --*/ { DWORD dwCtrType; // Make sure there is a counter handle if (m_hCounter == NULL) return; if (bValidSample) { // Read the raw value if ( NULL != m_pRawCtr ) { PdhGetRawCounterValue(m_hCounter, &dwCtrType, &m_pRawCtr[m_pCtrl->HistoryControl()->iCurrent]); } } else { // Mark value failed if ( NULL != m_pRawCtr ) { m_pRawCtr[m_pCtrl->HistoryControl()->iCurrent].CStatus = PDH_CSTATUS_INVALID_DATA; } } } PDH_STATUS CGraphItem::HistoryValue ( IN INT iIndex, OUT double *pdValue, OUT DWORD *pdwStatus ) /*++ Routine Description: HistoryValue computes a formated sample value from the selected raw history sample. The calculation is actually based on the the specified sample plus the preceding sample. Arguments: iIndex - Index of desired sample (0 = current, 1 = previous, ...) pdValue - Pointer to return value pdwStatus - Pointer to return counter status (PDH_CSTATUS_...) Return Value: Error status --*/ { PDH_STATUS stat = ERROR_INVALID_PARAMETER; INT iPrevIndex; INT iCurrIndex; PDH_FMT_COUNTERVALUE FmtValue; PHIST_CONTROL pHistCtrl = m_pCtrl->HistoryControl(); // Check for negative index if ( iIndex >= 0 ) { // If sample not available from cache or data, return invalid data status if ( NULL == m_pFmtCtr && ( m_hCounter == NULL || iIndex + 1 >= pHistCtrl->nSamples ) ) { *pdwStatus = PDH_CSTATUS_INVALID_DATA; *pdValue = 0.0; stat = ERROR_SUCCESS; } else { // if log source, index back from last valid sample if (m_pCtrl->IsLogSource()) { *pdValue = m_pLogData[pHistCtrl->nSamples - iIndex].m_dAvg; *pdwStatus = (*pdValue >= 0.0) ? PDH_CSTATUS_VALID_DATA : PDH_CSTATUS_INVALID_DATA; stat = ERROR_SUCCESS; } else { // Determine history array index of sample iCurrIndex = pHistCtrl->iCurrent - iIndex; if (iCurrIndex < 0) iCurrIndex += pHistCtrl->nMaxSamples; // Check to determine if loading from property bag if ( NULL == m_pFmtCtr ) { // Need previous sample as well if (iCurrIndex > 0) iPrevIndex = iCurrIndex - 1; else iPrevIndex = pHistCtrl->nMaxSamples - 1; // Compute the formatted value if ( NULL != m_pRawCtr ) { stat = PdhCalculateCounterFromRawValue(m_hCounter, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, &m_pRawCtr[iCurrIndex], &m_pRawCtr[iPrevIndex], &FmtValue); // Return value and status *pdValue = FmtValue.doubleValue; *pdwStatus = FmtValue.CStatus; } else { stat = ERROR_GEN_FAILURE; // Todo: More specific error } } else { // Loading from property bag *pdValue = m_pFmtCtr[iCurrIndex]; if ( 0 <= m_pFmtCtr[iCurrIndex] ) { *pdwStatus = ERROR_SUCCESS; } else { *pdwStatus = PDH_CSTATUS_INVALID_DATA; } stat = ERROR_SUCCESS; } } } } return stat; } void CGraphItem::SetHistoryValue ( IN INT iIndex, OUT double dValue ) /*++ Routine Description: SetHistoryValue loads a formated sample value for the specified sample index. This method is used when loading the control from a property bag. Arguments: iIndex - Index of desired sample (0 = current, 1 = previous, ...) dValue - Value Return Value: Error status --*/ { PHIST_CONTROL pHistCtrl = m_pCtrl->HistoryControl(); INT iRealIndex; // Check for negative index if ( (iIndex < 0) || ( iIndex >= pHistCtrl->nMaxSamples) ) { return; } if ( NULL == m_pFmtCtr ) { return; } // if log source, index back from last sample if (m_pCtrl->IsLogSource()) { return; } else { // Determine history array index of sample iRealIndex = pHistCtrl->iCurrent - iIndex; if (iRealIndex < 0) iRealIndex += pHistCtrl->nSamples; m_pFmtCtr[iRealIndex] = dValue; } return; } PDH_STATUS CGraphItem::GetLogEntry( const INT iIndex, double *dMin, double *dMax, double *dAvg, DWORD *pdwStatus ) { INT iLocIndex = iIndex; *dMin = -1.0; *dMax = -1.0; *dAvg = -1.0; *pdwStatus = PDH_CSTATUS_INVALID_DATA; if (m_pLogData == NULL) return PDH_NO_DATA; if (iLocIndex < 0 || iLocIndex >= m_pCtrl->HistoryControl()->nMaxSamples) return PDH_INVALID_ARGUMENT; // Subtract 1 because array is zero-based // Subtract another 1 because ?? iLocIndex = ( m_pCtrl->HistoryControl()->nMaxSamples - 2 ) - iIndex; if (m_pLogData[iLocIndex].m_dMax < 0.0) { *pdwStatus = PDH_CSTATUS_INVALID_DATA; } else { *dMin = m_pLogData[iLocIndex].m_dMin; *dMax = m_pLogData[iLocIndex].m_dMax; *dAvg = m_pLogData[iLocIndex].m_dAvg; *pdwStatus = PDH_CSTATUS_VALID_DATA; } return ERROR_SUCCESS; } PDH_STATUS CGraphItem::GetLogEntryTimeStamp( const INT iIndex, LONGLONG& rLastTimeStamp, DWORD *pdwStatus ) { INT iLocIndex = iIndex; rLastTimeStamp = 0; *pdwStatus = PDH_CSTATUS_INVALID_DATA; if (m_pLogData == NULL) return PDH_NO_DATA; if (iIndex < 0 || iIndex >= m_pCtrl->HistoryControl()->nMaxSamples) return PDH_INVALID_ARGUMENT; if ( ( MIN_TIME_VALUE == *((LONGLONG*)&m_pLogData[iLocIndex].m_LastTimeStamp) ) || ( 0 > *((LONGLONG*)&m_pLogData[iLocIndex].m_dMax) ) ) { *pdwStatus = PDH_CSTATUS_INVALID_DATA; } else { *pdwStatus = PDH_CSTATUS_VALID_DATA; } rLastTimeStamp = *((LONGLONG*)&m_pLogData[iLocIndex].m_LastTimeStamp); return ERROR_SUCCESS; } void CGraphItem::SetLogEntry( const INT iIndex, const double dMin, const double dMax, const double dAvg ) { if (m_pLogData) { m_pLogData[iIndex].m_dMin = dMin; m_pLogData[iIndex].m_dMax = dMax; m_pLogData[iIndex].m_dAvg = dAvg; } } void CGraphItem::SetLogEntryTimeStamp ( const INT iIndex, const FILETIME& rLastTimeStamp ) { if (m_pLogData) { m_pLogData[iIndex].m_LastTimeStamp.dwLowDateTime = rLastTimeStamp.dwLowDateTime; m_pLogData[iIndex].m_LastTimeStamp.dwHighDateTime = rLastTimeStamp.dwHighDateTime; } } HRESULT CGraphItem::GetValue( OUT double *pdValue, OUT long *plStat ) /*++ Routine Description: get_Value returns the most recent sample value for the counter. Arguments: pdValue - Pointer to returned value dlStatus - Pointer to returned counter status (PDH_CSTATUS_...) Return Value: HRESULT --*/ { DWORD dwTmpStat; // Convert PDH status to HRESULT if (HistoryValue(0, pdValue, &dwTmpStat) != 0) return E_FAIL; *plStat = dwTmpStat; return NOERROR; } HRESULT CGraphItem::GetStatistics ( OUT double *pdMax, OUT double *pdMin, OUT double *pdAvg, OUT LONG *plStatus ) /*++ Routine Description: GetStatistics computes the max, min, and average values for the sample history. Arguments: pdMax - Pointer to returned max value pdMax - Pointer to returned min value pdMax - Pointer to returned average value plStatus - Pointer to return counter status (PDH_CSTATUS_...) Return Value: HRESULT --*/ { HRESULT hr = NOERROR; PDH_STATUS stat = ERROR_SUCCESS; PDH_STATISTICS StatData; INT iFirst; PHIST_CONTROL pHistCtrl; // If no data collected, return invalid data status if ( NULL == m_hCounter ) { *plStatus = PDH_CSTATUS_INVALID_DATA; } else { if (m_pCtrl->IsLogSource()) { if (m_pLogData && PDH_CSTATUS_VALID_DATA == m_lLogStatsStatus ) { *pdMax = m_dLogMax; *pdMin = m_dLogMin; *pdAvg = m_dLogAvg; } else { *pdMax = 0.0; *pdMin = 0.0; *pdAvg = 0.0; } *plStatus = m_lLogStatsStatus; } else { if ( NULL == m_pFmtCtr ) { pHistCtrl = m_pCtrl->HistoryControl(); ZeroMemory ( &StatData, sizeof ( PDH_STATISTICS ) ); // Determine index of oldest sample if (pHistCtrl->iCurrent < pHistCtrl->nSamples - 1) { iFirst = pHistCtrl->iCurrent + 1; } else { iFirst = 0; } // Compute statistics over all samples // Note that max sample count is passed (i.e., buffer length) // not the number of actual samples if ( NULL != m_pRawCtr ) { stat = PdhComputeCounterStatistics (m_hCounter, PDH_FMT_DOUBLE | PDH_FMT_NOCAP100, iFirst, pHistCtrl->nMaxSamples, m_pRawCtr, &StatData ); if ( 0 != stat ) hr = E_FAIL; } else { hr = E_FAIL; } if ( SUCCEEDED ( hr ) ) { *plStatus = StatData.mean.CStatus; *pdMin = StatData.min.doubleValue; *pdMax = StatData.max.doubleValue; *pdAvg = StatData.mean.doubleValue; } } else { // Data is cached from property bag. *pdMax = m_dFmtMax; *pdMin = m_dFmtMin; *pdAvg = m_dFmtAvg; *plStatus = m_lFmtStatus; } } } return hr; } void CGraphItem::SetStatistics ( IN double dMax, IN double dMin, IN double dAvg, IN LONG lStatus ) /*++ Routine Description: SetStatistics sets the max, min, and average values for the sample history. It is used by LoadFromPropertyBag only. Arguments: dMax - max value dMin - min value dAvg - average value lStatus - counter status (PDH_CSTATUS_...) Return Value: HRESULT --*/ { if (!m_pCtrl->IsLogSource()) { m_dFmtMax = dMax; m_dFmtMin = dMin; m_dFmtAvg = dAvg; m_lFmtStatus = lStatus; } } /* * CGraphItem::QueryInterface * CGraphItem::AddRef * CGraphItem::Release */ STDMETHODIMP CGraphItem::QueryInterface( IN REFIID riid, OUT LPVOID *ppv ) { HRESULT hr = S_OK; try { *ppv = NULL; if (riid == IID_ICounterItem || riid == IID_IUnknown) { *ppv = this; } else if (riid == DIID_DICounterItem) { if (m_pImpIDispatch == NULL) { m_pImpIDispatch = new CImpIDispatch(this, this); if (m_pImpIDispatch == NULL) { hr = E_OUTOFMEMORY; } else { m_pImpIDispatch->SetInterface(DIID_DICounterItem, this); *ppv = m_pImpIDispatch; } } else { *ppv = m_pImpIDispatch; } } else { hr = E_NOINTERFACE; } // // So far everything is OK, add reference and return it. // if (*ppv != NULL) { ((LPUNKNOWN)*ppv)->AddRef(); } } catch (...) { hr = E_POINTER; } return hr; } STDMETHODIMP_(ULONG) CGraphItem::AddRef(void) { return ++m_cRef; } STDMETHODIMP_(ULONG) CGraphItem::Release(void) { if (--m_cRef == 0) { delete this; return 0; } return m_cRef; } // Get/Put Color STDMETHODIMP CGraphItem::put_Color ( IN OLE_COLOR Color ) { COLORREF rgbColor; HRESULT hr; hr = OleTranslateColor(Color, NULL, &rgbColor); if ( S_OK == hr ) { m_rgbColor = rgbColor; InvalidatePen(); InvalidateBrush(); } return hr; } STDMETHODIMP CGraphItem::get_Color ( OUT OLE_COLOR *pColor ) { HRESULT hr = S_OK; try { *pColor = m_rgbColor; } catch (...) { hr = E_POINTER; } return hr; } // Get/Put Width STDMETHODIMP CGraphItem::put_Width ( IN INT iWidthInPixels) { HRESULT hr = S_OK; if ( ( iWidthInPixels > 0 ) && (iWidthInPixels <= NumWidthIndices() ) ) { m_iWidth = iWidthInPixels; InvalidatePen(); } else { hr = E_INVALIDARG; } return hr; } STDMETHODIMP CGraphItem::get_Width ( OUT INT* piWidthInPixels ) { HRESULT hr = S_OK; try { *piWidthInPixels = m_iWidth; } catch (...) { hr = E_POINTER; } return hr; } // Get/Put Line Style STDMETHODIMP CGraphItem::put_LineStyle ( IN INT iLineStyle ) { HRESULT hr = S_OK; if ( ( iLineStyle >= 0 ) && (iLineStyle < NumStyleIndices() ) ) { m_iStyle = iLineStyle; InvalidatePen(); } else { hr = E_INVALIDARG; } return hr; } STDMETHODIMP CGraphItem::get_LineStyle ( OUT INT* piLineStyle ) { HRESULT hr = S_OK; try { *piLineStyle = m_iStyle; } catch (...) { hr = E_POINTER; } return hr; } // Get/Put Scale STDMETHODIMP CGraphItem::put_ScaleFactor ( IN INT iScaleFactor ) { HRESULT hr = NOERROR; if ( ( INT_MAX == iScaleFactor ) || ( ( iScaleFactor >= PDH_MIN_SCALE ) && (iScaleFactor <= PDH_MAX_SCALE) ) ) { PDH_COUNTER_INFO ci; DWORD size; m_iScaleFactor = iScaleFactor; if ( INT_MAX == iScaleFactor ) { if ( NULL != Handle() ) { size = sizeof(ci); hr = PdhGetCounterInfo ( Handle(), FALSE, &size, &ci); if (hr == ERROR_SUCCESS) { m_dScale = pow ((double)10.0f, (double)ci.lDefaultScale); m_CounterInfo = ci; } } else { // m_dScale remains at previous value (default=1) hr = PDH_INVALID_HANDLE; } } else { m_dScale = pow ((double)10.0, (double)iScaleFactor); hr = NOERROR; } } else { hr = E_INVALIDARG; } return hr; } STDMETHODIMP CGraphItem::get_ScaleFactor ( OUT INT* piScaleFactor ) { HRESULT hr = S_OK; try { *piScaleFactor = m_iScaleFactor; } catch (...) { hr = E_POINTER; } return hr; } STDMETHODIMP CGraphItem::get_Path ( OUT BSTR* pstrPath ) { LPWSTR szPath = NULL; BSTR pTmpPath = NULL; HRESULT hr = S_OK; szPath = FormPath(FALSE); if (szPath == NULL) { hr = E_OUTOFMEMORY; } else { pTmpPath = SysAllocString(szPath); if ( NULL == pTmpPath) { hr = E_OUTOFMEMORY; } } try { *pstrPath = pTmpPath; } catch (...) { hr = E_POINTER; } if (szPath) { delete [] szPath; } if (FAILED(hr) && pTmpPath) { SysFreeString(pTmpPath); } return hr; } STDMETHODIMP CGraphItem::get_Value ( OUT double* pdValue ) { DWORD dwTmpStat; double dValue; HRESULT hr = S_OK; try { *pdValue = 0; // Convert PDH status to HRESULT if (HistoryValue(0, &dValue, &dwTmpStat) != 0) { dValue = -1.0; } else { if (!IsSuccessSeverity(dwTmpStat)) { dValue = -1.0; } } *pdValue = dValue; } catch (...) { hr = E_POINTER; } return hr; } HPEN CGraphItem::Pen(void) { // if pen not valid if (m_hPen == NULL) { // create a new one based on current attributes m_hPen = CreatePen(m_iStyle, m_iWidth, m_rgbColor); // if can't do it, use a stock object (this can't fail) if (m_hPen == NULL) m_hPen = (HPEN)GetStockObject(BLACK_PEN); } return m_hPen; } HBRUSH CGraphItem::Brush(void) { // if brush is not valid if (m_hBrush == NULL) { m_hBrush = CreateSolidBrush(m_rgbColor); if (m_hBrush == NULL) m_hBrush = (HBRUSH)GetStockObject(BLACK_BRUSH); } return m_hBrush; } void CGraphItem::InvalidatePen(void) { if (m_hPen != NULL) { DeleteObject(m_hPen); m_hPen = NULL; } } void CGraphItem::InvalidateBrush(void) { if (m_hBrush != NULL) { DeleteObject(m_hBrush); m_hBrush = NULL; } } CGraphItem* CGraphItem::Next ( void ) { PCInstanceNode pInstance; PCObjectNode pObject; PCMachineNode pMachine; if (m_pNextItem) return m_pNextItem; else if ( NULL != m_pInstance->Next()) { pInstance = m_pInstance->Next(); return pInstance->FirstItem(); } else if ( NULL != m_pInstance->m_pObject->Next()) { pObject = m_pInstance->m_pObject->Next(); return pObject->FirstInstance()->FirstItem(); } else if ( NULL != m_pInstance->m_pObject->m_pMachine->Next()) { pMachine = m_pInstance->m_pObject->m_pMachine->Next(); return pMachine->FirstObject()->FirstInstance()->FirstItem(); } else { return NULL; } } LPWSTR CGraphItem::FormPath( BOOL fWildCard ) /*++ Routine Description: The function generate the full path of a counter item. Arguments: fWildCard - Indicates whether includeing wild card in counter path Return Value: Return the generated counter path, the caller must free it when finished using it. --*/ { ULONG ulCchBuf; LPWSTR szBuf = NULL; PDH_STATUS pdhStatus; PDH_COUNTER_PATH_ELEMENTS CounterPathElements; do { if ( szBuf ) { delete [] szBuf; szBuf = NULL; } else { ulCchBuf = PDH_MAX_COUNTER_PATH + 1; } szBuf = new WCHAR [ulCchBuf]; if (szBuf == NULL) { return NULL; } CounterPathElements.szMachineName = (LPWSTR)Machine()->Name(); CounterPathElements.szObjectName = (LPWSTR)Object()->Name(); if (fWildCard) { CounterPathElements.szInstanceName = L"*"; CounterPathElements.szParentInstance = NULL; } else { LPWSTR szInstName; LPWSTR szParentName; szInstName = Instance()->GetInstanceName(); if ( szInstName[0] ) { CounterPathElements.szInstanceName = szInstName; szParentName = Instance()->GetParentName(); if (szParentName[0]) { CounterPathElements.szParentInstance = szParentName; } else { CounterPathElements.szParentInstance = NULL; } } else { CounterPathElements.szInstanceName = NULL; CounterPathElements.szParentInstance = NULL; } } CounterPathElements.dwInstanceIndex = (DWORD)-1; CounterPathElements.szCounterName = (LPWSTR)Counter()->Name(); pdhStatus = PdhMakeCounterPath( &CounterPathElements, szBuf, &ulCchBuf, 0); } while (pdhStatus == PDH_MORE_DATA || pdhStatus == PDH_INSUFFICIENT_BUFFER); if (pdhStatus != ERROR_SUCCESS) { delete [] szBuf; return NULL; } // // Strip off the machine name. // if (m_fLocalMachine && szBuf[0] == L'\\' && szBuf[1] == L'\\') { LPWSTR szNewBuf = NULL; LPWSTR p; INT iNewLen = 0; p = &szBuf[2]; while (*p && *p != L'\\') { p++; } iNewLen = lstrlen(p) + 1; szNewBuf = new WCHAR [iNewLen]; if ( NULL != szNewBuf ) { StringCchCopy ( szNewBuf, iNewLen, p ); delete [] szBuf; szBuf = szNewBuf; } else { delete [] szBuf; szBuf = NULL; } } return szBuf; } void CGraphItem::Delete ( BOOL bPropogateUp ) // // This method just provides a convenient access to the DeleteCounter method // of the control when you only have a pointer to the graph item. // { m_pCtrl->DeleteCounter(this, bPropogateUp); } HRESULT CGraphItem::GetNextValue ( WCHAR*& pszNext, double& rdValue ) { HRESULT hr = S_OK; WCHAR szValue[MAX_DOUBLE_TEXT_SIZE + 1]; INT iLen; VARIANT vValue; rdValue = -1.0; iLen = wcscspn (pszNext, L"\t"); // // Change tab character to null. // pszNext[iLen] = L'\0'; hr = StringCchCopy ( szValue, MAX_DOUBLE_TEXT_SIZE + 1, pszNext ); if ( SUCCEEDED ( hr ) ) { VariantInit( &vValue ); vValue.vt = VT_BSTR; vValue.bstrVal = SysAllocString ( szValue ); hr = VariantChangeTypeEx( &vValue, &vValue, LCID_SCRIPT, VARIANT_NOUSEROVERRIDE, VT_R8 ); if ( SUCCEEDED(hr) ) { rdValue = vValue.dblVal; } VariantClear( &vValue ); } pszNext += iLen + 1 ; return hr; } BOOL CGraphItem::CalcRequiresMultipleSamples ( void ) { BOOL bReturn = TRUE; // // Todo: This code is a duplicate of PdhiCounterNeedLastValue. // When that method is added to pdhicalc.h, then use it instead of thie // duplicate code. // switch (m_CounterInfo.dwType) { case PERF_DOUBLE_RAW: case PERF_ELAPSED_TIME: case PERF_RAW_FRACTION: case PERF_LARGE_RAW_FRACTION: case PERF_COUNTER_RAWCOUNT: case PERF_COUNTER_LARGE_RAWCOUNT: case PERF_COUNTER_RAWCOUNT_HEX: case PERF_COUNTER_LARGE_RAWCOUNT_HEX: case PERF_COUNTER_TEXT: case PERF_SAMPLE_BASE: case PERF_AVERAGE_BASE: case PERF_COUNTER_MULTI_BASE: case PERF_RAW_BASE: //case PERF_LARGE_RAW_BASE: case PERF_COUNTER_HISTOGRAM_TYPE: case PERF_COUNTER_NODATA: case PERF_PRECISION_TIMESTAMP: bReturn = FALSE; break; case PERF_AVERAGE_TIMER: case PERF_COUNTER_COUNTER: case PERF_COUNTER_BULK_COUNT: case PERF_SAMPLE_COUNTER: case PERF_AVERAGE_BULK: case PERF_COUNTER_TIMER: case PERF_100NSEC_TIMER: case PERF_OBJ_TIME_TIMER: case PERF_COUNTER_QUEUELEN_TYPE: case PERF_COUNTER_LARGE_QUEUELEN_TYPE: case PERF_COUNTER_100NS_QUEUELEN_TYPE: case PERF_COUNTER_OBJ_TIME_QUEUELEN_TYPE: case PERF_SAMPLE_FRACTION: case PERF_COUNTER_MULTI_TIMER: case PERF_100NSEC_MULTI_TIMER: case PERF_PRECISION_SYSTEM_TIMER: case PERF_PRECISION_100NS_TIMER: case PERF_PRECISION_OBJECT_TIMER: case PERF_COUNTER_TIMER_INV: case PERF_100NSEC_TIMER_INV: case PERF_COUNTER_MULTI_TIMER_INV: case PERF_100NSEC_MULTI_TIMER_INV: case PERF_COUNTER_DELTA: case PERF_COUNTER_LARGE_DELTA: default: bReturn = TRUE; break; } return bReturn; }