PSEventing Plus 1.0 Released (v1.0.0.1)

About

Eventing extensions for PowerShell 2.0 Console Host. This module integrates with PowerShell 2.0 Eventing infrastructure. HotKey events can have a scriptblock action, or can put events in the event queue just like the OOTB eventing cmdlets. This is the recommended release for PowerShell 2.0 which already has built-in eventing support.

PSEventing 1.1 Released

Trap and respond to synchronous & asynchronous .NET, COM and WMI events or Hot Keys within your powershell scripts with this easy to use suite of cmdlets. Compatible with PowerShell 1.0, 2.0 & 3.0

Important

This is intended for PowerShell v1.0 only. PowerShell v2.0 has introduced support for eventing with similarly named cmdlets, but with real-time (interrupt-based) event support.

New Features

  • Multiple named queue support and default queue with -QueueName parameter
  • Better COM support, window message pumping etc.
  • NoFlush / Peek parameter support for queue reading
  • Get-EventQueue command added for viewing queues and their message counts.
See foreground / background swappable downloads in powershell for an advanced example.

Cmdlet Name Changes

  • Get-Event -> Read-Event
  • Connect-EventListener -> Connect-Event
  • Disconnect-EventListener -> Disconnect-Event

64 bit PowerShell Support

To install for use with the 64bit version of PowerShell, please use the 64bit version of InstallUtil:

& "$env:windir\Microsoft.NET\Framework64\v2.0.50727\InstallUtil.exe" nivot.powershell.eventing.dll

How does it work?

Events in PowerShell - but how? Events in a .NET executable are generally raised and handled in realtime in an asynchronous fashion; at first glance, this doesn't seem possible with our favourite little shell. PowerShell lets us wire up scriptblocks to .NET events as handlers relatively easily, but unfortunately if the event is raised on a different thread (as is the case with WMI watchers, FileSystemWatchers and other asynchronous objects), there is no runspace available to execute our script. So, how do we get a runspace to execute a handler? Well, we ensure that we are in a running pipeline before processing events. This is achieved by disconnecting the process: we separate the occurance of events from the act of handling them. How does this work in practice? Let's play with System.IO.FileSystemWatcher :

Example with FileSystemWatcher

This example uses the Cmdlets directly to demonstrate how the Snap-In works; you may find it easier and more familiar to work with the simple wrapper functions supplied on the Releases page. See the bottom of this page to read about these functions, called Add-EventHandler, Remove-EventHandler and Do-Events respectively.

Step 1

1# Add-PSSnapin pseventing

2# $fsw = new-object system.io.filesystemwatcher
3# $fsw.Path = "c:\temp"
4# $fsw.EnableRaisingEvents = $true

This loads our snap-in, creates a FileSystemWatcher instance assigned to the variable named fsw and tells it to keep an eye on our temp directory. So, what events can we watch? Well, before you go running to MSDN...

Step 2

#5 Get-EventBinding fsw -IncludeUnboundEvents | Format-Table -Auto

VariableName EventName TypeName          Listening
------------ --------- --------          ---------
fsw          Changed   FileSystemWatcher     False
fsw          Created   FileSystemWatcher     False
fsw          Deleted   FileSystemWatcher     False
fsw          Disposed  FileSystemWatcher     False
fsw          Error     FileSystemWatcher     False
fsw          Renamed   FileSystemWatcher     False

This command lets us examine what events this variable exposes. Note that I say variable, and not object. This is a running theme throughout this library - all PSEventing Cmdlets only deal with PSVariable objects, or variable names so do not prefix your variable names with the $ symbol. This will end up passing the instance instead of the variable, which is not what we want. This is to keep the paradigm similar to other late-bound scripting languages, like vbscript; allowing direct object references would not have been very admin-friendly. Ok, so next, lets watch the Changed and Deleted events. Btw, Event names are not case sensitive, unless you provide the -CaseSensitive SwitchParameter.

Step 3

5# Connect-Event fsw changed,deleted

