Geekpedia Programming Tutorials






Using Performance Counters in C#

The application we are building in this tutorial will make use of the Performance Counters made available by Windows. They allow us to retrieve CPU, memory and network usage among other counters.

On Saturday, June 30th 2007 at 12:16 AM
By Andrew Pociu (View Profile)
*****   (Rated 4.8 with 22 votes)
Contextual Ads
More C# Resources
Advertisement
Download this Visual Studio 2005 project Download this project (Visual Studio 2005)

The performance counters offered by the Windows Operating system can be accessed through Control Panel -> Administrative Tools -> Performance. But what's so great is that these performance counters can be easily queried from inside .NET Framework.

First of all, I want to make clear that the application we are building in this tutorial does not work with many of the performance counters that it lists, simply because there's too much code to write to make it adapt to different counter type, and we want to keep things simple. Fortunately it works with the most popular performance characters such as the processor usage and disk usage. Furthermore, since the graph that you see in the picture below is for fixed values of 0 to 100 (typical percentage values) it will not display correctly those performance counters that return values with a different range.

Let's start by creating the form:

Performance Counters Form

Three drop-down lists: lstCats, lstCounters, lstInstances are the ones that we populate with counter categories, counter names and counter instances. A start button btnStart is being used to enable the timer that reads the values. And speaking of that, don't forget to add the timer tmrCounter and set its Interval property to 1000 (1 second). This means the graph will update every second. I also added three labels lblValue, lblLow and lblHigh to display further information coming from the counter. The graph is drawn inside a PictureBox named picGraph that has a background image showing a scale. One more thing left: the actual PerformanceCounter object perfMain; you can find this on the Toolbox under Components.

Getting a list of available Performance Counters

In order to make this tutorial more useful we're not going to use a fixed, predefined list of counters, instead we're going to retrieve them dinamically from the user's operating system. This is particularly useful because the number of performance counters differ from system to system, depending on the hardware and software installed. Counters are divided into categories, thus first we need to retrieve these categories. We'll do this in the Load event of the form. But first let's place some using statements:


using System.Drawing.Drawing2D;

using System.Diagnostics;


And then initialize a variable we'll use to set the position of the graph line:


// Used to move the graph line

int graphIncrement = 0;


And now the Form1_Load event:


private void Form1_Load(object sender, EventArgs e)

{

    // Get a list of available performance counter categories

    PerformanceCounterCategory[] perfCounters = PerformanceCounterCategory.GetCategories();

    for (int i = 0; i < perfCounters.Length; i++)

    {

        // Add the category to the drop-down list

        lstCats.Items.Add(perfCounters[i].CategoryName);

    }

}


Now that we have the categories in the lstCats dropdown list, we should retrieve the counters depending on the selected category from the dropdown list. This will be done in the SelectedIndexChanged event of lstCats list, which fires each time a selection is made from the dropdown list:


private void lstCats_SelectedIndexChanged(object sender, EventArgs e)

{

    string[] instanceNames;

    System.Collections.ArrayList counters = new System.Collections.ArrayList();

    if (lstCats.SelectedIndex != -1)

    {

        System.Diagnostics.PerformanceCounterCategory mycat = new System.Diagnostics.PerformanceCounterCategory(this.lstCats.SelectedItem.ToString());

        // Remove the current contents of the list.

        this.lstCounters.Items.Clear();

        // Retrieve the counters.

        instanceNames = mycat.GetInstanceNames();

        if (instanceNames.Length == 0)

        {

            counters.AddRange(mycat.GetCounters());

        }

        else

        {

            for (int i = 0; i < instanceNames.Length; i++)

            {

                counters.AddRange(mycat.GetCounters(instanceNames[i]));

            }

        }

 

        // Add the retrieved counters to the list.

        foreach (System.Diagnostics.PerformanceCounter counter in counters)

        {

            this.lstCounters.Items.Add(counter.CounterName);

        }

    }

}


For the code above to work, don't forget to bind lstCats_SelectedIndexChanged to the SelectedIndexChanged event. The easiest way to do this in Visual Studio is to select the dropdown list, switch the Properties window to Events and double click the SelectedIndexChanged field.

We're not done yet with retrieving counters. Each counter has its own instances, which could reflect different values to be retrieved by that counter, some in percentages and others in various ranges. Some may not have any instances at all. _Total is a popular instance name which most of the time is used to retrieve the most popularly demanded type of value from that counter.
Each time a counter is selected we want to retrieve the list of possible instances:


private void lstCounters_SelectedIndexChanged(object sender, EventArgs e)

{

    // Clear the existing instance list

    lstInstances.Items.Clear();

    PerformanceCounterCategory perfCat = new PerformanceCounterCategory(lstCats.SelectedItem.ToString());

    string[] catInstances;

    catInstances = perfCat.GetInstanceNames();

    lstInstances.Items.Clear();

    foreach (string catInstance in catInstances)

    {

        lstInstances.Items.Add(catInstance);

    }

}


