Source code of Windows XP (NT5)
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 
 
 

1740 lines
41 KiB

/*++
Copyright (C) 1996-1999 Microsoft Corporation
Module Name:
grphitem.cpp
Abstract:
<abstract>
--*/
#ifndef _LOG_INCLUDE_DATA
#define _LOG_INCLUDE_DATA 0
#endif
#include <math.h>
#include <limits.h> // for INT_MAX
#include <pdhp.h>
#include "polyline.h"
#include "visuals.h"
#include "grphitem.h"
#include "unihelpr.h"
#include "utils.h"
#include "pdhmsg.h"
#define MAX_DOUBLE_TEXT_SIZE (64)
// 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_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;
return;
}
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:
SaveToStream writes the graph item's properties to the provided stream.
Arguments:
pIStream - Pointer to stream interface
pszPath - Path name to save item under
Return Value:
HRESULT - S_OK or stream error
--*/
{
LPWSTR pszWidePath;
TCHAR szPath[MAX_PATH];
LPTSTR szEnglishBuf = NULL;
DWORD dwEnglishBufSize = 0;
LPTSTR pNewBuf;
DWORD dwBufSize;
LPTSTR pszPath;
HRESULT hr = S_OK;
PDH_STATUS pdhStatus;
USES_CONVERSION
// Get Ansi path name
FormPath(szPath, fWildCard );
pszPath = szPath;
//
// Initialize the locale path buffer
//
if (dwEnglishBufSize == 0) {
dwEnglishBufSize = (MAX_PATH + 1) * sizeof(TCHAR);
szEnglishBuf = (LPTSTR) malloc(dwEnglishBufSize);
if (szEnglishBuf == NULL) {
dwEnglishBufSize = 0;
}
}
if (szEnglishBuf != NULL) {
//
// Translate counter name from Localization into English
//
dwBufSize = dwEnglishBufSize;
pdhStatus = PdhTranslate009Counter(
szPath,
szEnglishBuf,
&dwBufSize);
if (pdhStatus == PDH_MORE_DATA) {
pNewBuf = (LPTSTR)realloc(szEnglishBuf, dwBufSize);
if (pNewBuf != NULL) {
szEnglishBuf = pNewBuf;
dwEnglishBufSize = dwBufSize;
pdhStatus = PdhTranslate009Counter(
szPath,
szEnglishBuf,
&dwBufSize);
}
}
if (pdhStatus == ERROR_SUCCESS) {
pszPath = szEnglishBuf;
}
}
pszWidePath = T2W(pszPath);
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(pszWidePath ) );
ItemData.m_nPathLength = lstrlen(pszWidePath);
// Write structure to stream
hr = pIStream->Write(&ItemData, sizeof(ItemData), NULL);
if (FAILED(hr)) {
goto ErrorOut;
}
// Write path name to stream
hr = pIStream->Write(pszWidePath, ItemData.m_nPathLength*sizeof(WCHAR), NULL);
if (FAILED(hr)) {
goto ErrorOut;
}
}
ErrorOut:
if (szEnglishBuf != NULL) {
free(szEnglishBuf);
}
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;
TCHAR szPath[MAX_PATH];
PHIST_CONTROL pHistCtrl;
VARIANT vValue;
TCHAR szCounterName[16];
TCHAR szPropertyName[16+16];
DWORD dwCounterNameBytes;
DWORD dwCounterNameLength;
LPTSTR pszNext;
LPTSTR szEnglishBuf = NULL;
DWORD dwEnglishBufSize = 0;
LPTSTR pszPath;
DWORD dwBufSize;
LPTSTR pNewBuf;
PDH_STATUS pdhStatus;
USES_CONVERSION
// Write properties
// Write path name
_stprintf ( szCounterName, _T("%s%05d."), _T("Counter"), iIndex );
dwCounterNameLength = lstrlen (szCounterName);
dwCounterNameBytes = dwCounterNameLength * sizeof (TCHAR);
//
// Generate full path name. (machine\object\instance\counter format)
//
FormPath(szPath, FALSE);
pszPath = szPath;
//
// Initialize the locale path buffer
//
if (dwEnglishBufSize == 0) {
dwEnglishBufSize = (MAX_PATH + 1) * sizeof(TCHAR);
szEnglishBuf = (LPTSTR) malloc(dwEnglishBufSize);
if (szEnglishBuf == NULL) {
dwEnglishBufSize = 0;
}
}
if (szEnglishBuf != NULL) {
//
// Translate counter name from Localization into English
//
dwBufSize = dwEnglishBufSize;
pdhStatus = PdhTranslate009Counter(
szPath,
szEnglishBuf,
&dwBufSize);
if (pdhStatus == PDH_MORE_DATA) {
pNewBuf = (LPTSTR)realloc(szEnglishBuf, dwBufSize);
if (pNewBuf != NULL) {
szEnglishBuf = pNewBuf;
dwEnglishBufSize = dwBufSize;
pdhStatus = PdhTranslate009Counter(
szPath,
szEnglishBuf,
&dwBufSize);
}
}
if (pdhStatus == ERROR_SUCCESS) {
pszPath = szEnglishBuf;
}
}
//
// Save the counter path into property bag
//
memcpy ( szPropertyName, szCounterName, dwCounterNameBytes );
pszNext = szPropertyName + dwCounterNameLength;
lstrcpy ( pszNext, _T("Path") );
hr = StringToPropertyBag (
pIPropBag,
szPropertyName,
pszPath );
if (szEnglishBuf != NULL) {
free(szEnglishBuf);
}
// Write visual properties
if ( SUCCEEDED( hr ) ){
lstrcpy ( pszNext, _T("Color") );
hr = IntegerToPropertyBag ( pIPropBag, szPropertyName, m_rgbColor );
}
if ( SUCCEEDED( hr ) ){
lstrcpy ( pszNext, _T("Width") );
hr = IntegerToPropertyBag ( pIPropBag, szPropertyName, m_iWidth );
}
if ( SUCCEEDED( hr ) ){
lstrcpy ( pszNext, _T("LineStyle") );
hr = IntegerToPropertyBag ( pIPropBag, szPropertyName, m_iStyle );
}
if ( SUCCEEDED( hr ) ){
INT iLocalFactor = m_iScaleFactor;
lstrcpy ( pszNext, _T("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 ) {
LPTSTR pszData = NULL;
DWORD dwMaxStrLen = ( pHistCtrl->nMaxSamples * MAX_DOUBLE_TEXT_SIZE ) + 1;
pszData = new TCHAR[ 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)) {
lstrcpy ( pszNext, _T("Minimum") );
hr = DoubleToPropertyBag ( pIPropBag, szPropertyName, dMin );
if ( SUCCEEDED(hr) ) {
lstrcpy ( pszNext, _T("Maximum") );
hr = DoubleToPropertyBag ( pIPropBag, szPropertyName, dMax );
}
if ( SUCCEEDED(hr) ) {
lstrcpy ( pszNext, _T("Average") );
hr = DoubleToPropertyBag ( pIPropBag, szPropertyName, dAvg );
}
if ( SUCCEEDED(hr) ) {
lstrcpy ( pszNext, _T("StatisticStatus") );
hr = IntegerToPropertyBag ( pIPropBag, szPropertyName, lStatus );
}
}
}
if ( SUCCEEDED(hr) ) {
INT i;
LPTSTR pszTemp;
HRESULT hrConvert = S_OK;
double dblValue;
DWORD dwTmpStat;
DWORD dwCurrentDataLength;
DWORD dwTempLength;
LPTSTR pszDataNext;
lstrcpy ( pszData, _T("") );
dwCurrentDataLength = 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 );
pszTemp = W2T( vValue.bstrVal);
dwTempLength = lstrlen ( pszTemp );
// Extra TCHAR for NULL terminator
if ( dwTempLength + dwCurrentDataLength + sizeof(TCHAR) > dwMaxStrLen ) {
TCHAR* pszNewData;
dwMaxStrLen *= 2;
// Allocate a new buffer
pszNewData = new TCHAR[ dwMaxStrLen ];
if ( NULL != pszNewData ) {
memcpy ( pszNewData, pszData, dwCurrentDataLength * sizeof (TCHAR) );
delete pszData;
pszData = pszNewData;
pszDataNext = pszData;
} else {
hr = E_OUTOFMEMORY;
}
}
if ( SUCCEEDED(hr)) {
if ( i > 0 ) {
lstrcpy ( pszDataNext, _T("\t") );
dwCurrentDataLength += 1; // char count for _T("\t");
pszDataNext += 1;
}
memcpy ( pszDataNext, pszTemp, dwTempLength * sizeof(TCHAR) );
dwCurrentDataLength += dwTempLength;
pszDataNext += dwTempLength;
}
VariantClear( &vValue );
}
lstrcpy ( pszDataNext, _T("") );
}
lstrcpy ( pszNext, _T("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;
TCHAR szCounterName[16];
TCHAR szPropertyName[16+16];
OLE_COLOR clrValue;
INT iValue;
LPTSTR pszData = NULL;
int iBufSizeCurrent = 0;
int iBufSize;
USES_CONVERSION
_stprintf ( szCounterName, _T("%s%05d."), _T("Counter"), iIndex );
// Read visual properties
lstrcpy ( szPropertyName, szCounterName );
lstrcat ( szPropertyName, _T("Color") );
hr = OleColorFromPropertyBag ( pIPropBag, pIErrorLog, szPropertyName, clrValue );
if ( SUCCEEDED(hr) ) {
hr = put_Color ( clrValue );
}
lstrcpy ( szPropertyName, szCounterName );
lstrcat ( szPropertyName, _T("Width") );
hr = IntegerFromPropertyBag ( pIPropBag, pIErrorLog, szPropertyName, iValue );
if ( SUCCEEDED(hr) ) {
hr = put_Width ( iValue );
}
lstrcpy ( szPropertyName, szCounterName );
lstrcat ( szPropertyName, _T("LineStyle") );
hr = IntegerFromPropertyBag ( pIPropBag, pIErrorLog, szPropertyName, iValue );
if ( SUCCEEDED(hr) ) {
hr = put_LineStyle ( iValue );
}
lstrcpy ( szPropertyName, szCounterName );
lstrcat ( szPropertyName, _T("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) ) {
lstrcpy ( szPropertyName, szCounterName );
lstrcat ( szPropertyName, _T("Data") );
iBufSize = iBufSizeCurrent;
hr = StringFromPropertyBag (
pIPropBag,
pIErrorLog,
szPropertyName,
pszData,
iBufSize );
if ( SUCCEEDED(hr) &&
iBufSize > iBufSizeCurrent ) {
if ( NULL != pszData ) {
delete pszData;
}
pszData = new TCHAR[ iBufSize ];
if ( NULL == pszData ) {
hr = E_OUTOFMEMORY;
} else {
lstrcpy ( pszData, _T("") );
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;
TCHAR* pNextData;
TCHAR* 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.
lstrcpy ( szPropertyName, szCounterName );
lstrcat ( szPropertyName, _T("Maximum") );
hr = DoubleFromPropertyBag ( pIPropBag, pIErrorLog, szPropertyName, m_dFmtMax );
lstrcpy ( szPropertyName, szCounterName );
lstrcat ( szPropertyName, _T("Minimum") );
hr = DoubleFromPropertyBag ( pIPropBag, pIErrorLog, szPropertyName, m_dFmtMin );
lstrcpy ( szPropertyName, szCounterName );
lstrcat ( szPropertyName, _T("Average") );
hr = DoubleFromPropertyBag ( pIPropBag, pIErrorLog, szPropertyName, m_dFmtAvg );
lstrcpy ( szPropertyName, szCounterName );
lstrcat ( szPropertyName, _T("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;
TCHAR achPath[MAX_PATH];
PDH_COUNTER_INFO ci;
DWORD size;
PHIST_CONTROL pHistCtrl = m_pCtrl->HistoryControl();
// Can't add if already in query
if (m_hCounter != NULL)
return E_FAIL;
// 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)
return E_OUTOFMEMORY;
// Clear the statistics
m_dLogMax = 0.0;
m_dLogMin = 0.0;
m_dLogAvg = 0.0;
}
else {
// else allocate raw value space
m_pRawCtr = new PDH_RAW_COUNTER[pHistCtrl->nMaxSamples];
if ( NULL == m_pRawCtr )
return E_OUTOFMEMORY;
// Clear all status flags
for (i=0; i < pHistCtrl->nMaxSamples; i++)
m_pRawCtr[i].CStatus = PDH_CSTATUS_INVALID_DATA;
}
}
// Create the counter object
FormPath(achPath, FALSE);
hr = PdhAddCounter(hQuery, achPath, 0, &hCounter);
if (IsErrorSeverity(hr)) {
delete m_pRawCtr;
m_pRawCtr = NULL;
return hr;
}
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;
return NOERROR;
}
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 sample
if (m_pCtrl->IsLogSource()) {
*pdValue = m_pLogData[pHistCtrl->nSamples - iIndex - 1].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) {
*pdMax = m_dLogMax;
*pdMin = m_dLogMin;
*pdAvg = m_dLogAvg;
*plStatus = PDH_CSTATUS_VALID_DATA;
} else {
*plStatus = PDH_CSTATUS_INVALID_DATA;
}
} 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(REFIID riid
, LPVOID *ppv)
{
*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)
return E_OUTOFMEMORY;
m_pImpIDispatch->SetInterface(DIID_DICounterItem, this);
*ppv = m_pImpIDispatch;
}
else
{
*ppv = m_pImpIDispatch;
}
}
else
{
return E_NOINTERFACE;
}
((LPUNKNOWN)*ppv)->AddRef();
return NOERROR;
}
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 hReturn;
hReturn = OleTranslateColor(Color, NULL, &rgbColor);
if ( S_OK == hReturn ) {
m_rgbColor = rgbColor;
InvalidatePen();
InvalidateBrush();
}
return hReturn;
}
STDMETHODIMP CGraphItem::get_Color (
OUT OLE_COLOR *pColor
)
{
*pColor = m_rgbColor;
return NOERROR;
}
// Get/Put Width
STDMETHODIMP CGraphItem::put_Width (
IN INT iWidthInPixels)
{
if ( ( iWidthInPixels > 0 ) && (iWidthInPixels <= NumWidthIndices() ) ) {
m_iWidth = iWidthInPixels;
InvalidatePen();
return NOERROR;
} else {
return E_INVALIDARG;
}
}
STDMETHODIMP CGraphItem::get_Width (
OUT INT* piWidthInPixels
)
{
*piWidthInPixels = m_iWidth;
return NOERROR;
}
// Get/Put Line Style
STDMETHODIMP CGraphItem::put_LineStyle (
IN INT iLineStyle
)
{
if ( ( iLineStyle >= 0 ) && (iLineStyle < NumStyleIndices() ) ) {
m_iStyle = iLineStyle;
InvalidatePen();
return NOERROR;
} else {
return E_INVALIDARG;
}
}
STDMETHODIMP CGraphItem::get_LineStyle (
OUT INT* piLineStyle
)
{
*piLineStyle = m_iStyle;
return NOERROR;
}
// 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
)
{
*piScaleFactor = m_iScaleFactor;
return NOERROR;
}
STDMETHODIMP CGraphItem::get_Path (
OUT BSTR* pstrPath
)
{
TCHAR achPath[MAX_PATH];
USES_CONVERSION
FormPath(achPath, FALSE);
*pstrPath = SysAllocString(T2W(achPath));
if ( NULL == *pstrPath ) {
return E_OUTOFMEMORY;
} else {
return NOERROR;
}
}
STDMETHODIMP CGraphItem::get_Value (
OUT double* pdValue
)
{
DWORD dwTmpStat;
double dValue;
// Convert PDH status to HRESULT
if (HistoryValue(0, &dValue, &dwTmpStat) != 0) {
dValue = -1.0;
}
else
if (!IsSuccessSeverity(dwTmpStat)) {
dValue = -1.0;
}
*pdValue = dValue;
return NOERROR;
}
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;
}
}
void
CGraphItem::FormPath (
LPTSTR pszPath,
BOOL fWildCard
)
{
LPTSTR pszNext = pszPath;
if ( m_fLocalMachine )
pszPath[0] = 0;
else
lstrcpy ( pszPath, Machine()->Name() );
pszNext = pszNext + lstrlen ( pszPath );
lstrcpy ( pszNext, _T("\\") );
pszNext += 1; // Length of _T("\\");
lstrcpy ( pszNext, Object()->Name() );
pszNext += lstrlen ( Object()->Name() );
if (fWildCard) {
lstrcpy ( pszNext,_T("(*)") );
pszNext += 3; // Length of _T("(*)");
}
else if ( Instance()->Name()[0] ) {
lstrcpy ( pszNext,_T("("));
pszNext += 1; // Length of _T("(");
lstrcpy ( pszNext,Instance()->Name() );
pszNext += lstrlen ( Instance()->Name() );
lstrcpy ( pszNext,_T(")") );
pszNext += 1; // Length of _T("(");
}
lstrcpy ( pszNext, _T("\\") );
pszNext += 1; // Length of _T("\\");
lstrcpy ( pszNext,Counter()->Name() );
}
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 (
TCHAR*& pszNext,
double& rdValue )
{
HRESULT hr = NOERROR;
TCHAR szValue[MAX_DOUBLE_TEXT_SIZE + 1];
INT iDataLen;
INT iLen;
VARIANT vValue;
rdValue = -1.0;
iDataLen = wcscspn (pszNext, L"\t");
iLen = min ( iDataLen, MAX_DOUBLE_TEXT_SIZE );
lstrcpyn ( szValue, pszNext, iLen + 1 );
szValue[iLen] = L'\0';
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;
}
pszNext += iDataLen + 1 ;
VariantClear( &vValue );
return hr;
}
BOOL
CGraphItem::IsRateCounter ( void )
{
BOOL bReturn = FALSE;
DWORD dwRateMask = PERF_TYPE_COUNTER | PERF_COUNTER_RATE;
DWORD dwFractionMask = PERF_TYPE_COUNTER | PERF_COUNTER_FRACTION;
if ( dwRateMask == ( m_CounterInfo.dwType & dwRateMask ) ) {
bReturn = TRUE;
}
else if ( dwFractionMask == ( m_CounterInfo.dwType & dwFractionMask ) ) {
bReturn = TRUE;
}
return bReturn;
}