Left Side of Comparison

When using comparison operators, always make sure the relevant part is placed left. That’s because PowerShell looks at the left side of an operator and may automatically change types of the right side. Also, comparison operators may work as filters when there is an array on the left side.

Check out the differences:

$array = @()

'-'* 80
$array -eq $null
'-'* 80
$null -eq $array
'-'* 80

Since you are comparing an array, the comparison operator works like a filter and returns nothing in the first comparison. When you swap operands, it returns $false which is the right answer: an array, even an empty array, is not null.


Twitter This Tip! ReTweet this Tip!

Subscribe to Lock and Unlock Events

Whenever a user locks his machine, and whenever a user unlocks a machine, Windows emits an event. PowerShell can subscribe to these events and do things, for example use the text-to-speech engine to emit greetings.

But there are a number of useful actions as well. Maybe you want to clean up when you lock your machine, close all Windows Explorer windows, start a backup, or do something else. Here is the sample code:

function Start-LogMessage {
  $null = Register-ObjectEvent -InputObject ([Microsoft.Win32.SystemEvents]) -SourceIdentifier SessSwitch -EventName "SessionSwitch" -Action {
    Add-Type -AssemblyName System.Speech

    $synthesizer = New-Object -TypeName System.Speech.Synthesis.SpeechSynthesizer
    
    switch($event.SourceEventArgs.Reason) {
'SessionLock'    
{ $synthesizer.Speak("Have a good one, $env:username!") }
'SessionUnlock'  
{ $synthesizer.Speak("Heya, nice to see you $env:username again!") }
    }
  }
}

function Stop-LogMessage {
    $events = Get-EventSubscriber -SourceIdentifier SessSwitch
    $jobs = $events | Select-Object -ExpandProperty Action
    $events | Unregister-Event
    $jobs | Remove-Job
}

Twitter This Tip! ReTweet this Tip!

Finding Installed Updates (Part 2)

The Windows Update Client maintains its own log of installed updates. Rather than querying the generic system event log, or actively searching for updates which can take some time, here is example code to query and read the installation history from your Windows Update Client:

$Session = New-Object -ComObject Microsoft.Update.Session
  $Searcher = $Session.CreateUpdateSearcher()
  $HistoryCount = $Searcher.GetTotalHistoryCount()
  $Searcher.QueryHistory(1,$HistoryCount) | 
  Select-Object Date, Title, Description, SupportUrl

Twitter This Tip! ReTweet this Tip!

Finding Installed Updates (Part 1)

Get-Hotfix returns installed hotfixes but really only is a wrapper around the Win32_QuickFixEngineering WMI class. It is not returning all installed updates.

A better way may be querying the event log:

Get-EventLog  -LogName System -InstanceId 19  |
    ForEach-Object {
        [PSCustomObject]@{
            Time = $_.TimeGenerated
            Update = $_.ReplacementStrings[0]
        }
    } 

Even this may not be complete, and event log entries can always be cleared. The only authoritative answer can come from your Windows Update Client which takes an actual look at the files present on your system:

$pattern = 'KBd{6,9}'

$UpdateSession = New-Object -ComObject Microsoft.Update.Session
$UpdateSearcher = $UpdateSession.CreateupdateSearcher()
$Updates = @($UpdateSearcher.Search("IsInstalled=1").Updates)
$Updates | ForEach-Object {
  $kb = 'N/A'
  if ($_.Title -match $pattern) { $kb = $matches[0] }
  [PSCustomObject]@{
    KB = $kb
    Title = $_.Title
  }
}

Twitter This Tip! ReTweet this Tip!

Finding Missing Updates

PowerShell can access the same logic that is used by the Windows Update Client, and query for missing updates:

$UpdateSession = New-Object -ComObject Microsoft.Update.Session
$UpdateSearcher = $UpdateSession.CreateupdateSearcher()
$Updates = @($UpdateSearcher.Search("IsHidden=0 and IsInstalled=0").Updates)
$Updates | Select-Object Title

Here is a more sophisticated approach returning update title and KB number (if available):


$UpdateSession = New-Object -ComObject Microsoft.Update.Session
$UpdateSearcher = $UpdateSession.CreateupdateSearcher()
$Updates = @($UpdateSearcher.Search("IsHidden=0 and IsInstalled=0").Updates)
$Updates | 
ForEach-Object {
    $pattern = 'KBd{6,9}'
    if ($_.Title -match $pattern)
    {
        $kb = $matches[0]
    }
    else
    {
        $kb = 'N/A'
    }
    [PSCustomObject]@{
        Title = $_.Title
        KB = $kb
    }
} | Out-GridView 

Twitter This Tip! ReTweet this Tip!