Figure 5   Counter Value Strings

2 System
4 Memory
6 % Processor Time
10 File Read Operations/sec
12 File Write Operations/sec
14 File Control Operations/sec
16 File Read Bytes/sec
18 File Write Bytes/sec


Figure 6   Help Value Strings

3 The System object type includes those counters that apply to all processors on the computer collectively. the Se counters represent the activity of all processors on the computer.
5 The Memory object type includes those counters that describe the behavior of both real and virtual memory on the computer. Real memory is allocated in units of pages. Virtual memory may exceed real memory in size, causing page traffic as virtual pages are moved between disk and real memory.
7 Processor Time is expressed as a percentage of the elapsed time that a processor is busy executing a non-Idle thread. It can be viewed as the fraction of the time spent doing useful work. Each processor is assigned an Idle thread in the Idle process which consumes those unproductive processor cycles not used by any other threads.
9 % Total DPC Time is the sum of % DPC Time of all processors divided by the number of processors in the system. (See Processor: % DPC Time for detail.)
11 File Read Operations/sec is an aggregate of all the file system read operations on the computer.
13 File Write Operations/sec is an aggregate of all the file system write operations on the computer.
15 File Control Operations/sec is an aggregate of all file system operations that are neither reads nor writes. the Se operations usually include file system control requests or requests for information about device characteristics or status.
17 File Read Bytes/sec is an aggregate of the bytes transferred for all the file system read operations on the computer.
19 File Write Bytes/sec is an aggregate of the bytes transferred for all the file system write operations on the computer.


Figure 9   PERF_OBJECT_TYPE

Member Description
TotalByteLength, DefinitionLength, and HeaderLength Indicate byte sizes. the Se values must be initialized correctly so that a tool like PerfMon can walk the data block properly.
ObjectNameTitleIndex and ObjectHelpTitleIndex Indicate the numbers that were assigned to the Object text and its help when they were added to the registry.
ObjectNameTitle and ObjectHelpTitle Should always be set to NULL because the Se members are only used by the application requesting the data.
DetailLevel Indicates how hard to understand this object is to most users. Most objects are understandable to novice users. But you can say that your object is best understood by novice, advanced, expert, or wizard users.
NumCounters Indicates how many counters are offered by this object. In the example the Object offers two counters and will therefore have two PERF_COUNTER_DEFINITION structures immediately following this PERF_OBJECT_TYPE structure.
DefaultCounter Used by a PerfMon-like application. When an object is selected, this member indicates which counter should be selected by default in the Counters listbox.
NumInstances Indicates how many instances this object currently has. In the example, this object doesn't support instances, so PERF_NO_INSTANCES (-1) is returned.
CodePage If this object supports instances, each instance is returned as a string name. It is best to return the instance name as a Unicode string and this member to 0. If you prefer to return non-Unicode strings, set this member to the CodePage used for the instance string name.
PerfTime and PerfFreq Should always be set to 0 because the Se members are only used by the application requesting the data.


Figure 10   PERF_COUNTER_DEFINITION

Member Description
ByteLength Length in bytes of this structure. Used for walking the memory block.
CounterNameTitleIndex and CounterHelpTitleIndex Indicates the numbers that were assigned to the counter text and its help when they were added to the registry.
CounterNameTitle and CounterHelpTitle Should always be set to NULL because the Se members are only used by the application requesting the data.
DefaultScale Power of 10 by which to scale chart line of vertical axis is 100 (0=>1, 1=>10, -1=>1/10, and so on).
DetailLevel Indicates how hard to understand this counter is to most users. Most counters are understandable to novice users. But you can say that your counter is best understood by novice, advanced, expert, or wizard users.
CounterType Type of counter (see the WinPerf.H header file for a description).
CounterSize Number of bytes used for counter information (usually four or eight bytes).
CounterOffset Offset from the start of the PERF_COUNTER_BLOCK to the first byte of this counter.


Figure 12   PERF_INSTANCE_DEFINITION

