Retrieving Counter Data from the Registry

Last reviewed: January 15, 1997
Article ID: Q107728
The information in this article applies to:
  • Microsoft Win32 Application Programming Interface (API) included with: - Microsoft Windows NT, versions 3.1, 3.5, 3.51, 4.0

SUMMARY

The performance data begins with a structure of type PERF_DATA_BLOCK and is followed by PERF_DATA_BLOCK.NumObjectTypes data blocks. Each data block begins with a structure of type PERF_OBJECT_TYPE, followed by PERF_OBJECT_TYPE.NumCounters structures of type PERF_COUNTER_DEFINITION. Next, there are PERF_OBJECT_TYPE.NumInstances structures of type PERF_INSTANCE DEFINITION, each directly followed by an instance name, a structure of type PERF_COUNTER_BLOCK and PERF_OBJECT_TYPE.NumCounters counters. All of these data types are described in WINPERF.H.

MORE INFORMATION

The following steps are used to retrieve all of the counter information from the registry:

  1. Allocate a buffer to obtain the performance data. For single objects, you may need at little as 1.5K. For global objects, you may need as much as 50K. Call RegQueryValueEx() to obtain the data. If the call returns ERROR_MORE_DATA, then the buffer size was not big enough. Increase the size of the buffer and try again. Repeat until the call is successful.

  2. Get the first object type in PerfObj.

  3. Get the first instance.

  4. Get the first counter and its data. At this point, if the object has no instances, the next thing will be a pointer to a single PERF_COUNTER_BLOCK; otherwise, the next thing will be a pointer to the first PERF_INSTANCE_DEFINITION. Check PerfObj->NumInstances to find out how many instances there are.

  5. Get the next counter and its data. Repeat for all PerfObj->NumCounters counters.

  6. After all counters are retrieved for the instance, get the next instance and all its counters. Repeat for all PerfObj->NumInstances instances.

  7. After all instances of the object type are retrieved, move to the next object type and repeat steps 3 - 7.

Sample Code

#include <windows.h>
#include <stdio.h>
#include <malloc.h>

#define TOTALBYTES    8192
#define BYTEINCREMENT 1024

void main()
{
   PPERF_DATA_BLOCK PerfData = NULL;
   PPERF_OBJECT_TYPE PerfObj;
   PPERF_INSTANCE_DEFINITION PerfInst;
   PPERF_COUNTER_DEFINITION PerfCntr, CurCntr;
   PPERF_COUNTER_BLOCK PtrToCntr;
   DWORD BufferSize = TOTALBYTES;
   DWORD i, j, k;

// Allocate the buffer.
   PerfData = (PPERF_DATA_BLOCK) malloc( BufferSize );

   while( RegQueryValueEx( HKEY_PERFORMANCE_DATA,
                           "Global",
                           NULL,
                           NULL,
                           (LPBYTE) PerfData,
                           &BufferSize ) == ERROR_MORE_DATA )
   {
   // Get a buffer that is big enough.
      BufferSize += BYTEINCREMENT;
      PerfData = (PPERF_DATA_BLOCK) realloc( PerfData, BufferSize );
   }

// Get the first object type.
   PerfObj = (PPERF_OBJECT_TYPE)((PBYTE)PerfData +
      PerfData->HeaderLength);

// Process all objects.
   for( i=0; i < PerfData->NumObjectTypes; i++ )
   {
      printf( "\nObject: %ld\n", PerfObj->ObjectNameTitleIndex );

   // Get the counter block.
      PerfCntr = (PPERF_COUNTER_DEFINITION) ((PBYTE)PerfObj +
                 PerfObj->HeaderLength);

      if( PerfObj->NumInstances > 0 )
      {
      // Get the first instance.
         PerfInst = (PPERF_INSTANCE_DEFINITION)((PBYTE)PerfObj +
                    PerfObj->DefinitionLength);

      // Retrieve all instances.
         for( k=0; k < PerfObj->NumInstances; k++ )
         {
            printf( "\n\tInstance: %S\n", (char *)((PBYTE)PerfInst +
                    PerfInst->NameOffset) );
            CurCntr = PerfCntr;

         // Get the first counter.
            PtrToCntr = (PPERF_COUNTER_BLOCK)((PBYTE)PerfInst +
                        PerfInst->ByteLength);

         // Retrieve all counters.
            for( j=0; j < PerfObj->NumCounters; j++ )
            {
               printf("\t\tCounter: %ld\n",CurCntr->CounterNameTitleIndex);
            // Data is (LPVOID)((PBYTE)PtrToCntr + CurCntr->CounterOffset);

            // Get next counter.
               CurCntr =  (PPERF_COUNTER_DEFINITION)((PBYTE)CurCntr +
                          CurCntr->ByteLength);

            }

         // Get the next instance.
            PerfInst = (PPERF_INSTANCE_DEFINITION)((PBYTE)PtrToCntr +
                       PtrToCntr->ByteLength);
         }
      }
      else
      {
      // Get the first counter.
         PtrToCntr = (PPERF_COUNTER_BLOCK) ((PBYTE)PerfObj +
                     PerfObj->DefinitionLength );

      // Retrieve all counters.
         for( j=0; j < PerfObj->NumCounters; j++ )
         {
            printf( "\tCounter: %ld\n", PerfCntr->CounterNameTitleIndex );

         // Data is (LPVOID)((PBYTE)PtrToCntr + PerfCntr->CounterOffset);

            PerfCntr = (PPERF_COUNTER_DEFINITION)((PBYTE)PerfCntr +
                       PerfCntr->ByteLength);
         }
      }

      // Get the next object type.
      PerfObj = (PPERF_OBJECT_TYPE)((PBYTE)PerfObj +
                PerfObj->TotalByteLength);
   }
}

