One of the most interesting WinForms classes is the NotifyIcon. With it your can create an icon in the System Tray area of the TaskBar. Combined with a Timer class object to countdown and raise events you can monitor and alert. The following PowerShell script monitors for a CapsLock change and alerts with a balloon from the system tray icon and a voice notification. At any time you can mouse over the icon for the current status. The icon also changes according to the CapsLock status with an up or down arrow for a visual reference.

First I setup the main form. It has no properties and is then hidden. I call a function to get the path from where the script is executing so I can find the icons.

function Get-ScriptDirectory { 
    $Invocation = (Get-Variable MyInvocation -Scope 1).Value
    $global:callPath = Split-Path $Invocation.MyCommand.Path
} # End Function Get-ScriptDirectory

# Main Form and Setup
$mainForm = New-Object System.Windows.Forms.form
$global:lastCapsLock = [System.Console]::CapsLock
$global:callPath = “”
Get-ScriptDirectory
# Hide Main Form
$mainForm.ShowInTaskbar = $false
$mainForm.WindowState = “minimized”

Next I added a Timer object. It will count down a predefined time and then process an event, calling the CapsLock status change function. The interval is in milliseconds. The function call in add_Tick is required to be in brackets.

# Timer Object
$timer = New-Object System.Windows.Forms.Timer
$timer.Interval = 2000
$timer.add_Tick({CapsLockCheck})
$timer.start()

I then define the NotifyIcon and make it visible. Within the NotifyIcon I define a ContextMenu which is visible on a right-click of the icon. I add a MenuItem to it for the Exit option. Lastly I define a click event action to clean up and exit the script.

# NotifyIcon Objects
$notifyIcon= New-Object System.Windows.Forms.NotifyIcon
$contextMenu = New-Object System.Windows.Forms.ContextMenu
$notifyIcon.ContextMenu = $contextMenu
$notifyIcon.Visible = $true
$menuItem1 = New-Object System.Windows.Forms.MenuItem -ArgumentList “Exit”
$contextMenu.MenuItems.Add($menuItem1)

# Add Exit MenuItem
$menuItem1.add_Click({
     $notifyIcon.Visible = $False
     $timer.stop()
     $mainForm.close()
})

I do an initial load of the values the NotifyIcon will use. I get the current CapsLock status and load up the correct text and icon.

# Inital Load
$capsLock = [System.Console]::CapsLock
if ($capsLock -eq $false) {
     $notifyIcon.Icon = $global:callPath + “\down.ico”
     $notifyIcon.Text = “CapsLock is Off”
}
if ($capsLock -eq $true) {
     $notifyIcon.Icon = $global:callPath + “\up.ico”
     $notifyIcon.Text = “CapsLock is On”
}

Next I setup the function checking for a CapsLock status change and popping up the alerts. I check the CapsLock status and compare it to a saved value from the previous check. If change I go to the appropriate routine and set the icon and text. I then set the balloons title, text, duration and icon. The icon can be one of a few standardized ToolTip icons like – info, warning, or error. I then set the text to be spoken and call the VoiceNotifcation function.

Function CapsLockCheck {
     $capsLock = [System.Console]::CapsLock
     if ($capsLock -eq $false) {
         if ($capsLock -ne $global:lastCapsLock) {
            $notifyIcon.Icon = $global:callPath + “\down.ico”
            $notifyIcon.Text = “CapsLock is Off”
            $notifyIcon.BalloonTipText = “CapsLock is Off”
            $notifyIcon.BalloonTipTitle = “CapsLock has Changed”
            $notifyIcon.BalloonTipIcon = “Info”
            $notifyIcon.ShowBalloonTip(10000)
            $spokenText = “Caps Lock is Off”
            $global:lastCapsLock = $capsLock
            VoiceNotification
         }
     }
     if ($capsLock -eq $true) {
         if ($capsLock -ne $global:lastCapsLock) {
            $notifyIcon.Icon = $global:callPath + “\up.ico”
            $notifyIcon.Text = “CapsLock is On”
            $notifyIcon.BalloonTipText = “CapsLock is On”
            $notifyIcon.BalloonTipTitle = “CapsLock has Changed”
            $notifyIcon.BalloonTipIcon = “Info”
            $notifyIcon.ShowBalloonTip(10000)
            $spokenText = “Caps Lock is On”
            $global:lastCapsLock = $capsLock
            VoiceNotification
         }
     }
} # End Function CapsLockCheck

The voice notification process just takes the passed text, and using the text to speech class, speaks it.

Function VoiceNotification {
     $Voice = New-Object System.Speech.Synthesis.SpeechSynthesizer
     $voice.rate = -1
     $voice.volume = 100
     [void] $voice.Speak($spokenText)
} # End Function VoiceNotification

Finally I start the NotifyIcon running using the WinForms Application class Run method.

[System.Windows.Forms.Application]::Run($mainForm)

Included in the zip file is also a command file to facilitate executing the script. It will start the script with a hidden PowerShell command window. There is also the two icon files. All the files should be in the same folder to run properly.

powershell.exe -windowstyle hidden -nologo -command .\CapsLockMonitor.ps1

CapsLockMonitor

CapsLockMonitor.zip

FYI – In the next blog I encode and embed the icons directly into the script file.

Advertisements