Entries:
Comments:
Posts:

Loading User Information from Channel 9

Something went wrong getting user information from Channel 9

Latest Achievement:

Loading User Information from MSDN

Something went wrong getting user information from MSDN

Visual Studio Achievements

Latest Achievement:

Loading Visual Studio Achievements

Something went wrong getting the Visual Studio Achievements

Poor Man's Power Monitor

  This is an application that makes it really easy to know how much power you were using, and specifically where that power was going.
3 Leaf Development

Difficulty: Intermediate
Time Required: 1-3 hours
Cost: Free
Software: Visual Studio Express Editions
Hardware:
Download: Download

Note: For this code sample, please make sure to open the project with the Windows Form visible, select performanceCounter1 from the application tray, switch to the Properties Window and change the MachineName property to your computer name.

Laptops are currently outselling desktops, which means a couple of things.  It means that users are more mobile, and are using applications while they sit in conference rooms, coffee shops, or airline seats.  This also means that for a good percentage of the day, you can be away from the wall outlet that gives life to your laptop.  I often find that I'm consciously aware of what I run when I'm on a long flight, because I want the battery to last it all the way to my destination.  However, my choice of application usage is based on in intuition and environmental clues.  For example, if the fan's running, and the performance of applications is “chunky”, then I know that I'm loading the system down, and the battery is bleeding dry faster than I would like.  However, I wanted something a little more scientific than just laying my hands on the machine, and trying to feel the force of the current flowing.  There are some really sophisticated pieces of software that will examine the power usage of various applications, but I figured by just monitoring a few key performance counters, I could have a pretty good approximation of the drain rate.

The Stats

First, you know that when the CPU is maxed out, you're using a lot of battery.  The hard drive is another expensive piece of equipment to use, so physical disk IO is another indication.  Finally, network usage – especially wireless network usage – can also be a significant drain.  There are some other significant factors as well (is your brightness turned up all the way, for example), but much of this information is not readily accessible through software, so it's left out of the mix.  Even so, I believe that the power monitor developed for this article gives a good indication of how fast you're draining the electron bank.

The App

I wanted an application that made it really easy to know how much power you were using, and specifically where that power was going, so the following user interface was developed:

This is the power usage while I was simultaneously zipping a directory that contained SQL 2000, and downloading an ISO image over the wireless network.  As you can see, the system is hammered, and the power usage monitor indicates that the CPU, disk, and networking subsystems are all being stressed.  Stop the download, and the networking usage drops off:

And stop the Zip operation, and the system returns to a more idle state:

Also note the blue bar.  This indicates what the average power usage has been for the last minute.  As power usage tends to jump around a fair amount as applications are running, and this gives you an instant look what the average usage level has been.

The Details

The application uses a timer control to poll certain system counters on a regular – two second – interval.  For CPU data, this is trivial.  You can just use the .NET Framework's PerformanceCounter class.  For disk IO, the story is a tad more complicated.  The counter that I wanted to read was the “Physical Disk:% Disk Time:_Total” counter.  Unfortunately, as documented here, reading that counter from the .NET Framework is broken in v1.0, 1.1, and apparently still in v2.0.  However, with just a little WMI code, you can easily get this information:

Visual C#

physDiskCounter = new ManagementObject(
                "Win32_PerfRawData_PerfDisk_PhysicalDisk.Name='_Total'"); 

physDiskCounter.Get();
double newCountBase = (double)(ulong)physDiskCounter.Properties[
                       "PercentDiskTime_Base"].Value;
double newCountValue = (double)(ulong)physDiskCounter.Properties[
                       "PercentDiskTime"].Value;
double result = ((prevCountValue - newCountValue) / 
                (prevCountBase - newCountBase)) * 100;
prevCountBase = newCountBase;
prevCountValue = newCountValue;
return result;

Visual Basic

physDiskCounter = New ManagementObject(
                  "Win32_PerfRawData_PerfDisk_PhysicalDisk.Name='_Total'")

physDiskCounter.Get();
double newCountBase = (double)(ulong)physDiskCounter.Properties[
                       "PercentDiskTime_Base"].Value;
double newCountValue = (double)(ulong)physDiskCounter.Properties[
                        "PercentDiskTime"].Value;
double result = ((prevCountValue - newCountValue) /
                (prevCountBase - newCountBase)) * 100;
