PowerShell Eventing Library : PSEventing 0.5 Beta released
PSeventing is a very powerful PowerShell Snapin providing a library for handling events in PowerShell, a must have extension to PowerShell !!
Eventhanding suport is a bit limited for version 1.0 of PowerShell, there are some threading problems with for example the .NET filewatcher see : /\/\o\/\/ PowerShelled: MSH directory watcher with popup-balloon , I used IronPython as a workaround :Using IronPython from PowerShell Part 1 : Watch folder for changes without blocking Console .
I also implemented a non blocking version of the Popup-Balloon script in IronPython, it was still on my "To-Blog-List", not needed anymore so I might skip the blogentry but for the Iron Python Fan's I will still post the script to my blog tomorrow
Also for working with WMI events we have problems with blocking the console and not be able to Cancel the Event watcher when started, as Ctrl-C does not work, see also MSH Cluster Watcher script without getting an event we can not stop the Watcher without closing the complete console (Ctrl-Break).
As this is a very big handicap when working with events, and especialy WMI events I use a lot , so this is/was very high on my WishList, and something that I did realy miss in PowerShell (Hence all the workarounds), and belief me I did a lot of begging for better Event support, on Connect the NewGroup and for example on last MVP summit it was my main request to the team ;-)
I talked about the problem with handling events to Oisin ( also developer of the PowerShell SharePoint Provider and contributor to the PowerShell Community Extensions Project ) on IRC (in the #PowerShell channel on FreeNode) he also agreed on the problem, saw the possibilities and he started to make a Cmdlet for it.
I use this first version for a while now and it is Great !!!, as there where some small issues, that will say small issues in usage, but very hard to solve as they involved threading and Ctrl-C handling (a weak point of PowerShell V1.0 in global, also high on my Wish-List for V2 ) ,Oisin didn't want to release the Snapin , also he made it into a complete library and added help, so what did look like a fast solution resulted in a big project that took a lot of time.
But the Result is really amazing
Oisin has made this into this very cool PowerShell eventing library, and released it on CodePlex :
as Oisin did the FileWatcher example on the Homepage of the Project I will show how great this solution can be used for WMI Queries also.
for more information about the WMI EventWatcher and how to make it a script on PowerShell, see these posts on my former blog about this topic.
/\/\o\/\/ PowerShelled: Watching FSRM Quota and filescreen events ...
/\/\o\/\/ PowerShelled: MSH Cluster Watcher script (completer script and more info)
I start with making the WMI query, in this case I will watch all processes that do exit :
PoSH> Add-PSSnapin psEventing
PoSH>
PoSH> $query = New-Object System.Management.WQLEventQuery
PoSH> $query.EventClassName = "__InstanceDeletionEvent"
PoSH> $query.WithinInterval = New-TimeSpan -seconds 1
PoSH> $query.Condition = "TargetInstance ISA 'Win32_Process'"
PoSH> $query
QueryLanguage : WQL
QueryString : select * from __InstanceDeletionEvent within 1 where TargetInstance ISA 'Win32_Process'
EventClassName : __InstanceDeletionEvent
Condition : TargetInstance ISA 'Win32_Process'
WithinInterval : 00:00:01
GroupWithinInterval : 00:00:00
GroupByPropertyList : {}
HavingCondition :
PoSH>
PoSH> $watcher = New-Object System.Management.ManagementEventWatcher($query)
PoSH>
After I made the watcher this way I can now use the tools in the PowerShell Eventing Library, to interact with the eventwatcher,
If you use PowerTab
you can type %[tab]ev to see and easy select from the 4 Cmdlets added by the PsEvents SnapIn,
(There are 2 more Cmdlets provided to manage the Crtl-C key handling of PowerShell
Start-KeyHandler
Stop-KeyHandler
, very cool stuff also but to much to cover in this blogentry, it us most usefull with the -wait parameter that we also not use here. )
also you can see in the next example using PowerTab on the parameters of Get_eventBinding that the Get-Eventbinding also has a -IncludeUnboundEvents parameter that is very handy to check the event available on an Object :
PoSH> %ev
PoSH> ev
PoSH> Connect-EventListener
PoSH> Disconnect-EventListener
PoSH> Get-Event
PoSH> Get-EventBinding
PoSH> Get-EventLog
PoSH> [ 1 of 5 ]
PoSH>
PoSH> Get-EventBinding -
PoSH> -
PoSH> -Debug
PoSH> -ErrorAction
PoSH> -ErrorVariable
PoSH> -IncludeUnboundEvents
PoSH> -OutBuffer
PoSH> -OutVariable
PoSH> -Variable
PoSH> -VariableName
PoSH> -Verbose
PoSH> [ 4 of 9 ]
PoSH>
PoSH> Get-EventBinding watcher -IncludeUnboundEvents
VariableName EventName TypeName Listening
------------ --------- -------- ---------
watcher Disposed ManagementEventWatcher False
watcher EventArrived ManagementEventWatcher False
watcher Stopped ManagementEventWatcher False
PoSH>
PoSH> Connect-EventListener watcher EventArrived -Verbose
VERBOSE: Target is a ManagementEventWatcher
VERBOSE: Now listening for 'EventArrived' events from $watcher
PoSH>
PoSH> $watcher.Start()
PoSH> calc
PoSH> sleep 2
PoSH> gps calc | stop-process
PoSH> $e = get-Event
PoSH>
Now we have used Connect-EventListener to bind to the events of the Watcher object we made, and from now on we can use get-Event at anytime to check if there are events riased by the $watcher WMI event watcher object.
PoSH> $e
PoSH>
PoSH> $e.Args
PoSH> $e.Args.NewEvent
PoSH> $e.Args.NewEvent.TargetInstance | fl Name,ExecutablePath
PoSH>
PoSH> calc
PoSH> sleep 2
PoSH> gps calc | stop-process
PoSH>
PoSH> get-Event | select @{n='date';e={[datetime]::FromFileTime($_.Args.NewEvent.TIME_CREATED)}},@{n='Server';e={$_.Args
.NewEvent.TargetInstance.__SERVER}},@{n='Process';e={$_.Args.NewEvent.TargetInstance.Description}},@{n='Started';e={([wm
i]'').ConvertToDateTime($_.Args.NewEvent.TargetInstance.CreationDate)}} | select *,@{n='Active';e={$_.Date - $_.Started}
} | ft
date Server Process Started Active
---- ------ ------- ------- ------
5/9/2007 10:51:48 PM PoshVista1 calc.exe 5/9/2007 10:51:45 PM 00:00:02.5037460
5/9/2007 10:51:50 PM PoshVista1 calc.exe 5/9/2007 10:51:47 PM 00:00:02.4226970
PoSH> $e
PoSH> calc
PoSH> sleep 2
PoSH> gps calc | stop-process
PoSH> $e = get-Event
PoSH> $e
PoSH> $e = get-Event
PoSH> $e
Occurred Source Name Args
-------- ------ ---- ----
5/9/2007 10:56:51 PM variable:watcher EventArrived System.Management.EventArr...
PoSH> calc
PoSH> sleep 2
PoSH> gps calc | stop-process
PoSH> sleep 2
PoSH> $e = get-Event
PoSH> $e
Occurred Source Name Args
-------- ------ ---- ----
5/9/2007 10:59:16 PM variable:watcher EventArrived System.Management.EventArr...
PoSH> $e.Args
NewEvent Context
-------- -------
System.Management.ManagementBaseObject {}
PoSH> $e.Args.NewEvent
__GENUS : 2
__CLASS : __InstanceDeletionEvent
__SUPERCLASS : __InstanceOperationEvent
__DYNASTY : __SystemClass
__RELPATH :
__PROPERTY_COUNT : 3
__DERIVATION : {__InstanceOperationEvent, __Event, __IndicationRelated, __SystemClass}
__SERVER : PoshVista1
__NAMESPACE : //./root/CIMV2
__PATH :
SECURITY_DESCRIPTOR :
TargetInstance : System.Management.ManagementBaseObject
TIME_CREATED : 128232179565717710
PoSH> $e.Args.NewEvent.TargetInstance | fl Name,ExecutablePath
Name : calc.exe
ExecutablePath : C:\WINDOWS\system32\calc.exe
PoSH> $e.Args.NewEvent.TargetInstance | fl
__GENUS : 2
__CLASS : Win32_Process
__SUPERCLASS : CIM_Process
__DYNASTY : CIM_ManagedSystemElement
__RELPATH : Win32_Process.Handle="10084"
__PROPERTY_COUNT : 45
__DERIVATION : {CIM_Process, CIM_LogicalElement, CIM_ManagedSystemElement}
__SERVER : PoshVista1
__NAMESPACE : root\CIMV2
__PATH : \\NL-37YHW1J\root\CIMV2:Win32_Process.Handle="10084"
Caption : calc.exe
CommandLine : "C:\WINDOWS\system32\calc.exe"
CreationClassName : Win32_Process
CreationDate : 20070509225913.276083+120
CSCreationClassName : Win32_ComputerSystem
CSName : PoshVista1
Description : calc.exe
ExecutablePath : C:\WINDOWS\system32\calc.exe
ExecutionState :
Handle : 10084
HandleCount : 49
InstallDate :
KernelModeTime : 1093750
MaximumWorkingSetSize : 1413120
MinimumWorkingSetSize : 204800
Name : calc.exe
OSCreationClassName : Win32_OperatingSystem
OSName : Microsoft® Windows Vista™ Ultimate |C:\Windows|\Device\Harddisk0\Partition2
OtherOperationCount : 173
OtherTransferCount : 2632
PageFaults : 1203
PageFileUsage : 1306624
ParentProcessId : 10408
PeakPageFileUsage : 1323008
PeakVirtualSize : 54902784
PeakWorkingSetSize : 4853760
Priority : 8
PrivatePageCount : 1306624
ProcessId : 10084
QuotaNonPagedPoolUsage : 2400
QuotaPagedPoolUsage : 114352
QuotaPeakNonPagedPoolUsage : 2688
QuotaPeakPagedPoolUsage : 114888
ReadOperationCount : 1
ReadTransferCount : 12748
SessionId : 1
Status :
TerminationDate :
ThreadCount : 1
UserModeTime : 156250
VirtualSize : 54886400
WindowsVersion : 6.0.6000
WorkingSetSize : 4837376
WriteOperationCount : 0
WriteTransferCount : 0
PoSH> $e.Args.NewEvent.TargetInstance | fl [a-z]*
Caption : calc.exe
CommandLine : "C:\WINDOWS\system32\calc.exe"
CreationClassName : Win32_Process
CreationDate : 20070509225913.276083+120
CSCreationClassName : Win32_ComputerSystem
CSName : PoshVista1
Description : calc.exe
ExecutablePath : C:\WINDOWS\system32\calc.exe
ExecutionState :
Handle : 10084
HandleCount : 49
InstallDate :
KernelModeTime : 1093750
MaximumWorkingSetSize : 1413120
MinimumWorkingSetSize : 204800
Name : calc.exe
OSCreationClassName : Win32_OperatingSystem
OSName : Microsoft® Windows Vista™ Ultimate |C:\Windows|\Device\Harddisk0\Partition2
OtherOperationCount : 173
OtherTransferCount : 2632
PageFaults : 1203
PageFileUsage : 1306624
ParentProcessId : 10408
PeakPageFileUsage : 1323008
PeakVirtualSize : 54902784
PeakWorkingSetSize : 4853760
Priority : 8
PrivatePageCount : 1306624
ProcessId : 10084
QuotaNonPagedPoolUsage : 2400
QuotaPagedPoolUsage : 114352
QuotaPeakNonPagedPoolUsage : 2688
QuotaPeakPagedPoolUsage : 114888
ReadOperationCount : 1
ReadTransferCount : 12748
SessionId : 1
Status :
TerminationDate :
ThreadCount : 1
UserModeTime : 156250
VirtualSize : 54886400
WindowsVersion : 6.0.6000
WorkingSetSize : 4837376
WriteOperationCount : 0
WriteTransferCount : 0
PoSH> $e.Args.NewEvent.TargetInstance | fl ProcessId,Name,ExecutablePath
ProcessId : 10084
Name : calc.exe
ExecutablePath : C:\WINDOWS\system32\calc.exe
PoSH>
You can see that we have some event information but we get also back the Process information so we can use that also,
Next a will show a oneliner I made for the first version, that creates and formats a nice list ofthe processes with information about when they stopped when the ended and how long they did run.
get-Event | select @{n='date';e={[datetime]::FromFileTime($_.Args.NewEvent.TIME_CREATED)}},@{n='Server';e={$_.Args.NewEvent.TargetInstance.__SERVER}},@{n='Process';e={$_.Args.NewEvent.TargetInstance.Description}},@{n='Started';e={([wmi]'').ConvertToDateTime($_.Args.NewEvent.TargetInstance.CreationDate)}} | select *,@{n='Active';e={$_.Date - $_.Started}} | ft
[PoSH]> calc
[PoSH]> mspaint
[PoSH]> sleep 2
[PoSH]> gps calc | stop-process
[PoSH]> gps mspaint | stop-process
[PoSH]> sleep 1
[PoSH]>
[PoSH]> get-Event | select @{n='date';e={[datetime]::FromFileTime($_.Args.NewEvent.TIME_CREATED)}},@{n='Server';e={$_.Ar
gs.NewEvent.TargetInstance.__SERVER}},@{n='Process';e={$_.Args.NewEvent.TargetInstance.Description}},@{n='Started';e={([
wmi]'').ConvertToDateTime($_.Args.NewEvent.TargetInstance.CreationDate)}} | select *,@{n='Active';e={$_.Date - $_.Starte
d}} | ft
date Server Process Started Active
---- ------ ------- ------- ------
5/10/2007 1:20:46 AM PoshVista1 SearchProtocolHost.exe 5/10/2007 1:19:37 AM 00:01:08.5937505
5/10/2007 1:20:46 AM PoshVista1 SearchFilterHost.exe 5/10/2007 1:19:38 AM 00:01:08.3593755
5/10/2007 1:22:00 AM PoshVista1 calc.exe 5/10/2007 1:21:57 AM 00:00:02.4062505
5/10/2007 1:22:01 AM PoshVista1 mspaint.exe 5/10/2007 1:21:58 AM 00:00:03.2968755
[PoSH]>
*Note* When Oisin did see this one-liner he added also the Occurred property to the returned object so the WMI date translation is not needed anymore, but I did leave it in just to Show Off ;-)
You can see how powerfull and handy this is you define the events you want to watch, and can check them at any time you want (Tip leave this WMI eventwatcher for processess running for a couple of hours and then check the events like this you will be amazed ;-)
This Snapin really does add a lot of extra value and possibilities to the eventhandling of PowerShell I allready can not live without it anymore thats for sure , and it is another example what a great work is done within the PowerShell community
Thanks again to Oisin for fullfilling one of the most important PoSH feature wishes for me, and for makeing PsEvents available to the PowerShell community.
enjoy,
Greetings /\/\o\/\/