From this moment on, every time something is changed or deleted in c:\temp, a PSEvent object is added to our background event queue, even if you're busy running some other script or doing an unrelated task! Lets perform some related activity:

Step 4

6# "foo" > c:\temp\bar.txt
7# remove-item c:\temp\bar.txt

Now, we've just created and deleted a file. You could have also gone out to explorer and created and deleted the file; all events are captured, not just those triggered from within powershell. How do we find out whap's happened, and act on this info? with the read-Event cmdlet:

Step 5

8# $events = Read-Event
9# $events | Format-Table -Auto

Occurred            Source                                  Name    Args
--------            ------                                  ----    ----
5/9/2007 3:31:39 PM System.Management.Automation.PSVariable Changed System.IO.FileSystemEventArgs
5/9/2007 3:31:42 PM System.Management.Automation.PSVariable Deleted System.IO.FileSystemEventArgs

Two events occured since we wired up the listener (or since we last called read-Event since each call drains the queue). Lets have a look at the EventArgs for the first one:

Step 6

10# $events[0].Args | format-table -Auto

ChangeType FullPath        Name
---------- --------        ----
   Changed c:\temp\bar.txt bar.txt

And there you have it!

Download the 1.1 release and have a look at the sqlbackup.ps1 example script for more advanced uses.

Included Cmdlets

All cmdlets have -? help support, and you can also get more information via get-help about_pseventing
  • Connect-Event : Start tracking one or more event(s) for one or more variable(s)
    • Connect-Event [-VariableName] <String> [-EventName] <String[]> [-CaseSensitive [<SwitchParameter>]] [-QueueName <identifier>] [<CommonParameters>]
    • Connect-Event [-Variable] <PSVariable> [-EventName] <String[]> [-CaseSensitive [<SwitchParameter>]] [-QueueName <identifier>] <CommonParameters>]
  • Disconnect-Event : Stop tracking one or more event(s) for one or more variable(s)
    • Disconnect-Event [-VariableName] <String> [-EventName] <String[]> [-CaseSensitive [<SwitchParameter>]] [<CommonParameters>]
    • Disconnect-Event [-Variable] <PSVariable> [-EventName] <String[]> [-CaseSensitive [<SwitchParameter>]] <CommonParameters>]
  • Get-EventBinding : Retrieve event binding information for variables
    • Get-EventBinding [[-Variable] [<PSVariable[]>]] [-IncludeUnboundEvents [<SwitchParameter>]] [<CommonParameters>]
    • Get-EventBinding [[-VariableName] [<String[]>]] [-IncludeUnboundEvents [<SwitchParameter>]] [<CommonParameters>]

Hint : bind or unbind all events at once for a variable
  1# $fsw = New-Object System.IO.FileSystemWatcher
  2# Get-EventBinding fsw -IncludeUnboundEvents | Connect-Event

  • New-Event : Create a custom event for insertion into the queue with an optional object payload
    • New-Event [-EventName] <String> [[-Data] [<PSObject>]] [-QueueName <identifier>] [<CommonParameters>]
  • Read-Event : Retrieve one or more PSEvent objects representing tracked .NET events that have been raised since the last invocation of this command.
    • Read-Event [-Wait [<SwitchParameter>]] [-QueueName <identifier>] [-NoFlush <SwitchParameter>] [<CommonParameters>]
  • Get-EventQueue : Shows information about the default queue and/or named queues.
    • Get-EventQueue [[-QueueName] <String[]>] [-ShowEmpty] [<CommonParameters>]
  • Start-KeyHandler : Start generating PSEvent objects for break (cltr+c) and/or keydown events.
    • Start-KeyHandler [-CaptureCtrlC [<SwitchParameter>]] [-CaptureKeys [<SwitchParameter>]] [<CommonParameters>]
  • Stop-KeyHandler : Stop generating PSEvent objects for break (ctrl+c) and/or keydown events.
    • Stop-KeyHandler [<CommonParameters>]

Read some more Syntax Hints for a quick start.

- Oisin Grehan / x0n

Last edited May 19, 2011 at 8:38 PM by oisin, version 56