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))