prevCountBase = newCountBase;
prevCountValue = newCountValue;
return result;

This just accesses the raw performance counter values, subtracts off the previous value to get the delta, and returns the results.

Reading the network information is also a problem, but for a different reason.  First, I really want to just monitor the wireless network card, as that's what uses the most power.  I certainly don't want things like the loopback adapter included in the mix.  There are some low level APIs that I could use to find the wireless network adapter, but fortunately version 2.0 of the Framework contains a number of new networking classes that make this easy:

Visual C#

// Look for a "real" network interface, and preferably
// a WiFi, as it uses the most power.
NetworkInterface[] interfaces = 
    NetworkInterface.GetAllNetworkInterfaces();
foreach (NetworkInterface ni in interfaces)
{
    if (ni.NetworkInterfaceType == NetworkInterfaceType.Wireless80211 ||
        ni.NetworkInterfaceType == NetworkInterfaceType.Ethernet)
    {
        netName = ni.Description.Replace("(", "[").Replace(")", "]");
        speed = ni.Speed / 10;
        if (ni.NetworkInterfaceType == NetworkInterfaceType.Wireless80211)
        {
            break;
        }
    }
} 

Visual Basic

' Look for a "real" network interface, and preferably
' a WiFi, as it uses the most power.
Dim interfaces() As NetworkInterface =  
    NetworkInterface.GetAllNetworkInterfaces() 
Dim ni As NetworkInterface
For Each ni In interfaces
    If ni.NetworkInterfaceType = NetworkInterfaceType.Wireless80211 Or _
        ni.NetworkInterfaceType = NetworkInterfaceType.Ethernet Then
        netName = ni.Description.Replace("(", "[").Replace(")", "]")
        speed = ni.Speed / 10
        If ni.NetworkInterfaceType = 
            NetworkInterfaceType.Wireless80211 Then
            Exit For
        End If
    End If
Next

You can see that the NetworkInterface class exposes a wealth of information about each network interface.  Specifically, it's easy to find an 802.11 interface.  However (nothing's ever easy), this method returned “Intel 21140-Based PCI Fast Ethernet Adapter (Generic) - Packet Scheduler Miniport” for the adapter description.  However, the “real” name appears to be “Intel 21140-Based PCI Fast Ethernet Adapter [Generic] - Packet Scheduler Miniport”.  Note the brackets instead of parenthesis around the work “Generic”.  I'm not sure why the NetworkInterface API is returning parenthesis, but it's enough of a difference that if you try to use a PerformanceCounter class, or WMI, to read the counter, it won't find it because the name exactly doesn't match.

I ran into another problem with the PerformanceCounter as well.  It appears that it truncates the instance name to 64 characters, so while I passed in “Intel 21140-Based PCI Fast Ethernet Adapter [Generic] - Packet Scheduler Miniport”, it only took “Intel 21140-Based PCI Fast Ethernet Adapter [Generic] - Packet S”.  The last 17 characters were lopped off, and the PerformanceCounter class threw an exception saying it couldn't find what I was looking for.

The (relatively) new MSDN Product Feedback Center came to the rescue.  I used it to look up the bug, and found that it had already been reported, and that it was scheduled to be fixed in Beta 2 of Visual Studio 2005.

In the end, it was back to WMI to read this performance counter as well.

That's Smooth

At this point, I was able to read all the applicable performance counters, and it was time to render the information.  When reading performance counters, you want to read them infrequently, as reading performance counters is relatively expensive.  However, if your bar graph only updates every two seconds, it tends to jump around a lot, making it hard to see how much power you're really using (high one reading, low the next).  I wanted to smooth out the movement of the bar, so I used an additional timer control, ticking 10 times per second, to move the bar an increment between the last performance counter reading, and the current one.  The affect is shown here:

The Solution is the Problem

Again, you don't want your power monitor to use a lot of power itself.  This application can consume as much as 6% of the CPU while it's running.  To me, this is too expensive.  To correct for this, I modified the application so that when it's minimized, it uses a system tray icon to display the power usage graphics, and with a little logic, the application uses much less power. 

A really good way to reduce the power of many applications is to shut off your rending logic if the application isn't visible.  This is really simple to do (but you'd be surprised how few applications bother to do this):

Visual C#

if (Visible)
{
    int x = pictureBox1.Width;
    int y = pictureBox1.Height;
    using (Graphics g = pictureBox1.CreateGraphics())
    {
        Render(g, x, y);
    }
}

