Figure 1 CPerfTitleDatabase
CTITLEDB.H
#ifndef __Ctitledb_h__
#define __Ctitledb_h__
enum PERFORMANCE_TITLE_TYPE { PERF_TITLE_COUNTER, PERF_TITLE_EXPLAIN };
class CPerfTitleDatabase
{
private:
unsigned m_nLastIndex;
PTSTR * m_TitleStrings;
PTSTR m_pszRawStrings;
public:
CPerfTitleDatabase( PERFORMANCE_TITLE_TYPE titleType );
~CPerfTitleDatabase( );
unsigned GetLastTitleIndex(void) { return m_nLastIndex; }
PTSTR GetTitleStringFromIndex( unsigned index );
unsigned GetIndexFromTitleString( PTSTR pszTitleString );
};
#endif
CTITLEDB.CPP
//===========================================================================
// File: CTITLEDB.CPP
// Author: Matt Pietrek
// From: Microsoft Systems Journal, "Under the Hood", March 1996
//===========================================================================
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <stdlib.h>
#include <winperf.h>
#include <tchar.h>
#pragma hdrstop
#include "ctitledb.h"
CPerfTitleDatabase::CPerfTitleDatabase(
PERFORMANCE_TITLE_TYPE titleType )
{
m_nLastIndex = 0;
m_TitleStrings = 0;
m_pszRawStrings = 0;
// Determine the appropriate strings to pass to RegOpenKeyEx
PTSTR psz009RegValue, pszLastIndexRegValue;
if ( PERF_TITLE_COUNTER == titleType )
{
psz009RegValue = TEXT("Counter 009");
pszLastIndexRegValue = TEXT("Last Counter");
}
else if ( PERF_TITLE_EXPLAIN == titleType )
{
psz009RegValue = TEXT("Explain 009");
pszLastIndexRegValue = TEXT("Last Help");
}
else
return;
// Find out the max number of entries
HKEY hKeyPerflib = 0;
DWORD cbLastIndex;
// Open the registry key that has the values for the maximum number
// of title strings
if ( ERROR_SUCCESS != RegOpenKeyEx(
HKEY_LOCAL_MACHINE,
TEXT("software\\microsoft\\windows nt\\currentversion\\perflib"),
0, KEY_READ, &hKeyPerflib ) )
return;
// Read in the number of title strings
if ( ERROR_SUCCESS != RegQueryValueEx(
hKeyPerflib, pszLastIndexRegValue, 0, 0,
(PBYTE)&m_nLastIndex, &cbLastIndex ) )
{
RegCloseKey( hKeyPerflib );
return;
}
RegCloseKey( hKeyPerflib );
//
// Now go find and process the raw string data
//
// Determine how big the raw data in the REG_MULTI_SZ value is
DWORD cbTitleStrings;
if ( ERROR_SUCCESS != RegQueryValueEx( HKEY_PERFORMANCE_DATA, psz009RegValue, 0,0,0, &cbTitleStrings))
return;
// Allocate memory for the raw registry title string data
m_pszRawStrings = new TCHAR[cbTitleStrings];
// Read in the raw title strings
if ( ERROR_SUCCESS != RegQueryValueEx( HKEY_PERFORMANCE_DATA,
psz009RegValue, 0, 0, (PBYTE)m_pszRawStrings,
&cbTitleStrings ) )
{
delete []m_pszRawStrings;
return;
}
// allocate memory for an array of string pointers.
m_TitleStrings = new PTSTR[ m_nLastIndex+1 ];
if ( !m_TitleStrings )
{
delete []m_pszRawStrings;
return;
}
// Initialize the m_TitleStrings to all NULLs, since there may
// be some array slots that aren't used.
memset( m_TitleStrings, 0, sizeof(PTSTR) * (m_nLastIndex+1) );
// The raw data entries are an ASCII string index (e.g., "242"), followed
// by the corresponding string. Fill in the appropriate slot in the
// m_TitleStrings array with the pointer to the string name. The end
// of the list is indicated by a double NULL.
PTSTR pszWorkStr = (PTSTR)m_pszRawStrings;
unsigned cbCurrStr;
// While the length of the current string isn't 0...
while ( 0 != (cbCurrStr = lstrlen( pszWorkStr)) )
{
// Convert the first string to a binary representation
unsigned index = _ttoi( pszWorkStr ); // _ttoi -> atoi()
if ( index > m_nLastIndex )
break;
// Now point to the string following it. This is the title string
pszWorkStr += cbCurrStr + 1;
// Fill in the appropriate slot in the title strings array.
m_TitleStrings[index] = pszWorkStr;
// Advance to the next index/title pair
pszWorkStr += lstrlen(pszWorkStr) + 1;
}
}
CPerfTitleDatabase::~CPerfTitleDatabase( )
{
delete []m_TitleStrings;
m_TitleStrings = 0;
delete []m_pszRawStrings;
m_pszRawStrings = 0;
m_nLastIndex = 0;
}
PTSTR
CPerfTitleDatabase::GetTitleStringFromIndex( unsigned index )
{
if ( index > m_nLastIndex ) // Is index within range?
return 0;
return m_TitleStrings[ index ];
}
unsigned
CPerfTitleDatabase::GetIndexFromTitleString( PTSTR pszTitleString )
{
if ( IsBadStringPtr(pszTitleString, 0xFFFFFFFF) )
return 0;
// Loop through all the non-null string array entries, doing a case-
// insensitive comparison. If found, return the correpsonding index
for ( unsigned i = 1; i <= m_nLastIndex; i++ )
{
if ( m_TitleStrings[i] )
if ( 0 == _tcsicmp( pszTitleString, m_TitleStrings[i] ) )
return i;
}
return 0;
}
Figure 2 SHOWTITL.CPP
//====================================================
// File: SHOWTITL.CPP
// Author: Matt Pietrek
// From: Microsoft Systems Journal
// "Under the Hood", March 1996
// To Build: CL [arguments] SHOWTITL.CPP CTITLEDB.CPP
//====================================================
#include <windows.h>
#include <stdio.h>
#include <tchar.h>
#pragma hdrstop
#include "ctitledb.h"
int main()
{
CPerfTitleDatabase counterTitles( PERF_TITLE_COUNTER );
CPerfTitleDatabase explainTitles( PERF_TITLE_EXPLAIN );
unsigned i;
PTSTR psz;
// Display the counter title strings
for ( i = 0; i < counterTitles.GetLastTitleIndex(); i++ )
{
psz = counterTitles.GetTitleStringFromIndex( i );
if ( psz )
_tprintf( TEXT("%u %s\n"), i, psz );
}
// Display the "Explain" (help) title strings
for ( i = 0; i < explainTitles.GetLastTitleIndex(); i++ )
{
psz = explainTitles.GetTitleStringFromIndex( i );
if ( psz )
_tprintf( TEXT("%u %s\n"), i, psz );
}
return 0;
}
Figure 3 CPerfSnapshot
PERFSNAP.H
#ifndef __Perfsnap_h__
#define __Perfsnap_h__
#ifndef _WINPERF_
#include <winperf.h>
#endif
class CPerfTitleDatabase;
class CPerfSnapshot
{
private:
PPERF_DATA_BLOCK m_pPerfDataHeader; // Points to snapshot data
CPerfTitleDatabase * m_pCounterTitles; // The title conversion object
// Private function to convert the ASCII strings passedto TakeSnapshot()
// into a suitable form for the RegQueryValue call
BOOL ConvertSnapshotItemName( PTSTR pszIn, PTSTR pszOut, DWORD nSize );
public:
CPerfSnapshot( CPerfTitleDatabase * pCounterTitles );
~CPerfSnapshot( void );
BOOL TakeSnapshot( PTSTR pszSnapshotItems );
void DisposeSnapshot( void );
DWORD GetNumObjectTypes( void ); // # of objects the snapshot includes
BOOL GetSystemName( PTSTR pszSystemName, DWORD nSize );
PVOID GetPostHeaderPointer( void ); // Pointer to data following header
};
#endif
PERFSNAP.CPP
//============================================================================
// File: PERFSNAP.CPP
// Author: Matt Pietrek
// From: Microsoft Systems Journal, "Under the Hood", March 1996
//============================================================================
#ifndef WIN32_LEAN_AND_MEAN
#define WIN32_LEAN_AND_MEAN
#endif
#include <windows.h>
#include <winperf.h>
#include <stdlib.h>
#include <tchar.h>
#pragma hdrstop
#include "ctitledb.h"
#include "perfsnap.h"
#include "makeptr.h"
CPerfSnapshot::CPerfSnapshot(
CPerfTitleDatabase * pCounterTitles )
{
m_pPerfDataHeader = 0;
m_pCounterTitles = pCounterTitles;
}
CPerfSnapshot::~CPerfSnapshot( void )
{
DisposeSnapshot();
}
BOOL
CPerfSnapshot::TakeSnapshot( PTSTR pszSnapshotItems )
{
DisposeSnapshot(); // Clear out any current snapshot
// Convert the input string (e.g., "Process") into the form required
// by the HKEY_PERFORMANCE_DATA key (e.g., "232")
TCHAR szConvertedItemNames[ 256 ];
if ( !ConvertSnapshotItemName( pszSnapshotItems, szConvertedItemNames,
sizeof(szConvertedItemNames) ) )
return FALSE;
DWORD cbPerfInfo = 0, cbAllocSize = 0;
LONG retValue;
m_pPerfDataHeader = 0;
while ( 1 ) // Loop until we get the data, or we fail unexpectedly
{
retValue = RegQueryValueEx( HKEY_PERFORMANCE_DATA,
szConvertedItemNames, 0, 0,
(PBYTE)m_pPerfDataHeader, &cbPerfInfo );
if ( retValue == ERROR_SUCCESS ) // We apparently got the snapshot
{
// Verify that the signature is a unicode "PERF"
if ( memcmp( m_pPerfDataHeader->Signature, L"PERF", 8 ) )
break;
return TRUE;
}
else if ( retValue != ERROR_MORE_DATA ) // Anything other failure
break; // code means something
// bad happened, so bail out.
// If we get here, our buffer wasn't big enough. Delete it and
// try again with a bigger buffer.
delete []m_pPerfDataHeader;
// The new buffer size will be 4096 bytes bigger than the larger
// of: 1) The previous allocation size, or 2) The size that the
// RegQueryValueEx call said was necessary.
if ( cbPerfInfo > cbAllocSize )
cbAllocSize = cbPerfInfo + 4096;
else
cbAllocSize += 4096;
// Allocate a new, larger buffer in preparation to try again.
m_pPerfDataHeader = (PPERF_DATA_BLOCK) new BYTE[ cbAllocSize ];
if ( !m_pPerfDataHeader )
break;
cbPerfInfo = cbAllocSize;
}
// If we get here, the RegQueryValueEx failed unexpectedly.
delete []m_pPerfDataHeader;
m_pPerfDataHeader = 0;
return FALSE;
}
void
CPerfSnapshot::DisposeSnapshot( void )
{
delete m_pPerfDataHeader;
m_pPerfDataHeader = 0;
}
DWORD
CPerfSnapshot::GetNumObjectTypes( void )
{
return m_pPerfDataHeader ? m_pPerfDataHeader->NumObjectTypes: 0;
}
BOOL
CPerfSnapshot::GetSystemName( PTSTR pszSystemName, DWORD nSize )
{
if ( !m_pPerfDataHeader ) // If no snapshot data, bail out.
return FALSE;
// Make sure the input buffer size is big enough
if ( nSize < m_pPerfDataHeader->SystemNameLength )
return FALSE;
// Make a unicode string point to the system name string
// that's stored in the PERF_DATA_BLOCK
LPWSTR lpwszName = MakePtr( LPWSTR, m_pPerfDataHeader,
m_pPerfDataHeader->SystemNameOffset );
#ifdef UNICODE // Copy the PERF_DATA_BLOCK string to the input buffer
lstrcpy( pszSystemName, lpwszName );
#else
wcstombs( pszSystemName, lpwszName, nSize );
#endif
return TRUE;
}
PVOID
CPerfSnapshot::GetPostHeaderPointer( void )
{
// Returns a header to the first byte following the PERF_DATA_BLOCK
// (including the variable length system name string at the end)
return m_pPerfDataHeader ?
MakePtr(PVOID, m_pPerfDataHeader,m_pPerfDataHeader->HeaderLength)
: 0;
}
BOOL
CPerfSnapshot::ConvertSnapshotItemName( PTSTR pszIn,
PTSTR pszOut, DWORD nSize )
{
if ( IsBadStringPtr( pszIn, 0xFFFFFFFF ) )
return FALSE;
PTSTR pszCopy = _tcsdup( pszIn ); // Need a copy because _tcstok will
// modify the string it processes
PTSTR pszCopy2 = pszCopy, pszToken;
PTSTR pszOut2 = pszOut;
// For each token (' ' delimited) from the input string, see if it
// corresponds to a counter title. If so, emit the title index as
// in ASCII form to the output string. If the input token isn't a
// counter title, pass it through unmodified to the output string.
// This allows things like "Global" and "Costly" to go through unscathed.
while ( pszToken = _tcstok(pszCopy2, TEXT(" ")) ) // _tcstok == strtok
{
DWORD objectID = m_pCounterTitles->GetIndexFromTitleString(pszToken);
// Needs to check output string len!
if ( objectID )
pszOut2 += wsprintf( pszOut2, TEXT("%u "), objectID );
else
pszOut2 += wsprintf( pszOut2, TEXT("%s "), pszToken );
pszCopy2 = 0; // Set to 0 for 2nd and later passes through the loop
}
free( pszCopy );
return TRUE;
}
MAKETPR.H
// MakePtr is a macro that allows you to easily add two values (including
// pointers) together without dealing with C's pointer arithmetic. It
// essentially treats the last two parameters as DWORDs. The first
// parameter is used to typecast the result to the appropriate pointer type.
#define MakePtr(cast, ptr, addValue) (cast)( (DWORD)(ptr) + (DWORD)(addValue))