HOWTO: Access the Performance Registry Under Windows 95

ID: Q174631

The information in this article applies to:

SUMMARY

Windows 95 comes with an application called System Monitor (Sysmon.exe) that provides users with a method to monitor system resources or performance data. Note that Sysmon.exe is not installed by default under Windows 95, but users can install it via the Control Panel applet named Add/Remove Programs, clicking the Windows Setup tab, and then selecting the System Monitor check box. System resources exist as a collection of objects where an object is a grouping of resources. Each resource within an object can be monitored and is known as a counter. Each object and counter form a unique pair from which performance data is collected. All the information required to perform System Monitor functionality is located in the Windows 95 registry.

The process for writing a Performance Monitor involves the following steps:

1. Determine which performance objects are currently active in the system.

2. Determine the characteristics of each performance object.

3. Determine which performance counters are currently active for each

   performance object.

4. Determine the characteristics of each performance counter.

5. Enable the collection of performance data.

6. Retrieve performance data for an object\counter pair.

7. Disable the collection of performance data.

In addition to monitoring a local Windows 95 machine, the above procedure of querying the registry to obtain system-monitoring data, can also be performed on a remote machine running Windows 95.

MORE INFORMATION

Step 1: Determine What Performance

Objects Are Currently Active in the System

To determine which objects are active in the system, start by enumerating the following registry key:

   HKEY_LOCAL_MACHINE\System\CurrentControlSet\control\PerfStats\Enum

Each registry key that is listed under this location is called an Object (for example, VFAT, KERNEL, etc.). The availability of objects is dynamic. Therefore, this location must be checked first before proceeding to collect performance data. The following sample code illustrates this concept:

Sample Code

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

   #define DIFFFLAGLEN 6
   #define PERFENUMKEY
              "System\\CurrentControlSet\\Control\\PerfStats\\Enum"

   char szCounter[MAX_PATH];
   char szObject[MAX_PATH];
   char szName[MAX_PATH];
   LPSTR lpszDescBuff = NULL;
   char szDiffFlag[DIFFFLAGLEN]; // can contain either "TRUE" or "FALSE"

   void main(void)
   {
    DWORD rc;
    DWORD dwIndex1, dwIndex2;
    DWORD dwType;
    DWORD dwBufSize;
    HKEY hKey;
    HKEY hObject;
    HKEY hCounter;

    rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE,
      PERFENUMKEY, 0, KEY_READ, &hKey);
    if (rc == ERROR_SUCCESS)
    {
      dwBufSize = MAX_PATH;
      dwIndex1 = 0;

      // enumerate objects
      while ( RegEnumKeyEx(hKey, dwIndex1++, szObject,
              &dwBufSize, NULL, NULL, NULL, NULL) == ERROR_SUCCESS )
      {
         if ( (rc=RegOpenKeyEx(hKey, szObject, 0, KEY_READ, &hObject))
                           == ERROR_SUCCESS)
         {
            // process information on the open key using hObject
         }
         RegCloseKey(hObject);
         dwBufSize = MAX_PATH;
      }
      RegCloseKey(hKey);
    }
    else
      printf("Error %d opening %s\n", rc, PERFENUMKEY);
   } // end main

Step 2: Determine the Characteristics of Each Performance Object

Each performance object key is characterized by one registry value. This registry value is of type REG_SZ and is called "Name." The "Name" registry key identifies the commonly used name of the object (for example, VFAT is know as "File System"). It is this "Name" that would be shown to an end user that is using the system monitor application. Use RegQueryValueEx() to obtain this registry value as shown in the following sample code:

Sample Code

   if ((rc=RegQueryValueEx(hObject, "Name", NULL, &dwType,
                           szObject, &dwBufSize)) == ERROR_SUCCESS)
   {
   }

Step 3: Determine what Performance Counters Are Active for Each perf Object

Associated with each performance object key identified above with hObject, there is one or more performance counters. Each performance counter exists as a subkey of a performance object key. The following sample code enumerates all these performance counters for a given performance object (hObject):

Sample Code

   // enumerate counters
   dwIndex2 = 0;
   dwBufSize = MAX_PATH;
   while(RegEnumKeyEx(hObject, dwIndex2++, szCounter, &dwBufSize,
             NULL, NULL, NULL, NULL) == ERROR_SUCCESS)
   {
      if ( (rc=RegOpenKeyEx(hObject, szCounter, 0, KEY_READ, &hCounter))
             == ERROR_SUCCESS)
      {
         // do processing on open counter key using hCounter
      }
      else
         printf("Error %d opening counter %s\n", rc, szCounter);

      RegCloseKey(hCounter);
      dwBufSize = MAX_PATH;
   }

