- Overcoming the Paucity of Tools
- Defining Custom Performance Counters
- Testing Your Custom Counter
- Real-World Counter Generators
It's relatively easy to access any performance counter you need from Server Explorer (see my article, Using Server Explorer from Visual C++ .NET, for details).
However, these counters do precisely what you'd expect: They monitor performance. You need to build a custom performance counter that does more than simply monitor performance. The classes required to build a custom counter appear in the Diagnostics namespace, so you need to add a reference to your application like this:
using namespace System::Diagnostics;
The code itself requires references to several global variables to create the counter, code to initialize the counter, and some code to generate data for presentation with the counter. Listing 1 provides some ideas on how to build a counter with these features.
Listing 1 Creating a Custom Counter
// A collection of counters. CounterCreationDataCollection *CounterCollect; // The error counter. CounterCreationData *ErrorCount; // Determines random error. Random *EventVal; // Error performance counter. PerformanceCounter *PerfCount; // Application name. String *AppName; Form1(void) { // Required for Windows Form Designer support InitializeComponent(); // Create the collection and error counter. CounterCollect = new CounterCreationDataCollection(); ErrorCount = new CounterCreationData( "Error_Count", "Contains the application error count.", PerformanceCounterType::RateOfCountsPerSecond32); // Add the counter to the collection. CounterCollect->Add(ErrorCount); // Get the application name. AppName = Application::ExecutablePath; AppName = AppName->Substring(AppName->LastIndexOf("\\") + 1); AppName = AppName->Substring(0, AppName->Length - 4); // Create a custom counter category. PerformanceCounterCategory::Create(AppName, "Error checking counter.", CounterCollect); // Create the performance counter. PerfCount = new PerformanceCounter(AppName, "Error_Count", false); // Create the random number generator. EventVal = new Random(DateTime::Now.Second); }
I used a standard Windows application for the example, but the same technique works fine in a component or in any other kind of application you want to create. The constructor begins by initializing the counter. This example shows a very simple counter. The code begins by creating a counter and a counter collection to hold it.
Normally, I use the application name for the counter. If you run multiple instances of the application (such as with a COM+ application), you might want to combine the application name with a unique number to ensure that you're monitoring the correct application. While writing this application, I noted something odd about the Application object provided by Visual C++. When working with other .NET languages, you can use the Application object to obtain the product name. However, when working with Visual C++, the Application::ProductName property returns an empty string. The code overcomes this problem by creating the AppName String shown using the Application::ExecutablePath property.
Once the code creates the counter and counter collection, it uses the PerformanceCounterCategory.Create() method to add the collection to Windows. The collection can contain any number of counters, and you can create counters of various types. You can read more about the various performance counter types at this MSDN site.
Creating a performance counter for Windows doesn't provide access to it. The code creates a new PerformanceCounter object using the name of the counter category and the individual counter. Notice that you must set the ReadOnly argument to false or else you can't generate new data for the counter.
In most cases, the one element in the constructor that you don't need to create is the Random object EventVal object. This object generates random errors in this example. In a production environment, you'd want to know about only the real errors. Speaking of test code, the best way to test this example is to generate random events that simulate errors. Listing 2 shows some typical test code.
Listing 2 Building the Counter Test Code
private: System::Void btnTest_Click( System::Object * sender, System::EventArgs * e) { // Set the number of events per second. EventGen->Interval = Convert::ToInt32(1000 / txtTimerVal->Value); // Start the timer. EventGen->Start(); } private: System::Void Form1_Closing( System::Object * sender, System::ComponentModel::CancelEventArgs * e) { // Destroy the counter. PerformanceCounterCategory::Delete(AppName); } private: System::Void EventGen_Tick( System::Object * sender, System::EventArgs * e) { // Generate a random error event. if (EventVal->Next(5) <= 1) PerfCount->Increment(); }
The btnTest_Click() method sets the timer interval and starts the timer. Once the timer starts, it generates ticks at the rate specified by the Interval property. Each call to the EventGen_Tick() event handler is the result of a tick. The code checks the current random number value. If it's less than or equal to 1, the code increments the performance counter using the PerfCount.Increment() method.
Because this is a temporary counter, you should destroy it before you exit the application. The frmMain_Closing() method accomplishes this task. All you need to supply is the name of the counter category.