Welcome PowerShell User! This recipe is just one of the hundreds of useful resources contained in the PowerShell Cookbook.
If you own the book already, login here to get free, online, searchable access to the entire book's content.
If not, the Windows PowerShell Cookbook is available at Amazon, or any of your other favourite book retailers. If you want to see what the PowerShell Cookbook has to offer, enjoy this free 90 page e-book sample: "The Windows PowerShell Interactive Shell".
You want to respond automatically to a .NET, WMI, or engine event.
Use the -Action
parameter of the Register-ObjectEvent
, Register-CimIndicationEvent
, and Register-EngineEvent
cmdlets to be notified when an event arrives and have PowerShell invoke the script block you supply:
PS > $timer = New-Object Timers.Timer PS > $timer.Interval = 1000 PS > Register-ObjectEvent $timer Elapsed -SourceIdentifier Timer.Elapsed ` -Action { $GLOBAL:lastRandom = Get-Random } Id Name State HasMoreData Location -- ---- ----- ----------- -------- 2 Timer.Elapsed NotStarted False PS > $timer.Enabled = $true PS > $lastRandom 836077209 PS > $lastRandom 2030675971 PS > $lastRandom 1617766254 PS > Unregister-Event Timer.Elapsed
PowerShell’s event registration cmdlets give you a consistent way to interact with many different event technologies: .NET events, WMI events, and PowerShell engine events.
By default, when you register for an event, PowerShell adds a new entry to the sessionwide event repository called the event queue. You can use the Get-Event
cmdlet to see events added to this queue and the Remove-Event
cmdlet to remove events from this queue.
In addition to its support for manual processing of events, you can also supply a script block to the -Action
parameter of the event registration cmdlets. When you provide a script block to the -Action
parameter, PowerShell automatically processes events when they arrive.
However, doing two things at once means multithreading. And multithreading? Thar be dragons! To prevent you from having to deal with multithreading issues, PowerShell tightly controls the execution of these script blocks. When it’s time to process an action, it suspends the current script or pipeline, executes the action, and then resumes where it left off. It processes only one action at a time.
PS > $timer = New-Object Timers.Timer PS > $timer.Interval = 1000 PS > Register-ObjectEvent $timer Elapsed -SourceIdentifier Timer.Elapsed ` -Action { Write-Host "Processing event" } $timer.Enabled = $true PS > while($true) { Write-Host "Processing loop"; Sleep 1 } Processing loop Processing event Processing loop Processing event Processing loop Processing event Processing loop Processing event Processing loop (...)
Inside the -Action
script block, PowerShell gives your script access to five automatic variables:
$eventSubscriber
The subscriber (event registration) that generated this event.
$event
The details of the event itself: MessageData
, TimeGenerated
, etc.
$args
The arguments and parameters of the event handler. Most events place the event sender and customized event information as the first two arguments, but this depends on the event handler.
$sender
The object that fired the event (if any).
$eventArgs
The customized event information that the event defines, if any. For example, the Timers.Timer
object provides a TimerElapsedEventArgs
object for this parameter. This object includes a SignalTime
parameter, which identifies exactly when the timer fired. Likewise, WMI events define an object that places most of the information in the $eventArgs.NewEvent
property.
In addition to the script block that you supply to the -Action
parameter, you can also supply any objects you’d like to the -MessageData
parameter during your event registration. PowerShell associates this data with any event notifications it generates for this event registration.
To prevent your script block from accidentally corrupting the state of scripts that it interrupts, PowerShell places it in a very isolated environment. Primarily, PowerShell gives you access to your event action through its job infrastructure. As with other PowerShell jobs, you can use the Receive-Job
cmdlet to retrieve any output generated by your event action:
PS > $timer = New-Object Timers.Timer PS > $timer.Interval = 1000 PS > Register-ObjectEvent $timer Elapsed -SourceIdentifier Timer.Elapsed ` -Action { $SCRIPT:triggerCount = 1 + $SCRIPT:triggerCount "Processing Event $triggerCount" } PS > $timer.Enabled = $true Id Name State HasMoreData Location -- ---- ----- ----------- -------- 1 Timer.Elapsed NotStarted False PS > Get-Job 1 Id Name State HasMoreData Location -- ---- ----- ----------- -------- 1 Timer.Elapsed Running True PS > Receive-Job 1 Processing Event 1 Processing Event 2 Processing Event 3 (...)
For more information about working with PowerShell jobs, see Recipe 1.6.
In addition to exposing your event actions through a job interface, PowerShell also uses a module to ensure that your -Action
script block is not impacted by (and does not impact) other scripts running on the system. As with all modules, $GLOBAL
variables are shared by the entire session. $SCRIPT
variables are shared and persisted for all invocations of the script block. All other variables persist only for the current triggering of your event action. For more information about PowerShell modules, see Recipe 11.7.
For more information about useful .NET and WMI events, see Appendix I.
Recipe 1.6, “Invoke a Long-Running or Background Command”
Recipe 11.7, “Write Commands That Maintain State”
Appendix I, Selected Events and Their Uses