Introduction
This tool displays information about system resources and settings. It uses WMI instrumentation and Windows API for the queries. At the moment, the following system information is available for display:
- Free physical memory
- Free virtual memory
- Free paging area
- Free disk space (text or bar graph)
- IP-addresses
- Subnet mask
- MAC-addresses
- Default gateway
- Network throughput (text and graph)
- Host name
- User
- Boot date
- OS version
- Service Pack
- CPU usage (text and graph)
- Top 5 processes (memory usage)
- Top 5 processes (CPU usage)
- Disk usage (text and graph)
- Battery status (text and graph)
Further features:
- Freely positionable on the desktop
- Different background settings (transparent/gradient/texture)
- Support for loading background images
- Support for manipulating background image colors
- Modifiable font settings
- Modifiable refresh time
- The HDD bar graph includes a simple directory browser
- Displaying active network connections with options like tracert, whois, close port
Background
This program is based on my first program here: WMIInfo. It's now almost 2 years ago that I started it. During that time, many ideas came up in my mind about how to extend and enhance the program. As I made many changes to the original program and also used different methods than just WMI instrumentation, I decided to give the program a new, more common name... I still have to say (like I did the first time here), you shouldn't expect too much with regard to the code - though I hope my coding has improved . And surely, any suggestions, corrections, or improvements are very welcome!
Using the Program
The first time you start the program, you have to set up the information to be displayed.
The right row shows the available functions. You can add them to the Active Function list by double-clicking, or remove them from the list also by double-clicking.
You can assign individual font types and colors to each function by unchecking "global". Again, the function you want to assign a different font / color has to be selected in the Active Functions list. Assign the font / color by clicking the arrow-button next to the font-button.
- Order: The functions will be displayed in the order in which they show up in the list. You can change the order with the arrow buttons on the bottom of the form.
- Titles: You can add an individual title to most of the active functions. Just select the function, write a title in the left title field, and assign the title by clicking the arrow button beside it.
- Text colors and font type: Text color and font type apply to the whole form when "global" is checked.
- Refresh rate: The refresh rate refers to the form and some functions. It's a global setting for how often the controls of the form are refreshed. Nevertheless, the "usage" functions have their own refresh rate setting.
- Background gradient: This applies to the gradient for the background color of the whole form. You can assign two colors and the angle for the gradient. The background itself is selected by the context menu of the form - I'll refer to that later below.
- 'Aero'-glass: This applies to Vista through Win8 with DWM ('Aero') enabled. If you have the 'aero effect' enabled in the 'background' settings, you can change here the color of the glass.
Selecting 'transparent' sets the background color to the transparency key of the form. In Vista/Win7 it will result in the usual glass look. In Win8 it results in a real transparent look - but renders much better than the 'normal' transparency on coloured backgrounds.
- textured: the glass-effect is overlayed by a texture (see 'background' tab)
- gradient: the glass-effect is overlayed by a gradient (see above 'background gradient')
- transparency: the level of transparency of the selected texture/gradient
Extended Settings
The extended settings refer to the usage functions like CPU usage, network usage, and disk usage. All these functions have a little graph that shows the usage over a certain time span, and is updated at a given refresh rate. The refresh rate is in milliseconds, and the time span in seconds.
Keep in mind that a higher refresh rate (e.g a smaller value) consumes more CPU power.
For Win7 and Vista, the text shows the CPU number followed by the core number, and its actual speed followed by the actual usage.
The text always starts with the name of the network connection and its actual speed setting. (I'm not quite sure, but I think the speed for the wireless connections is reduced by the router when not in use over a certain time.)
The throughput graph shows the sent and received bytes per second. This graph is auto-scaled as you won't see much when scaled to the maximum possible speed. The graph for received bytes is light blue, while for sent bytes is turquoise.
'Disable Adapter' holds a list of your network devices. You may disable them here to not show up in the tool window.
As an additional feature, double clicking the network label opens a window showing the active network connections:
LEFT clicking an remote address opens a context menu with the options:
- trace route
- trace route with resolving the ip-address - this might be very slow!
- getting the whois info - the query is processed by the "GeekTools Whois Proxy" - it automatically queries the related top level domain services
- terminating the connection either by closing the remote port or by closing the connection
LEFT clicking a PID or process gives you the option to terminate the process.
This tool is still a little bit buggy as for example when sorting the list by clicking the header, the returned value of an selected entry is still the value of the original field...
- Disk usage: Shows the read and write usage of a hard disk or a removable device like a USB stick.
The first line shows the drive number followed by its partitions.
The y-scale of the graph is set to ~100Mbytes/sec.
- HDD-Bar: The HDD-bar is an alternative to the free disk space text display.
You can assign three colors for the thresholds of 0-50, 50-75, and 75-100%. It may be a little bit confusing, but these thresholds refer to the used space - while the text values refer to the free space.
An additional feature of the HDD-bar is a little file browser. You can open it by left clicking on the bar:
It acts like the Windows file browser: double click opens files and folders, right click opens the context menu. Shift and Ctrl keys are also supported.
You can test your settings with the Test button. 'Restore' restores the settings to the last saved state. Apply the settings and close the form with the 'Apply' button.
Background Settings
I've implemented some little image manipulating: You can load an image as background for the tool and manipulate the colors.
Most image file types are supported.
Don't forget to use only small images!
The color manipulation is done by the colormatrix class. Here's a good example.
- "refresh" refreshes the form controls
- "settings": "change" opens the settings dialog - "delete" restores the initial settings
- "language" changes the language of the control (English/German available)
- "border" adds a border to the form -- (thin/thick/AeroSpecial) available -- enable/disable by clicking on "border"
- "background" options: transparent / aero / gradient / texture (blue / grey)
- "locked" at moment disables movement of the form
|
|
|
|
Transparent | Aero glass effect (Vista/7) | Gradient | Texture and thick border |
Points of Interest
First of all, I'd like to say thanks to all the programmers out there who enabled me to write this program because they shared their ideas and code! To mention some of them:
Inspiration for the file browser and shell context menu:
Finally, I want to mention some basic concepts and interesting ideas I found out. I can't go too much into the details as it would go far beyond the scope of this article.
In my first program WMIInfo
, I only made use of one label for displaying the system info. But as I wanted to use graphs and bars, I had to change the concept for displaying the info. At the moment, I have 20 predefined labels on the form which are placeholders for the functions.
As I made more and more use of dynamically created controls, I found these labels could be created dynamically when needed. But it is too much effort in my opinion at the moment...
When starting the program, we load the stored settings from the settings file.
VARIABLE = Properties.Settings.Default.[Name of property]
There is a global array for the functions (iFunction[]
) and a global array for the label controls (cLabel[]
).
The active functions have a value greater than -1
, and provide the order of appearance. The index of the function array refers to the function itself:
Index | Function |
0 | free memory |
1 | free virtual memory |
2 | free paging memory |
3 | free disk space |
4 | network (settings and usage) |
5 | hostname |
6 | username |
7 | boot date |
8 | OS version |
9 | service pack |
10 | CPU usage |
11 | top 5 processes (memory usage) |
12 | top 5 processes (CPU usage) |
13 | disk usage |
14 | battery status |
In order to display a graph instead of a text label, I made use of panels. The panels get a text label and a graph control assigned. The panel itself replaces the label in the control array. I found out that I can assign controls to a label, which I think is funny. But the drawback is that the autosize
property doesn't work for these controls.
The raw workflow of the program is like follows:
- Load settings
- Initiate arrays for labels and functions
- Initiate selected functions
- Process the functions periodically
WMI Queries
The processing of the WMI data is like this:
string query = "SELECT * FROM Win32_OperatingSystem";
ManagementObjectSearcher seeker = new ManagementObjectSearcher(query);
ManagementObjectCollection oReturnCollection = seeker.Get();
foreach (ManagementObject m in oReturnCollection)
{
VARIABLE = m["NAMEOFPROPERTY"];
(...)
}
For the top 5 processes, I used a LINQ query:
var query = (from p in System.Diagnostics.Process.GetProcesses()
orderby p.PrivateMemorySize64 descending
select p)
.Skip(0)
.Take(5)
.ToList();
string s = "",t = "";
foreach (var item in query)
{
s += item.ProcessName + "\r\n";
t += CalcSize(item.PrivateMemorySize64.ToString(), 1) + "\r\n";
}
Classes
As I started using classes, I made global references to classes. Meanwhile, I found out that I could assign a class to a panel control and refer to it later without a global reference. Don't know if this is a better way....
I made the classes mostly independent... some have references to timer settings - if you replace these, you should be able to use them independently of this project.
Classes in this project:
To get the adapter names, you need to query Win32_NetworkAdapterConfiguration
like this:
string[] s = new string[0];
string query = "SELECT * FROM Win32_NetworkAdapterConfiguration " +
"WHERE IPEnabled = TRUE";
ManagementObjectSearcher seeker = new ManagementObjectSearcher(query);
ManagementObjectCollection oReturnCollection = seeker.Get();
foreach (ManagementObject m in oReturnCollection)
{
try
{
Array.Resize(ref s, i + 1);
s[i] = Networkadapter(m);
i++;
}
catch
{ }
}
You can get the names from the Registry like this:
public string Networkadapter(ManagementObject m)
{
RegistryKey rK = Registry.LocalMachine;
string s = "";
s = m["SettingID"].ToString();
RegistryKey rSub = rK.OpenSubKey("SYSTEM\\CurrentControlSet\\Control" +
"\\Network\\{4D36E972-E325-11CE-BFC1-08002BE10318}\\" +
s + "\\Connection");
s = rSub.GetValue("Name").ToString();
return s;
}
Finally, you can init the label and the graph for each adapter in a loop with:
for (int k = 0; k < monitor.Adapters.Length; k++)
{
netLoad[k] = new NetworkWorkload(s[k], monitor.Adapters[k]);
monitor.Adapters[k].init();
pNet.Controls.Add(netLoad[k].Label);
pNet.Controls.Add(netLoad[k].NetGraphControl);
}
where netload[]
is an array of Networkload
class, and pNet
is a panel control containing the label and the graph.
If the network configuration changes, e.g., when dis-/connecting the WLAN or LAN, you need to re-init the monitors. With the event NetworkChange.NetworkAddressChanged += new NetworkAddressChangedEventHandler(AddressChangedCallback);
, you can detect changes.
CPUProcess
: CPU usage - Top 5 list of processes
- Init with
processlist pList = new processlist();
- Retrieve list with
System.Collections.IList iList = pList.top5_list;
CPUWorkload
: Displays CPU usage as text and graph
- Init with
CPUWorkload CWL = new CPUWorkload({bool bGraph}, {bool bText});
- Retrieve the text in a label control:
CWL.Label
- Retrieve the graph:
CWL.CPUGraphControl
DiskUsage
: throughput of disks as text and graph
- Init with
DiskUsage du = new DiskUsage("part of the name of the physical partition - like C:")
- Retrieve the text in a label control:
du.Label
- Retrieve the graph:
du.DiskGraphControl
NetworkAdapter
/ NetworkMonitor
/ NetworkWorkload
: Network throughput as text and graph
- Init the adapter with
NetworkMonitor monitor = new NetworkMonitor({Array of networkadapter names from registry});
Windows API Stuff
It was quite interesting for me to discover the possibilities of using the Windows API. Though implementing the file browser was quite hard, it also was a great joy as it worked. " />
Here are a few nice little helpers.
Disk Change Notification
In order to receive notifications of hardware events, you can override window messages:
private const UInt32 WM_DEVICECHANGE = 0x0219;
private const UInt32 DBT_DEVICEARRIVAL = 0x8000;
private const UInt32 DBT_DEVICEREMOVECOMPLETE = 0x8004;
protected override void WndProc(ref Message m)
{
if (iFunction[13] > -1 && m.Msg == WM_DEVICECHANGE) {
if (m.WParam.ToInt32() == DBT_DEVICEARRIVAL ||
m.WParam.ToInt32() == DBT_DEVICEREMOVECOMPLETE)
{
Action action = _diskusage_init;
this.BeginInvoke(action);
}
}
base.WndProc(ref m);
}
First, I ran into some problems when calling the method "_diskusage_init()
" directly here. It gave me some strange messages of a disconnected COM object. By Googling, I found that it was a thread invoke problem. I solved this with these two lines:
Action action = _diskusage_init;
this.BeginInvoke(action);
Instead of these, I first used the "traditional" delegate method. But then I came to the idea of trying it like this, as I've used the "ExecuteThreadSafe
" method by Marcell Spies at other places.
public static class ControlExtensions
{
public static void ExecuteThreadSafe(this Control control, Action action)
{
if (control.InvokeRequired)
{
control.BeginInvoke(action);
}
else
{
action.Invoke();
}
}
}
Interestingly, the runtime even didn't "know" that it has to be invoked, because I had the same error when calling it with this.ExecuteThreadSafe(_diskusage_init);
. So I forced it without querying InvokeRequired
. This still leaves the question of why we need to declare a global delegate and a delegate method when the same is done with "Action", which is also a delegate.
Aero Glass Effect (Vista and Higher)
With Vista, Microsoft introduced the DesktopWindowManager
(DWM) with some nice effects like Aero Glass blurring the background of the form. To make this work, Aero and transparency has to be enabled for the system. First, you need to import some methods from dwmapi.dll:
[System.Runtime.InteropServices.DllImport("dwmapi.dll", PreserveSig = false)]
public static extern bool DwmIsCompositionEnabled();
[System.Runtime.InteropServices.DllImport("dwmapi")]
private static extern int DwmEnableBlurBehindWindow(
System.IntPtr hWnd, ref DWM_BLURBEHIND pBlurBehind);
[System.Runtime.InteropServices.DllImport("gdi32.dll")]
static extern IntPtr CreateRectRgn(int x1, int y1, int x2, int y2);
public struct DWM_BLURBEHIND
{
public int dwFlags;
public bool fEnable;
public System.IntPtr hRgnBlur;HRGN
public bool fTransitionOnMaximized;
}
As this form is not static, but dynamically fits its size to changes like new disk drive or network connection, the rendering of the glass effect has to be initialized each time the form resizes - at least this is the way I managed to make it work; there's probably a better solution.
private void Form1_Resize(object sender, EventArgs e)
{
if (System.Environment.OSVersion.Version.Major >= 6 &&
DwmIsCompositionEnabled())
{
IntPtr hr = CreateRectRgn(0, 0, this.Width, this.Height);
DWM_BLURBEHIND dbb;
dbb.fEnable = bAero;
dbb.dwFlags = 1 | 2;
dbb.hRgnBlur = hr;
dbb.fTransitionOnMaximized = true;
DwmEnableBlurBehindWindow(this.Handle, ref dbb);
this.Invalidate();
System.Runtime.InteropServices.Marshal.Release(dbb.hRgnBlur);
}
else
{
this.Invalidate();
}
}
When the effect is en-/disabled, you need to recreate the form to apply the change properly:
this.RecreateHandle();
With Win8 microsoft changed a lot of things - by chance I found out that DwmEnableBlurBehindWindow hasn't been removed or disabled. It's not exactly the same glass effect as in Win7 as there are no shades.
In order to make it work we need another API call:
[DllImport("dwmapi.dll")]
public static extern void DwmExtendFrameIntoClientArea(IntPtr hWnd, Margins pMargins);
[StructLayout(LayoutKind.Sequential)]
public class Margins
{ public int cxLeftWidth, cxRightWidth, cyTopHeight, cyBottomHeight;}
So I had to extend the resize event a little bit:
if ((System.Environment.OSVersion.Version.Minor >= 2)|(bFrameChecked&&!bFrameType&&bAeroFrame))
DwmExtendFrameIntoClientArea(Handle, new Margins { cxLeftWidth = -1, cxRightWidth = -1, cyTopHeight = -1, cyBottomHeight = -1 });
Again by chance I found out that applying this to a window with 'thick border' and aero enabled in Win7 it results in a nice shaped glassy tile - therefor I added the option 'Aero special' in the border menu.
After playin' a while with aero I added the 'gradient' and 'textured' feature.
The implementation of the gradient feature is quite simple: you just need to add/adjust the alpha channel to the gradient colors
if (bAero & bAeroGradient)
{
c1 = Color.FromArgb((byte)(fAeroTransparency * (float)255),c1);
c2 = Color.FromArgb((byte)(fAeroTransparency * (float)255),c2);
}
The texture feature needs a little bit more work. I found a nice method by Jan Romell. The code loops over every pixel and changes the alpha channel. This works for all images with non indexed colors.
ImageBack = ChangeImageOpacity(ImageBack, fAeroTransparency);
So the background image's transparency is set before the 'e.Graphics.DrawImage' command.
Transparent Border
I struggled a bit with drawing rounded shapes or borders. I first tried some ideas with rounded rectangles and clipping, but the result wasn't as expected. Finally, I found several articles about overriding CreateParams
and manipulating the form style, and ended up with this quite simple method:
public const int WS_EX_TOOLWINDOW = 0x00000080;
public const int WS_THICKFRAME = 0x800000;
public const int WS_BORDER = 0x00040000;
protected override CreateParams CreateParams
{
get
{
new System.Security.Permissions.SecurityPermission(
System.Security.Permissions.SecurityPermissionFlag.UnmanagedCode).Demand();
CreateParams cp = base.CreateParams;
if (bFrameChecked)
{
cp.Style |= bFrameType ? WS_THICKFRAME : WS_BORDER;
}
cp.ExStyle |= WS_EX_TOOLWINDOW;
return cp;
}
}
By setting WS_EX_TOOLWINDOW
, the app doesn't show up in the ALT-TAB list and also not in the applist of Task Manager.
This leads me to transparency in general: in order to make the form transparent, you first need to set SetStyle(ControlStyles.SupportsTransparentBackColor, true);
to enable transparency.
Then you need to select a certain color for transparency in the form properties and set the background color of the form to the selected color. The drawback of this method is that whenever this color appears in your controls, it will be transparent!
this.BackColor = System.Drawing.Color.FromArgb(2, 2, 2);
this.TransparencyKey = System.Drawing.Color.FromArgb(2, 2, 2);
Another drawback is, if you disable any borders of the form like I did here, you won't be able to move it anymore. " /> The solution is to make the background opaque when entering the form and then to call mouse events when pushing the mouse button and moving the mouse.
Moving a Transparent Borderless Form
private Point m_offset;
private Point m_Pos;
private void EM_MouseMove(object sender, MouseEventArgs e)
{
if (e.Button == MouseButtons.Left)
{
m_Pos = Control.MousePosition;
m_Pos.Offset(m_offset.X, m_offset.Y);
Location = m_Pos;
}
}
private void EM_MouseDown(object sender, MouseEventArgs e)
{
int x1,x2,y1,y2,dx,dy;
x1 = Location.X;
y1 = Location.Y;
x2 = -MousePosition.X;
y2 = -MousePosition.Y;
dx = x1 + x2;
dy = y1 + y2;
m_offset = new Point(dx,dy);
}
Some Things that Don't Work Properly
The different classes for usage and with a graph are running in background tasks. Therefore the controls of the form probably won't show up in the right position immediately when testing or applying the settings as the classes might not be fully initialized when the form is updated. It corrects itself with the next refresh. Nevertheless, sometimes you have to refresh it manually as a workaround.
When the background is set to transparent, the text doesn't render nicely on a desktop with many different colors like it is in a picture. I think it has to do with the transparency of the text labels and anti-aliasing as you can see small artifacts of the form-background color around the letters.
EDIT: As mentioned above, in Win8 having the 'Aero effect' enabled and the background color set to the transparency key, the text renders really nice.
XP
I've tested the program on XP and found out that some WMI classes aren't available, like Win32_PerfFormattedData_Counters
that I used for getting the CPU details. Therefore, I had to do a workaround by querying the OS version and using different WMI-classes for the query (like Win32_PerfFormattedData_PerfOS_Processor
for the CPU usage, and Win32_Processor
for the CPU details).
Finally, it showed up that querying "Win32_Processor
" takes quite long and eats up a lot of CPU power. So, if you use XP, I would suggest disabling the CPU text when showing the CPU usage. Interestingly, I had to switch to the same class on a quad core machine (running Win7) as it always showed zero for the current CPU speed, while on a dual core, it's working without problems - anyone has any idea why this happens (might be because the quad is a 64 bit processor)?
History
- 8 Sep 2010: SYSInfo 1.0
- 19 Sep 2010:
- Added battery status on request from sam.hill.
- Fixed the settings dialog showing wrong language when changing language after settings were made.
- Added a "locked" option on request from JF2015. (Still working on a 'true' lock option.)
- Removed the app from ALT-TAB list on request from JF2015.
- Added a 'delete' option in context menu - settings. In case the settings file is screwed up, you can now restore the initial settings by this.
- 25 Sep 2010:
- 3 Jan 2013:
- Some Bugfixes e.g. unhandled exception when a drive reports '0' as free space (mainly CDs)
- extended tooltips for the filebrowser
- Added 'aero' glass support for Win8
- Added a color option for the 'aero' glass background
- 6 Jan 2013:
- changed the NeutralResourcesLanguage to 'english' - so You won't be bothered with 'German' if the UI falls back to the neutral language " src="http://www.codeproject.com/script/Forums/Images/smiley_wink.gif" />
- 7 Jan 2013:
- changed default language in settings.settings to 'EN-gb'
- added a list for hiding/disabling HDDs - might be useful when cardreaders installed
- 13 Jan 2013
- After playin' a while with the 'aero' stuff in win8 I finally added a few options:
- textured: the glass-effect is overlayed by a texture
- gradient: the glass-effect is overlayed by a gradient
- transparency: make the overlay transparent - well, this is the main feature
I didn't try it on win7, yet - so I really can't say @moment how it looks there or if it even works...maybe tomorrow @office on the secretary's pc " src="http://www.codeproject.com/script/Forums/Images/smiley_wink.gif" />
- 30 Jan 2013
- minor bugfix for the filebrowser - wrong property has been selected when rightclicking on a file
- 28 Feb 2014
- minor bugfixes
- added option "total" for CPU usage graph&text display / so it shows only the average usage and not all the single cores
- tried a workaround for the new "behaviour" of win8 regarding the logged startup time
- changed the scale for diskusage graph from fixed value to "auto"
- 18 Apr 2014
- Bugfix: on some systems, the app freezes because of a timing problem with the RPC service. I think its even worse with windows 8x. Probably they changed something with the handling of the calls...?
Therefore I added a lock for all the wmi queries:
private ReaderWriterLockSlim dcLock = new ReaderWriterLockSlim();
if (dcLock.TryEnterWriteLock(50))
(...)
I've watched it a couple of days and it seems to work now.... - Added a zip-file preview for the folder browser - extracting is not implemented, yet.
-
- 28 Oct 2014
- minor update - the whois service provider "whois-server(s).net" seems no longer to accept queries - switched to "whois.ripe.net"
btw. does anyone know a good & free whois service provider like ripe? Ripe is really fast, but it does not host all records....
- 04 Aug 2015
- 07 Aug 2015
- Made some cosmetic fixes
- the settings window will open now near the screen center (on high res displays with dpi scaling the top of the window moved outside the screen)
- new background for the graphs
- scaling of the bars should be uniform now
I'm still working on solving the dpi scaling problem on high res displays
As mentioned above, You should also try to disable the dpi-scaling in the compatibility settings of the app.
- 10 Aug 2015