Leaked source code of windows server 2003
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.
 
 
 
 
 
 

1954 lines
48 KiB

/*++
Copyright (C) 1996-1999 Microsoft Corporation
Module Name:
grphitem.cpp
Abstract:
<abstract>
--*/
#ifndef _LOG_INCLUDE_DATA
#define _LOG_INCLUDE_DATA 0
#endif
#include "polyline.h"
#include <strsafe.h>
#include <math.h>
#include <limits.h> // for INT_MAX
#include <pdhp.h>
#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;
}