And now that the code for populating the instance list is in, we can write the code that starts the monitoring, and this is located in the Click even of btnStart:


private void btnStart_Click(object sender, EventArgs e)

{

    if (lstCats.SelectedIndex != -1 && lstCounters.SelectedIndex != -1 && lstInstances.SelectedIndex != -1)

    {

        // Clear the graph

        picGraph.Invalidate();

        graphIncrement = 0;

        perfMain.CategoryName = lstCats.SelectedItem.ToString();

        perfMain.CounterName = lstCounters.SelectedItem.ToString();

        perfMain.InstanceName = lstInstances.SelectedItem.ToString();

        tmrCounter.Start();

    }

    else

    {

        MessageBox.Show("Please select a category, counter and instance first.", "Selection needed", MessageBoxButtons.OK, MessageBoxIcon.Error);

    }

}


Aside from setting the properties for the counter object, we're starting the tmrCounter Timer object - it will be doing all the work from now on, so let's look at what's inside its Tick event:


private void tmrCounter_Tick(object sender, EventArgs e)

{

    float currVal;

    // Move to and get the latest value in the performance counter

    currVal = perfMain.NextValue();

    // Update the label with the value

    lblValue.Text = Math.Round(Convert.ToDouble(currVal.ToString()), 2) + "%";

    Graphics gfx = picGraph.CreateGraphics();

    Pen pn = new Pen(Color.Red, 1);

    gfx.DrawLine(pn, graphIncrement, 200, graphIncrement, 199 - (currVal * 2));

 

    graphIncrement += 2;

    if (graphIncrement > 500)

    {

        picGraph.Invalidate();

        graphIncrement = 0;

    }

}


Below is the application in action. There are many issues to address, including the PictureBox clearing when it gets invalidated and not all performance counters being properly scaled on the graph, but this is only a proof of concept application. One issue that you might experience though and which can easily be addressed is the "Access to the registry key 'Global' is denied." exception. This is caused on certain operating systems such as Windows Vista Ultimate when the account doesn't have administrative privileges and it's not allowed access to all the performance counters. After you compile the code, right click the application's executable, select Properties and in the Compatibility tab check the "Run this program as an administrator" checkbox.

Performance Counters Window

The full source code for the application is below:


using System;

using System.Collections.Generic;

using System.ComponentModel;

using System.Data;

using System.Drawing;

using System.Text;

using System.Windows.Forms;

using System.Drawing.Drawing2D;

using System.Diagnostics;

 

namespace PerformanceCounters

{

    public partial class Form1 : Form

    {

        // Used to move the graph line

        int graphIncrement = 0;

 

        public Form1()

        {

            InitializeComponent();

        }

 

        private void Form1_Load(object sender, EventArgs e)

        {

            // Get a list of available performance counter categories

            PerformanceCounterCategory[] perfCounters = PerformanceCounterCategory.GetCategories();

            for (int i = 0; i < perfCounters.Length; i++)

            {

                // Add the category to the drop-down list

                lstCats.Items.Add(perfCounters[i].CategoryName);

            }

        }

 

        private void tmrCounter_Tick(object sender, EventArgs e)

        {

            float currVal;

            // Move to and get the latest value in the performance counter

            currVal = perfMain.NextValue();

            // Update the label with the value

            lblValue.Text = Math.Round(Convert.ToDouble(currVal.ToString()), 2) + "%";

            Graphics gfx = picGraph.CreateGraphics();

            Pen pn = new Pen(Color.Red, 1);

            gfx.DrawLine(pn, graphIncrement, 200, graphIncrement, 199 - (currVal * 2));

 

            graphIncrement += 2;

            if (graphIncrement > 500)

            {

                picGraph.Invalidate();

                graphIncrement = 0;

            }

        }

 

        private void lstCats_SelectedIndexChanged(object sender, EventArgs e)

        {

            string[] instanceNames;

            System.Collections.ArrayList counters = new System.Collections.ArrayList();

            if (lstCats.SelectedIndex != -1)

            {

                System.Diagnostics.PerformanceCounterCategory mycat = new System.Diagnostics.PerformanceCounterCategory(this.lstCats.SelectedItem.ToString());

                // Remove the current contents of the list.

                this.lstCounters.Items.Clear();

                // Retrieve the counters.

                instanceNames = mycat.GetInstanceNames();

                if (instanceNames.Length == 0)

                {

                    counters.AddRange(mycat.GetCounters());

                }

                else

                {

                    for (int i = 0; i < instanceNames.Length; i++)

                    {

                        counters.AddRange(mycat.GetCounters(instanceNames[i]));

                    }

                }

 

                // Add the retrieved counters to the list.

                foreach (System.Diagnostics.PerformanceCounter counter in counters)

                {

                    this.lstCounters.Items.Add(counter.CounterName);

                }

            }

        }

 