Visual Basic

If Visible Then
    Dim x As Integer = pictureBox1.Width
    Dim y As Integer = pictureBox1.Height
    Using g As Graphics = pictureBox1.CreateGraphics()
        Render(g, x, y)
    End Using
End If

In other words, if the Form isn't visible, don't render the bar graph on it.  This change alone cut the power consumption from 6% of CPU to about 2% of CPU.  When the application is minimized, it still renders the bar graph to the system tray using the notification icon control:

Visual C#

Bitmap iconBitmap = new Bitmap(16, 16);
using (Graphics g = Graphics.FromImage(iconBitmap))
{
    Render(g, 16, 16);
    try
    {
        notifyIcon1.Icon = Icon.FromHandle(iconBitmap.GetHicon());
    }
    catch (Exception)
    {
    }
} 

Visual Basic

Dim iconBitmap As Bitmap = New Bitmap(16, 16)
Using g As Graphics = Graphics.FromImage(iconBitmap)
    Render(g, 16, 16)
    Try
        notifyIcon1.Icon =
            System.Drawing.Icon.FromHandle(iconBitmap.GetHicon())
    Catch
    End Try
End Using

As you can see, it's easy to generate a bitmap, and then use the GetHicon method to display the bitmap in the notification icon.  Rendering a 256 pixels to the system tray uses very little power compared rendering 11500 pixels to the form.

One other thing I quickly noticed was that, because the notification icon just isn't the same resolution, you didn't see a physical change in it 10 times a second, so I realized that I could drop my rendering rate when the power monitor is minimized:

Visual C#

private void Form1_VisibleChanged(object sender, EventArgs e)
{
    renderTimer.Interval = Visible ? 100 : 500;    
}

Visual Basic

Private Sub Form1_VisibleChanged(ByVal sender As Object, ByVal e As  
    EventArgs) Handles Me.VisibleChanged
    renderTimer.Interval = IIf(Visible, 100, 500)
End Sub

With these changes in effect, the power monitor now consumes an average of less than 1% of CPU when minimized, while still providing a visual indication of overall power usage.  With a simple double-click, you can display the full form for more details.  This is low enough power consumption that you can leave it running all the time without significantly affecting battery life.

At this point, I have an application that I can easily use to make intelligent choices about what to run to conserve battery.  Also, while the application doesn't monitor individual processes, I have found it useful for identifying power hogs.

Follow the Discussion

  • MarcoMarco

    hi...can i choose the type of interface to control?? for example i want try to look if the Wireless in enable but

    it recognizes the Wireless as Ethernet and not as Wireless...

  • JarekJarek

    Do you have a compiled .exe version?

  • Chad Chad

    Does this part of your code work on Vista?

    double newCountBase = (double)(ulong)physDiskCounter.Properties[

                          "PercentDiskTime_Base"].Value;

    double newCountValue = (double)(ulong)physDiskCounter.Properties[

                          "PercentDiskTime"].Value;

  • Thanos NaranjaThanos Naranja

    This is very interesting and helpful,  I am an amateur trying to write an application that will identify the Wifi card (PCI or PCMCIA) and turn the radio on or off with a simple software switch.  You would be amazed at the number of  WiFi radios (ThrendNet for one) that are sold without the ability to turn them off other than be going through the Device Manager.  Would you have any suggestions or code examples addressing the device management layer on this subject?

    (Sorry forgot to include my Email)

  • Natural Male EnhancementNatural Male Enhancement

    Thanks for very interesting article. btw. I really enjoyed reading all of your posts. It’s interesting to read ideas, and observations from someone else’s point of view… makes you think more.

    So please keep up the great work. Greetings.

  • sureshsuresh

    i need MotherBoard ,Cpu,Fan Temparature

  • Clint RutkasClint I'm a "developer"

    @Paul, looking at this, I'm not sure this really calculates power but more resource usages on the computer.  Not sure without doing some major research if that is possible.  Easy way is to get a "Kill-A-Watt" and alter it so it connects to your computer

  • PaulPaul

    I am in need of a piece of code which does this but also measures the usage in watts? is there anything like this out there

Remove this comment

Remove this thread

close

Comments Closed

Comments have been closed since this content was published more than 30 days ago, but if you'd like to continue the conversation, please create a new thread in our Forums,
or Contact Us and let us know.