Member Description
ByteLength Length in bytes of this structure. Used for walking the memory block.
ParentObjectTitleIndex An object instance can be the child of another object/instance. For example, threads are children of a process. If this instance has a parent, this number identifies the parent object's assigned registry number (0 = no parent).
ParentObjectInstance If this instance has a parent, this number identifies the parent instance's index relative to the parent object.
UniqueID An instance can be identified by a number or by a string name. If you want to use a number, set this member to the number and set NameOffset and NameLength to 0. If you wish to use a string name, set this member to -1 and set the NameOffset and NameLength members appropriately.
NameOffset Number of bytes from the beginning of this structure to this instance's Unicode string name.
NameLength Number of bytes (not characters) of this instance's Unicode string name (add two bytes for the terminating 0 character).


Figure 13   Performance Monitor Project Files

File Name Description
PrfData.h Header file that defines the CPrfData class. This file also declares the macros used to create a PRFDATA map.
PrfData.cpp The file that implements the CPrfData class's member functions. This file also exports the Open, Collect, and Close functions.
Optex.h Header file included by PrfData.h. The COptex class defined in this file is a high-speed critical section that can be used to synchronize threads in different processes. This class was described in my January 1998 Win32 Q&A column.
Optex.cpp File that implements the COptex class's member functions.


Figure 14   Object and Counter Symbols


/******************************************************************************
Module name: HWInputPrfDataMap.h
Notices: Written 1998 by Jeffrey Richter
Description: Definition of performance objects and counters
******************************************************************************/


#include "PrfData.h"


///////////////////////////////////////////////////////////////////////////////


PRFDATA_DEFINE_OBJECT(PRFOBJ_HWINPUT,            100);
PRFDATA_DEFINE_COUNTER(HWINPUT_KEYS,             101);
PRFDATA_DEFINE_COUNTER(HWINPUT_KEYSPERSEC,       102);
PRFDATA_DEFINE_COUNTER(HWINPUT_MOUSEMOVES,       103);
PRFDATA_DEFINE_COUNTER(HWINPUT_MOUSEMOVESPERSEC, 104);

PRFDATA_DEFINE_OBJECT(PRFOBJ_MOUSECLCKS,         200);
PRFDATA_DEFINE_COUNTER(MOUSECLCKS_CLICKS,        201);
PRFDATA_DEFINE_COUNTER(MOUSECLCKS_CLICKSPERSEC,  202);


Figure 15   HWInputPrfDataMap.cpp


/******************************************************************************
Module name: HWInputPrfDataMap.cpp
Notices: Written 1998 by Jeffrey Richter
Description: Performance object and counter map
******************************************************************************/


#include "CmnHdr.h"
#include <Windows.h>
#include "PrfData.h"
#include "HWInputPrfDataMap.h"


///////////////////////////////////////////////////////////////////////////////


PRFDATA_MAP_BEGIN()
   PRFDATA_MAP_OBJ(PRFOBJ_HWINPUT, L"Hardware Input", 
      L"The Hardware Input object type includes those counters "
      L"that apply to keystrokes and mouse moves.", 
      PERF_DETAIL_NOVICE, HWINPUT_KEYS, PERF_NO_INSTANCES, 0)

   PRFDATA_MAP_CTR(HWINPUT_KEYS,  L"Keystrokes",  
      L"The number of down and up keystrokes",  
      PERF_DETAIL_NOVICE, 0, PERF_COUNTER_RAWCOUNT)
   PRFDATA_MAP_CTR(HWINPUT_KEYSPERSEC,  L"Keystrokes/sec",  
      L"The number of down and up keystrokes per second",  
      PERF_DETAIL_NOVICE, 0, PERF_COUNTER_COUNTER)
   PRFDATA_MAP_CTR(HWINPUT_MOUSEMOVES,  L"Mouse moves",  
      L"The number of mouse moves",  
      PERF_DETAIL_NOVICE, 0, PERF_COUNTER_RAWCOUNT)
   PRFDATA_MAP_CTR(HWINPUT_MOUSEMOVESPERSEC,  L"Mouse moves/sec",  
      L"The number of mouse moves per second",  
      PERF_DETAIL_NOVICE, 0, PERF_COUNTER_COUNTER)



   PRFDATA_MAP_OBJ(PRFOBJ_MOUSECLCKS, L"Mouse Clicks", 
      L"The Mouse Clicks object type includes those counters "
      L"that apply to mouse button clicks.", 
      PERF_DETAIL_NOVICE, MOUSECLCKS_CLICKS, 4, 10)

   PRFDATA_MAP_CTR(MOUSECLCKS_CLICKS,  L"Clicks",  
      L"The number of down clicks",  
      PERF_DETAIL_NOVICE, 0, PERF_COUNTER_RAWCOUNT)
   PRFDATA_MAP_CTR(MOUSECLCKS_CLICKSPERSEC,  L"Clicks/sec",  
      L"The number of down clicks per second",  
      PERF_DETAIL_NOVICE, 0, PERF_COUNTER_COUNTER)
