Using WMI for system Metrics efficiantly

G

Guest

Hello all. I'm writting a little application to help me expand my knowledge
in a few different areas. The app is, as you might of guessed from the
subject, a system metrics display using WMI. For now I'm doing it with
Windows Forms, but later on I will be changing it to use DirectX's Overlays.

The question I have right now is how to get the information gathered from
WMI to easily, and efficiantly, update what is shown. I've tried using the
OnPaint method of the form to call an updating function to show the newest
information, but this turned out to be *very* slow. It does work, it just
takes ~15 seconds before it will show an update. This is unacceptable
concidering I'm using a 2.5GHz machine to test this on!

Below is the code I have so far:

using System;
using System.Collections;
using System.Collections.Generic;
using System.ComponentModel;
using System.Data;
using System.Drawing;
using System.Management;
using System.Text;
using System.Windows.Forms;

namespace BNetSysMetrics
{
public partial class Form1 : Form
{
public Form1()
{
InitializeComponent();
}

protected override void OnPaint(PaintEventArgs e)
{

UpdateStats();
base.OnPaint(e);
this.Invalidate();
}

private bool UpdateStats()
{
#region Management Object Init
ManagementObjectSearcher sysInfoQuery = new
ManagementObjectSearcher("SELECT * FROM Win32_OperatingSystem");
ManagementObjectCollection sysInfo = sysInfoQuery.Get();

ManagementObjectSearcher diskInfoQuery = new
ManagementObjectSearcher("SELECT * FROM Win32_LogicalDisk");
ManagementObjectCollection diskInfo = diskInfoQuery.Get();

ManagementObjectSearcher cpuInfoQuery = new
ManagementObjectSearcher("SELECT * FROM Win32_Processor");
ManagementObjectCollection cpuInfo = cpuInfoQuery.Get();

ManagementObjectSearcher networkInfoQuery = new
ManagementObjectSearcher("SELECT * FROM Win32_NetworkAdapter");
ManagementObjectCollection networkInfo = networkInfoQuery.Get();
#endregion

foreach (ManagementObject manObj in cpuInfo)
{
try
{
txtCPUClock.Text =
manObj["CurrentClockSpeed"].ToString() + "MHz";
txtCPULoad.Text = manObj["LoadPercentage"].ToString() +
"%";
}
catch (Exception)
{
}
}

foreach (ManagementObject manObj in sysInfo)
{
txtSysName.Text = manObj["CSName"].ToString();
txtRAMFree.Text =
Convert.ToUInt64(manObj["FreePhysicalMemory"].ToString()) / 1000 + "MB";
txtRAMTotal.Text =
Convert.ToUInt64(manObj["TotalVirtualMemorySize"].ToString()) / 1000 + "MB";
txtVirtualMemoryFree.Text =
Convert.ToUInt64(manObj["FreeVirtualMemory"].ToString()) / 1000 + "MB";
txtVirtualMemoryTotal.Text =
Convert.ToUInt64(manObj["TotalVisibleMemorySize"].ToString()) / 1000 + "MB";
}

return true;
}
}
}


If anyone can point me in the right direction to get this up to speed, I'd
be very appreciative.

Many thanks
 
W

Willy Denoyette [MVP]

Most of the data you are retrieving is static, why do you need to get
properties of classes like Win32_NetworkAdapter, LogicalDisk,
Win32_Processor which rarely or never change.
Anyway, a LogicalDisk query is expensive when the system contains a floppy
drive, so I would suggest you to refine your query and exclude the flopy
drive(s).

Willy.