Step 4: Determine the Characteristics of Each Performance Counter

Similar to a performance object, each performance counter is characterized by three registry values. Each of these registry values is identified by "Name" and is of type REG_SZ. This "Name" value identifies the commonly used name of the counter (for example, CPUUsage is know as "Processor Usage (%)") and would be displayed to a user. A second REG_SZ value called "Description" describes the counter in more detail. You can use this description to provide more detail to the user. And finally, there is a third REG_SZ value called "Differentiate" that is either "TRUE" or "FALSE." If "Differentiate" is set to "FALSE" then the data retrieved is used in its raw form. If "Differentiate" is "TRUE" the data retrieved for this counter must be differentiated over time as follows:

               Counter_Value2 - Counter_Value1
           -----------------------------------------
                 Time_Taken2 - Time_Taken1

The following code illustrates this procedure:

Sample Code

   // First, get the Name of the performance counter
   dwBufSize = MAX_PATH;
   rc = RegQueryValueEx(hObject, "Name", NULL, &dwType, szName,
                           &dwBufSize);

   // Second, get the Description
   dwBufSize = 0;
   RegQueryValueEx(hCounter, "Description", NULL, &dwType, NULL,
                   &dwBufSize);
   lpszDescBuff = LocalAlloc(LPTR, dwBufSize + 1);
   rc = RegQueryValueEx(hCounter, "Description", NULL, &dwType,
                           lpszDescBuff, &dwBufSize);

   if (ERROR_SUCCESS == rc)
   {
      printf("%s: %s\n", szCounter, lpszDescBuff);
      LocalFree(lpszDescBuff);
   }

   // get the Differentiate value
   dwBufSize = DIFFFLAGLEN;
   rc = RegQueryValueEx(hCounter, "Differentiate", NULL, &dwType,
                           szDiffFlag, &dwBufSize);
   if (ERROR_SUCCESS == rc)
   {
      printf("Differentiating Counter: %s\n\n", szDiffFlag);
   }

Step 5: Enable the Collection of Performance Data

Together, the performance object key name and counter subkey name pair is used to initialize performance data collection. To request the beginning of a performance data collection for any performance object\counter pair, use the object\counter pair to query the registry value located at HKEY_DYN_DATA\PerfStats\StartStat using RegQueryValueEx. For example, to start performance data collection on KERNEL\CPUUsage, a registry query on KERNEL\CPUUsage at location HKEY_DYN_DATA\PerfStats\StartStat is performed. If no requests to start data collection are issued, the performance data retrieved for KERNEL\CPUUsage is always 100%. Once a request to start performance data collection is issued, the data retrieved reflects the actual CPU utilization.

Sample Code

   BOOL EnableDataCollection(char *szObjName, char *szCounterName)
   {
    HKEY hOpen;
    DWORD cbData;
    DWORD dwType;
    LPBYTE pByte;
    DWORD rc;
    char *szCounterData;
    BOOL bSuccess = TRUE;

    if ( (rc = RegOpenKeyEx(HKEY_DYN_DATA,"PerfStats\\StartStat", 0,
                           KEY_READ, &hOpen)) == ERROR_SUCCESS)
    {
      // concatenate the object and couter key names
      szCounterData = LocalAlloc(LPTR,
             lstrlen(szObjName) + lstrlen(szCounterName) + 2);
      wsprintf(szCounterData, "%s\\%s", szObjName, szCounterName);

      // query to get data size
      if ( (rc = RegQueryValueEx(hOpen,szCounterData,NULL,&dwType,
             NULL, &cbData )) == ERROR_SUCCESS )
      {
         pByte = LocalAlloc(LPTR, cbData);

         // query the performance start key to initialize performance data
         // query the start key to initialize performance data
         rc = RegQueryValueEx(hOpen,szCounterData,NULL,&dwType, pByte,
                              &cbData );
         // at this point we don't do anything with the data
         //  free up resources
         LocalFree(pByte);
      }
      else
         bSuccess = FALSE;

      RegCloseKey(hOpen);
      LocalFree(szCounterData);
    }
    else
      bSuccess = FALSE;

    return bSuccess;
   }

Step 6: Retrieve Performance Data for an object\counter Pair

Once performance data collection is initialized, each performance object key and counter subkey pair is again used to retrieve performance data from the registry key HKEY_DYN_DATA\PerfStats\StartData using RegQueryValueEx (for example, KERNEL\CPUUsage, KERNEL\Threads, etc.). The performance data retrieved using RegQueryValueEx is always of type DWORD (for example, 4 bytes).