PRFDATA_MAP_END("HWInputMon")

Figure 16   HWInputMon.cpp


/********************************************************************
Module name: HWInputMon.cpp
Notices: Written 1998 by Jeffrey Richter
Description: App to monitor keyboard & mouse performance info.
********************************************************************/


#include "CmnHdr.h"
#include <Windows.h>
#include <WindowsX.h>
#include <Math.h>
#include <TChar.h>
#include "HWInputPrfDataMap.h"


/////////////////////////////////////////////////////////////////////


LRESULT CALLBACK LowLevelKeyboardProc(int nCode, 
   WPARAM wParam, LPARAM lParam) {

   if (nCode == HC_ACTION) {
      switch (wParam) {
         case WM_KEYDOWN:  case WM_SYSKEYDOWN:
         case WM_KEYUP:    case WM_SYSKEYUP: 
            g_PrfData.GetCtr32(HWINPUT_KEYS)++; 
            g_PrfData.GetCtr32(HWINPUT_KEYSPERSEC)++;
            break;
      }
   }
   return(CallNextHookEx(NULL, nCode, wParam, lParam));
}


/////////////////////////////////////////////////////////////////////


typedef enum { 
   mciFirst = 0, 
   mciTotal = mciFirst, 
   mciLeft, 
   mciMiddle, 
   mciRight,
   mciLast = mciRight
} MOUSECLCKINST;

CPrfData::INSTID g_MouseClckInstToPrfInstId[mciLast + 1];


/////////////////////////////////////////////////////////////////////


LRESULT CALLBACK LowLevelMouseProc(int nCode, 
   WPARAM wParam, LPARAM lParam) {

   if (nCode == HC_ACTION) {
      if (wParam == WM_MOUSEMOVE) {
         g_PrfData.GetCtr32(HWINPUT_MOUSEMOVES)++;
         g_PrfData.GetCtr32(HWINPUT_MOUSEMOVESPERSEC)++;
      }

      BOOL fDown = ((wParam == WM_LBUTTONDOWN) || 
         (wParam == WM_MBUTTONDOWN) || (wParam == WM_RBUTTONDOWN));
      if (fDown) {

         MOUSECLCKINST mci;
         if ((wParam == WM_LBUTTONDOWN) || (wParam == WM_LBUTTONUP))
            mci = mciLeft;
         if ((wParam == WM_MBUTTONDOWN) || (wParam == WM_MBUTTONUP))
            mci = mciMiddle;
         if ((wParam == WM_RBUTTONDOWN) || (wParam == WM_RBUTTONUP))
            mci = mciRight;

         g_PrfData.GetCtr32(MOUSECLCKS_CLICKS,       
            g_MouseClckInstToPrfInstId[mciTotal])++;
         g_PrfData.GetCtr32(MOUSECLCKS_CLICKSPERSEC, 
            g_MouseClckInstToPrfInstId[mciTotal])++;
         g_PrfData.GetCtr32(MOUSECLCKS_CLICKS,       
            g_MouseClckInstToPrfInstId[mci])++;
         g_PrfData.GetCtr32(MOUSECLCKS_CLICKSPERSEC, 
            g_MouseClckInstToPrfInstId[mci])++;
      }
   }
   return(CallNextHookEx(NULL, nCode, wParam, lParam));
}


/////////////////////////////////////////////////////////////////////