Note that the instance names are retrieved in a fashion that is similar to retrieving the data.

The steps above showed how to obtain all of the counters. You can retrieve only the counters that pertain to a particular object by using the titles database. The information is stored in the registry in the format index, name, index, name, and so forth.

To retrieve the titles database and store it in TitlesDatabase:

  1. Open the key:

          RegOpenKeyEx( HKEY_LOCAL_MACHINE,
    
             "Software\\Microsoft\\Windows NT\\CurrentVersion\\Perflib\\009",
                    0,
             KEY_READ,
                &Hkey);
    
       Note that 009 is a language ID, so this value will be different on a
       non-English version of the operating system.
    
    

  2. Query the information from the key:

          RegQueryInfoKey(
    
                       Hkey,
             (LPTSTR) Class,
                 &ClassSize,
                       NULL,
                    &Subkey,
                  MaxSubKey,
                  &MaxClass,
                    &Values,
                   &MaxName,
                   &MaxData,
                   &SecDesc,
              &LastWriteTime );
    
    

  3. Allocate a buffer to store the information:

          TitlesDataBase = (PSTR) malloc( (MaxData+1) * sizeof(TCHAR) )
    

  4. Retrieve the data:

          RegQueryValueEx(     Hkey,
                (LPTSTR) "Counters",
                               NULL,
                               NULL,
            (LPBYTE) TitlesDataBase,
                           &MaxData );
    
    
Once you have the database, it is possible to write code that will go through all objects, searching by index (field ObjectNameTitleIndex) or by type (field ObjectNameTitle - which is initially NULL).

Or, you could obtain only the performance data for specified objects by changing the call to ReqQueryValueEx() in step 1 of the SUMMARY section above to:

   RegQueryValueEx( HKEY_PERFORMANCE_DATA,
                           Indices,
                              NULL,
                              NULL,
                          PerfData,
                        &BufferSize );

Note that the only difference here is that instead of specifying "Global" as the second parameter, you specify a string that represents the decimal value(s) for the object(s) of interest that are obtained from the titles database.

REFERENCES

The PVIEWER and PERFMON samples in the MSTOOLS\SAMPLES\SDKTOOLS\WINNT directory contain complete sample code that deals with performance data.

For more information, please see the "Performance Overview" in the Win32 SDK documentation and the volume titled "Optimizing Windows NT" in the Windows NT Resource Kit.


KBCategory: kbprg
KBSubcategory: BsePerfmon
Additional reference words: 3.10 3.50 3.51 4.00 Perfmon


THE INFORMATION PROVIDED IN THE MICROSOFT KNOWLEDGE BASE IS PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND. MICROSOFT DISCLAIMS ALL WARRANTIES, EITHER EXPRESS OR IMPLIED, INCLUDING THE WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE. IN NO EVENT SHALL MICROSOFT CORPORATION OR ITS SUPPLIERS BE LIABLE FOR ANY DAMAGES WHATSOEVER INCLUDING DIRECT, INDIRECT, INCIDENTAL, CONSEQUENTIAL, LOSS OF BUSINESS PROFITS OR SPECIAL DAMAGES, EVEN IF MICROSOFT CORPORATION OR ITS SUPPLIERS HAVE BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGES. SOME STATES DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES SO THE FOREGOING LIMITATION MAY NOT APPLY.

Last reviewed: January 15, 1997
© 1998 Microsoft Corporation. All rights reserved. Terms of Use.