Performance Monitor - Get System Counter Values (CPU, Memory, etc.)

Environment: VC6, Windows 2000, Windows NT 4.0 SP3

Note: This article is specific to Windows 2000 and Windows NT 4.0 SP3+. It does not support Windows 95 or 98. For Windows NT 4, you will need PDH.DLL and PDH.LIB which are not supplied with the OS.

I wrote this code because I couldn't find any good resource meters for Windows 2000. Plenty of them exist for Windows 95/98 and I figured that I might as well write my own.

I have wrapped the PDH into a nice C++ class called CPerfMon. It will support all the counters available under Windows. I have done some predefined timers for easy use. These are as follows:

// % of cpu in use
#define CNTR_CPU "\\Processor(_Total)\\% Processor Time" 

// mem in use measured in bytes
#define CNTR_MEMINUSE_BYTES "\\Memory\\Committed Bytes" 

// mem available measured in bytes
#define CNTR_MEMAVAIL_BYTES "\\Memory\\Available Bytes" 

// mem avail in kilobytes
#define CNTR_MEMAVAIL_KB "\\Memory\\Available KBytes" 

// mem avail in megabytes
#define CNTR_MEMAVAIL_MB "\\Memory\\Available MBytes" 

// % of mem in use
#define CNTR_MEMINUSE_PERCENT "\\Memory\\% Committed Bytes In Use" 

// commit limit on memory in bytes
#define CNTR_MEMLIMIT_BYTES "\\Memory\\Commit Limit" 

This class contains a few easy to use functions to add counters, remove counters, get statistics (avg, min, max), and to get the current value. On to the code. :)

First you need to initialize the class:

CPerfMon pm;

// init
if (!pm.Initialize())
{
 AfxMessageBox("PerfMon did not initialize properly!");
 return;
}
Then we add the counters we want to track. To find new counter names, use the function PdhBrowseCounters().
int nCpuIndex = pm.AddCounter(CNTR_CPU); // we used a predefined name for these counters...
int nMemIndex = pm.AddCounter(CNTR_MEMINUSE_PERCENT);

// test them if you want
ASSERT(nCpuIndex > -1 && nMemIndex > -1);
Then we can call the functions to obtain values:
// execute the query for our counters
if (!pm.CollectQueryData())
{
 AfxMessageBox("Could not collect data.");
 return;
}

// grab the current value for our counters
long lCpu = pm.GetCounterValue(nCpuIndex);
long lMem = pm.GetCounterValue(nMemIndex);

// -999 is returned on error (hacky I know. Sorry.) ;)
ASSERT(lCpu != -999L && lMem != -999L); 

// grab the statistical values
long lCpuMin, lCpuMax, lCpuAvg;
long lMemMin, lMemMax, lMemAvg;
if (!pm.GetStatistics(&lCpuMin, &lCpuMax, &lCpuAvg, nCpuIndex) 
|| !pm.GetStatistics(&lMemMin, &lMemMax, &lMemAvg, nMemIndex))
{
 AfxMessageBox("Could not get statistical data.");
 return;
}
Now all the values that you want are contained in lCpu, lMem, lCpuMin, lCpuMax, lCpuAvg, lMemMin, lMemMax, and lMemAvg.

The way the class was intended to work is with a window timer. Set a timer called every 1/2 second or second and call the above functions. Then populate your dialog or whatever with the returned data. See the demo for an example of this.

Good luck and have fun. Please feel free to email me any comments or questions (or post them to CodeGuru).

Downloads

Download demo project - 15 Kb
Download source - 4 Kb