(read the Part 1 of the series here)
PowerShell opens up a lot of possibilities when it comes to task automation. And while it’s often been used to automate currently pending tasks, it can also be used for continuously monitoring things in the background.
Whether you need to monitor the utilization of a file server at work or the temperature of your hard drives at home, the principle is always the same: the tool or script should work silently and invisible in the background, until there is something you need to know.
The Notify Icon
With the NotifyIcon class you can display an icon in the system tray, which can be equipped with a tooltip text for on demand status information:
and a balloon tip to get your attention when necessary:
You also can change the icon at any time when necessary, to further visualize a status message for example. And the best is, that the usage of the NotifyIcon class is –as you will see- very easy.
The Timer Object
It’s in the nature of a monitoring script, that most of the time it has nothing to do. It checks something, waits a little, checks it again, waits a little and so on. The easiest way to accomplish such behavior, is to use a loop and the start-sleep command.
Don’t do that.
In fact, whenever you’re using a GUI in your script, you should completely forget that the start-sleep command exists. Because this “sleep” isn’t like a little nap, its more like a deep hibernation. And as long as your script does that, your GUI will completely freeze and it won’t react to any events (remember those?), like a mouse click for example, because you decided that you would like to end the script.
A much more elegant way to let a script do stuff from time to time, is a Timer Object. You simple create it, set a time interval and whenever its time is up, an event gets raised and it executes the code you specified, e.g. calls a function. In between those times, while it does nothing, your GUI stays responsive and can react to other events. Sounds better, doesn’t it?
Alright, enough theory, lets see them in action:
Example: HDD Health Monitor
For this example we will create something that a lot of people have running on their desktops: a little HDD Health Monitor. They are usually based on the S.M.A.R.T. data, which every modern hard drive can report.
For the purpose of this script, we will focus on the hard disk temperature, but when we’re done, you will be able to easily amend it to monitor any other SMART attribute.
Reading the S.M.A.R.T. data
I originally intended to read the SMART attributes via WMI, but the trouble is, WMI reports only the plain values of all the different vendor specific attributes. Its basically just a bunch of numbers and you would need to find the right attribute among hundreds. While we could accomplish that, the real problem is, that which attributes are available and how they are implemented is drive specific. And as we’re not trying to learn the specifics of S.M.A.R.T. implementations here, i decided to simply use Smartmontools. If you don’t know it, its a free, small and easy to use command line tool to read SMART data and it does it’s job very good.
You can download it from here.
After you installed it, type “smartctl -a /dev/hda” in a new console window. The “a” in “hda” stands for the first hard drive in your system, so the second (if present) would be “hdb” and so on. Note that these letters don’t correlate in any way with the drive letters in Windows explorer.
The lower part of the output should look like this:
[...] Vendor Specific SMART Attributes with Thresholds: ID# ATTRIBUTE_NAME FLAG VALUE WORST THRESH TYPE WHEN_FAILED RAW_VALUE 1 Raw_Read_Error_Rate 0x000f 117 099 000 Pre-fail - 140339126 3 Spin_Up_Time 0x0003 091 091 000 Pre-fail - 0 4 Start_Stop_Count 0x0032 100 100 000 Old_age - 271 5 Reallocated_Sector_Ct 0x0033 100 100 000 Pre-fail - 0 [...] 190 Airflow_Temperature_Cel 0x0022 058 052 000 Old_age - 42 (Lifetim e Min/Max 24/43) 194 Temperature_Celsius 0x0022 042 049 000 Old_age - 42 (0 16 0 0) 195 Hardware_ECC_Recovered 0x001a 049 038 000 Old_age - 140339126 197 Current_Pending_Sector 0x0012 100 100 000 Old_age - 0 198 Offline_Uncorrectable 0x0010 100 100 000 Old_age - 0 199 UDMA_CRC_Error_Count 0x003e 200 200 000 Old_age - 1 [...]
The value of attribute 194 (Temperature_Celsius) is what we need, so we write a short function that filters out all the rest:
(note that we’ll execute smartctl with the parameter “-A”, not “-a” as above, as this gives you just the SMART attributes without the rest of the drive info.)
Now we can read the temperature value of any disk in the system with the command FNreadtemp plus “a” or “b” etc.
This should work for most hard drives. If the smartctl output for your disk is different from the one above, you may need to adjust this function.
We will also need two status icons for our tool, one for normal and one high temperature. You can either go to your favorite drawing program and create them yourself, or you can select some from the tons of free ones that are available on the web. :)
Now lets create the form objects we need: an empty form window (necessary for the other objects), the notify icon, a context menu with a context menu entry (to have a way to end the script), the timer object and two icon objects (you need to change the path to where you put the ico files):
Because we won’t have any visible objects in our form, we should make sure that this empty form window is not visible. To make it disappear, we simply set its state to minimized and don’t allow it to be visible in the taskbar:
Then we set the properties for the notify icon. First we set the icon we assigned to the variable $iconOk, as the icon to use at the beginning. Because this will be our standard icon.
Next we assign the the context menu object ($ContextMenu) as the icons context menu. This context menu is invisible until we add at least one entry, in this case $MenuItem (which we will define later).
In opposite to most other form objects, the property that decides if an object is visible or not (“.visible”), is per default $false for NotifyIcon objects. So we need to set this to $true.
Next we define the Timer object:
The time after which an event gets raised (and the assigned code get executed) is defined in the property “.interval”. Its in milliseconds, so i did set it to 300000, which means that the timer will raise the event every 5 minutes. You can change that to every value you like, just be aware that smartctl should have time to start and return the output, so I’d say at least 3 seconds. And although smartctl has normally no measurable impact on the computers resources, if you call it every other second, it quite possibly will.
Then we add the event “Tick” and add a function call, for a function we will name “FNcheckSMART”. Last we start the timer.
The last object we need to define is the menu item, which we already assigned to the context menu of the icon. We add the text and the event which should be raised when you click on the item. When its been raised we want it to close the form (which will usually end the script), but we need to clean up first, which means stopping the timer and removing the icon. If you don’t do that, the icon will stay there until the taskbar gets refreshed by Windows.
Alright, thats the form, now we need a function that calls FNreadtemp for every hard disk and warn us if the temperature exceeds a certain value. The function looks like this:
– The function calls FNreadtemp for two hard disks (a and b) as an example. If you have more or less, you need to change that here. In case the temperature is 48° (random value, set it to another value that suits your needs) or higher its creates a warning text and assigns it to $warn.
– Then the tooltip text ($NotifyIcon.Text) for the icon gets created and assigned to the icon
– If $warn does exist, the icon will be changed to the warning icon, a BalloonTip will be displayed and the variable $warn will be removed
The .ShowBalloonTip method needs four parameters (in this order): how long (in milliseconds) the balloon tip should be displayed; the title; the text message; and one of four possible ToolTipIcons.
– If after the next interval $warn shouldn’t exist anymore, the icon will be changed back to the standard icon
As this is just an example, i kept this very simple. If you’re going to use this method for your own script, depending on the importance of the warning message, you might want to add sending an email or displaying a popup. In that case, you should change the function, so that you –despite the timer interval- get an email only every hour or so . :)
Ok, finally we need to display the form and call the SMART function once, so that the icons tooltip already has data when it’s displayed first:
And here is the whole script:
That’s it for today. As you can see, with just a few simple objects you can create a useful monitoring tool.
Monitoring software, especially for enterprises, is a huge market and they are often excessively expensive. But apart from the shiny surface, what they do is often quite simple. Powershell gives you the opportunity to create your own customized monitoring tool.
If you liked this tutorial or you have any wishes for the next Windows Forms tutorial or had any trouble with this one, please drop me a comment or send me an email.
Have a nice day,