int WINAPI WinMain (HINSTANCE hinstExe, 
   HINSTANCE hinstExePrev, LPSTR pszCmdLine, int nCmdShow) {

   BOOL fRunApp = TRUE;

   if (lstrcmpiA(pszCmdLine, "-Install") == 0) {
      WCHAR szPath[_MAX_PATH];
      GetModuleFileNameW(hinstExe, szPath, chDIMOF(szPath));
      wcscpy(wcsrchr(szPath, L'\\') + 1, L"HWInputMonPrfInfo.dll");
      g_PrfData.Install(szPath);
      fRunApp = FALSE;
   }

   if (lstrcmpiA(pszCmdLine, "-Uninstall") == 0) {
      g_PrfData.Uninstall();
      fRunApp = FALSE;
   }

   if (fRunApp) {
      g_PrfData.Activate();

      // Add the four Mouse Click Object Instances
      g_MouseClckInstToPrfInstId[mciTotal]  = 
         g_PrfData.AddInstance(PRFOBJ_MOUSECLCKS, L"_Total");
      g_MouseClckInstToPrfInstId[mciLeft]   = 
         g_PrfData.AddInstance(PRFOBJ_MOUSECLCKS, L"Left");
      g_MouseClckInstToPrfInstId[mciMiddle] = 
         g_PrfData.AddInstance(PRFOBJ_MOUSECLCKS, L"Middle");
      g_MouseClckInstToPrfInstId[mciRight]  = 
         g_PrfData.AddInstance(PRFOBJ_MOUSECLCKS, L"Right");

      // Install the low-level keyboard & mouse hooks      }
      HHOOK hhkLowLevelKybd  = SetWindowsHookEx(WH_KEYBOARD_LL, 
         LowLevelKeyboardProc, hinstExe, 0);
      HHOOK hhkLowLevelMouse = SetWindowsHookEx(WH_MOUSE_LL, 
         LowLevelMouseProc, hinstExe, 0);

      // Keep this app running until we're told to stop
      int x = IDRETRY;
      while (x == IDRETRY) {

         if (x == IDRETRY) {
            // Reset all of the counters to zero
            g_PrfData.LockCtrs();
            g_PrfData.GetCtr32(HWINPUT_KEYS) = 0;
            g_PrfData.GetCtr32(HWINPUT_KEYSPERSEC) = 0;
            g_PrfData.GetCtr32(HWINPUT_MOUSEMOVES) = 0;
            g_PrfData.GetCtr32(HWINPUT_MOUSEMOVESPERSEC) = 0;

            MOUSECLCKINST mci = mciFirst; 
            while (mci <= mciLast) {
               g_PrfData.GetCtr32(MOUSECLCKS_CLICKS,       
                  g_MouseClckInstToPrfInstId[mci]) = 0;
               g_PrfData.GetCtr32(MOUSECLCKS_CLICKSPERSEC, 
                  g_MouseClckInstToPrfInstId[mci]) = 0;

               mci = (MOUSECLCKINST) ((int) mci + 1);
            }
            g_PrfData.UnlockCtrs();
         }

         x = ::MessageBox(NULL, 
            __TEXT("Click \"Retry\"  to reset the counters.\n")
            __TEXT("Click \"Cancel\" to terminate the application."),
            __TEXT("Hardware Input Performance Monitor "),
            MB_RETRYCANCEL);
      }

      UnhookWindowsHookEx(hhkLowLevelKybd);
      UnhookWindowsHookEx(hhkLowLevelMouse);

      // Remove the four Mouse Click Object Instances
      g_PrfData.RemoveInstance(PRFOBJ_MOUSECLCKS, 
         g_MouseClckInstToPrfInstId[mciTotal]);
      g_PrfData.RemoveInstance(PRFOBJ_MOUSECLCKS, 
         g_MouseClckInstToPrfInstId[mciLeft]);
      g_PrfData.RemoveInstance(PRFOBJ_MOUSECLCKS, 
         g_MouseClckInstToPrfInstId[mciMiddle]);
      g_PrfData.RemoveInstance(PRFOBJ_MOUSECLCKS, 
         g_MouseClckInstToPrfInstId[mciRight]);
   }

   return(0);
}