Sample Code

   BOOL CollectPerfData(char *szObjName, char *szCounterName,
                        DWORD *pdwData)
   {
    HKEY hOpen;
    DWORD dwType;
    DWORD cbData;
    char *szCounterData;
    DWORD rc;
    BOOL bSuccess = TRUE;

    // concatenate the object and counter keys
    szCounterData = LocalAlloc(LPTR,
           lstrlen(szObjName) + lstrlen(szCounterName) + 2);
    wsprintf(szCounterData, "%s\\%s", szObjName, szCounterName);

    // open performance data key
    if ( (rc = RegOpenKeyEx(HKEY_DYN_DATA,"PerfStats\\StatData", 0,
         KEY_READ, &hOpen)) == ERROR_SUCCESS)
    {
      cbData = sizeof(DWORD);
      // retrieve performance data which is of size of DWORD
      rc=RegQueryValueEx(hOpen,szCounterData,0,&dwType,
            (LPBYTE)pdwData,&cbData );

      // let the caller know if it worked or not
      bSuccess = rc == ERROR_SUCCESS;
      RegCloseKey(hOpen);
    }
    else
      bSuccess = FALSE;

    // free the resources
    LocalFree(szCounterData);
    return bSuccess;
   }

Step 7: Disable the Collection of Performance Data

After performance data retrieval is completed, performance data collection must be de-initialized or turned off to allow system resources to be released. To request the end of performance data collection for any object\counter pair, the object\counter pair is used to query the registry value located at HKEY_DYN_DATA\PerfStats\StopStat using RegQueryValueEx(). For example, to request performance data collection end for KERNEL\CPUUsage, RegQueryValueEx() is used to query the registry value KERNEL\CPUUsage at location HKEY_DYN_DATA\PerfStats\StopStat.

Sample Code

   BOOL DisableDataCollection(char *szObjName, char *szCounterName)
   {
    HKEY hOpen;
    LPBYTE pByte;
    DWORD cbData;
    DWORD dwType;
    DWORD rc;
    char *szCounterData;
    BOOL bSuccess = TRUE;

    if ( (rc = RegOpenKeyEx(HKEY_DYN_DATA,"PerfStats\\StopStat", 0,
                           KEY_READ, &hOpen)) == ERROR_SUCCESS)
    {
      // concatenate the object and couter key names
      szCounterData = LocalAlloc(LPTR,
            lstrlen(szObjName) + lstrlen(szCounterName) + 2);
      wsprintf(szCounterData, "%s\\%s", szObjName, szCounterName);

      // query to get data size
      if ( (rc = RegQueryValueEx(hOpen,szCounterData,NULL,&dwType,
            NULL, &cbData )) == ERROR_SUCCESS )
      {
         pByte = LocalAlloc(LPTR, cbData);

         // query the performance start key to initialize performance data
         // query the start key to initialize performance data
         rc = RegQueryValueEx(hOpen,szCounterData,NULL,&dwType, pByte,
                              &cbData );
         // at this point we don't do anything with the data
         // free up resources
         LocalFree(pByte);

      }
      else
         bSuccess = FALSE;

      RegCloseKey(hopen);
      LocalFree(szCounterData);
    }
    else
      bSuccess = FALSE;

    return bSuccess;
   }

Step 8: Remote System Monitoring

Since performance data is part of the registry and since RegConnectRegistry() enables one Windows 95 machine to read and write to a remote registry, system monitoring can be performed on a remote Windows 95 machine. Follow these steps to enable this functionality:

1. Install the "Remote Registry Service" on all Windows 95 machines.

2. Use RegConnectRegistry to retrieve handles to HKEY_LOCAL_MACHINE and

   HKEY_DYN_DATA from the remote Windows 95 target, as shown below:

    RegConnectRegistry(szComputerName,     // name of remote computer
                   HKEY_LOCAL_MACHINE, // predefined registry handle
                   &hkRemoteMachine);  // buffer for remote registry handle

3. Use the handle returned by RegConnectRegistry as replacements for
   HKEY_LOCAL_MACHINE and HKEY_DYN_DATA, as shown below:

    RegConnectRegistry(szComputerName, // name of remote computer
                   HKEY_DYN_DATA,  // predefined registry handle
                   &hkDynaData);   // buffer for remote registry handle

4. Perform the steps as described above.

REFERENCES

Since performance monitoring is such a valuable tool, Windows 95 has provided a method by which additional performance objects can be added to the registry. In Windows 95, performance data is provided by a collection of VxDs. It is the job of these VxDs to provide the performance data upon request as the registry is queried. To get more information on how to providing performance objects, consult the Device Driver Kit (DDK) documentation.

Additional query words:

Keywords          : kbKernBase kbPerfMon kbRegistry kbGrpKernBase 
Version           : WINNT:
Platform          : winnt
Issue type        : kbhowto

Last Reviewed: July 1, 1998