        private void btnStart_Click(object sender, EventArgs e)

        {

            if (lstCats.SelectedIndex != -1 && lstCounters.SelectedIndex != -1 && lstInstances.SelectedIndex != -1)

            {

                // Clear the graph

                picGraph.Invalidate();

                graphIncrement = 0;

                perfMain.CategoryName = lstCats.SelectedItem.ToString();

                perfMain.CounterName = lstCounters.SelectedItem.ToString();

                perfMain.InstanceName = lstInstances.SelectedItem.ToString();

                tmrCounter.Start();

            }

            else

            {

                MessageBox.Show("Please select a category, counter and instance first.", "Selection needed", MessageBoxButtons.OK, MessageBoxIcon.Error);

            }

        }

 

        private void lstCounters_SelectedIndexChanged(object sender, EventArgs e)

        {

            // Clear the existing instance list

            lstInstances.Items.Clear();

            PerformanceCounterCategory perfCat = new PerformanceCounterCategory(lstCats.SelectedItem.ToString());

            string[] catInstances;

            catInstances = perfCat.GetInstanceNames();

            lstInstances.Items.Clear();

            foreach (string catInstance in catInstances)

            {

                lstInstances.Items.Add(catInstance);

            }

        }

    }

}

Digg Digg It!     Del.icio.us Del.icio.us     Reddit Reddit     StumbleUpon StumbleIt     Newsvine Newsvine     Furl Furl     BlinkList BlinkList

Rate Rate this tutorial
Comment Current Comments
by haroon shahzad on Thursday, May 15th 2008 at 07:47 AM

Good code!
Thanks

by mahesh kulkarni on Tuesday, June 24th 2008 at 03:31 AM

I HAVE TIRED THSI AMPLE CODE IT NOT GIVING ANY ERROR BUT IT IS NOT SHOWING THE GRAPH.ALSO TELL ME ABOUT WHIC COUTER NAME AND CATAGORY NAME I HAVE TO SET INITIALY.

by mahesh kulkarni on Tuesday, June 24th 2008 at 03:31 AM

I HAVE TIRED THSI AMPLE CODE IT NOT GIVING ANY ERROR BUT IT IS NOT SHOWING THE GRAPH.ALSO TELL ME ABOUT WHIC COUTER NAME AND CATAGORY NAME I HAVE TO SET INITIALY.

by mahesh kulkarni on Tuesday, June 24th 2008 at 03:31 AM

I HAVE TIRED THSI AMPLE CODE IT NOT GIVING ANY ERROR BUT IT IS NOT SHOWING THE GRAPH.ALSO TELL ME ABOUT WHIC COUTER NAME AND CATAGORY NAME I HAVE TO SET INITIALY.

by Chakradhar on Tuesday, July 22nd 2008 at 12:37 PM

Its nice to see the code.I am devloping the same counters cpu memory and battery life for windows mobile.Can this code be helpful.Can i get cpu usage from this?
Chakradhar

by Chakradhar on Tuesday, July 22nd 2008 at 12:37 PM

Its nice to see the code.I am devloping the same counters cpu memory and battery life for windows mobile.Can this code be helpful.Can i get cpu usage from this?
Chakradhar

by oduy on Friday, February 20th 2009 at 07:59 PM

Good code
it is very nice, but there are some problem with Catgory Name: .NET Network CRL it is not work ,if any one has the soultion for this problem plz submit on sits
Thanks

by oduy on Friday, February 20th 2009 at 08:00 PM

Good code
it is very nice, but there are some problem with Catgory Name: .NET Network CRL it is not work ,if any one has the soultion for this problem plz submit on sits
Thanks

by minhquang on Wednesday, September 23rd 2009 at 03:35 AM

this code is very good
thank's

by Ta Xuan Truong on Thursday, December 10th 2009 at 11:45 AM

That's great !

Thank you for your sharing.

by Ta Xuan Truong on Thursday, December 10th 2009 at 11:45 AM

That's great !

Thank you for your sharing.

by markf4 on Monday, January 4th 2010 at 06:44 AM

very good!
I've tried in on my webservice in <a href="http://catering-havaya.co.il/קייטרינג-חלבי>קייטרינג חלבי</a> site and it works perfect!


Comment Comment on this tutorial
Name: Email:
Message:
Comment Related Tutorials
There are no related tutorials.

Comment Related Source Code
There is no related source code.

Jobs C# Job Search
My skills include:
Enter a City:

Select a State:


Advanced Search >>
Latest Tech Bargains

Advertisement

Free Magazine Subscriptions

Today's Pictures

Today's Video

Other Resources

Latest Download

Latest Icons