| Hello all. I'm writting a little application to help me expand my
knowledge
| in a few different areas. The app is, as you might of guessed from the
| subject, a system metrics display using WMI. For now I'm doing it with
| Windows Forms, but later on I will be changing it to use DirectX's
Overlays.
|
| The question I have right now is how to get the information gathered from
| WMI to easily, and efficiantly, update what is shown. I've tried using the
| OnPaint method of the form to call an updating function to show the newest
| information, but this turned out to be *very* slow. It does work, it just
| takes ~15 seconds before it will show an update. This is unacceptable
| concidering I'm using a 2.5GHz machine to test this on!
|
| Below is the code I have so far:
|
| using System;
| using System.Collections;
| using System.Collections.Generic;
| using System.ComponentModel;
| using System.Data;
| using System.Drawing;
| using System.Management;
| using System.Text;
| using System.Windows.Forms;
|
| namespace BNetSysMetrics
| {
| public partial class Form1 : Form
| {
| public Form1()
| {
| InitializeComponent();
| }
|
| protected override void OnPaint(PaintEventArgs e)
| {
|
| UpdateStats();
| base.OnPaint(e);
| this.Invalidate();
| }
|
| private bool UpdateStats()
| {
| #region Management Object Init
| ManagementObjectSearcher sysInfoQuery = new
| ManagementObjectSearcher("SELECT * FROM Win32_OperatingSystem");
| ManagementObjectCollection sysInfo = sysInfoQuery.Get();
|
| ManagementObjectSearcher diskInfoQuery = new
| ManagementObjectSearcher("SELECT * FROM Win32_LogicalDisk");
| ManagementObjectCollection diskInfo = diskInfoQuery.Get();
|
| ManagementObjectSearcher cpuInfoQuery = new
| ManagementObjectSearcher("SELECT * FROM Win32_Processor");
| ManagementObjectCollection cpuInfo = cpuInfoQuery.Get();
|
| ManagementObjectSearcher networkInfoQuery = new
| ManagementObjectSearcher("SELECT * FROM Win32_NetworkAdapter");
| ManagementObjectCollection networkInfo =
networkInfoQuery.Get();
| #endregion
|
| foreach (ManagementObject manObj in cpuInfo)
| {
| try
| {
| txtCPUClock.Text =
| manObj["CurrentClockSpeed"].ToString() + "MHz";
| txtCPULoad.Text = manObj["LoadPercentage"].ToString() +
| "%";
| }
| catch (Exception)
| {
| }
| }
|
| foreach (ManagementObject manObj in sysInfo)
| {
| txtSysName.Text = manObj["CSName"].ToString();
| txtRAMFree.Text =
| Convert.ToUInt64(manObj["FreePhysicalMemory"].ToString()) / 1000 + "MB";
| txtRAMTotal.Text =
| Convert.ToUInt64(manObj["TotalVirtualMemorySize"].ToString()) / 1000 +
"MB";
| txtVirtualMemoryFree.Text =
| Convert.ToUInt64(manObj["FreeVirtualMemory"].ToString()) / 1000 + "MB";
| txtVirtualMemoryTotal.Text =
| Convert.ToUInt64(manObj["TotalVisibleMemorySize"].ToString()) / 1000 +
"MB";
| }
|
| return true;
| }
| }
| }
|
|
| If anyone can point me in the right direction to get this up to speed, I'd
| be very appreciative.
|
| Many thanks
 
G

Guest

I would assume that Win32_Processor would not have static information, as
part of it's info is the current load. The current clock also changes (for
example with a laptop, the CPU clock is adjusted according to load).

As for the NetworkAdapter section, I'm removing that as the WMI isn't
complete in regards to what I was looking to poll.

What would be a better way of polling for new information than overriding
the OnPaint method? That's my main question really. Would following the
asynchronous examples in the MSDN library be the best bet?
 
W

Willy Denoyette [MVP]

|I would assume that Win32_Processor would not have static information, as
| part of it's info is the current load. The current clock also changes (for
| example with a laptop, the CPU clock is adjusted according to load).

Only 6 properties of a total of 32 of this class are dynamic....
Retrieving all dynamic properties of this class takes a couple of seconds as
the implementation must calculate the loadpercentage by taking two
snapshots, querying the static properties only take a couple of msecs.


|
| As for the NetworkAdapter section, I'm removing that as the WMI isn't
| complete in regards to what I was looking to poll.
|
| What would be a better way of polling for new information than overriding
| the OnPaint method? That's my main question really. Would following the
| asynchronous examples in the MSDN library be the best bet?
|

Much better is to implement a timer based (using a server timer) WMI query,
note that this will run on a worker thread, so you need to marshal the
update of the UI using Control.Invoke/BeginInvoke.



| "Willy Denoyette [MVP]" wrote:
|
| > Most of the data you are retrieving is static, why do you need to get
| > properties of classes like Win32_NetworkAdapter, LogicalDisk,
| > Win32_Processor which rarely or never change.
| > Anyway, a LogicalDisk query is expensive when the system contains a
floppy
| > drive, so I would suggest you to refine your query and exclude the flopy
| > drive(s).
| >
| > Willy.
 
Top