In PowerShell you can subscribe to an event by using the add_NameOfEvent({scriptblock})
-method of the object. This works well for Form objects like buttons etc. However when I tried it with a System.Timers.Timer
it didn't work. Why is that? Sample:
$timer1 = New-Object System.Timers.Timer
$timer1.Interval = 2000
$timer1.add_Elapsed({ Write-Host "Timer1 tick" })
$timer2 = New-Object System.Timers.Timer
$timer2.Interval = 2000
Register-ObjectEvent -InputObject $timer2 -EventName Elapsed -Action { Write-Host "Timer2 tick" }
$timer1.Start()
$timer2.Start()
$timer2
will work fine, but $timer1
will never write to the console. What makes a Timer
different from ex. a form-component(where the add_...
method works)? Does the Timer
run in a seperate thread and because of that, writes to a "hidden" console?
Proof that the method works with form-components for those not familiar with it:
PS > Add-Type -AssemblyName System.Windows.Forms
PS > $b = New-Object System.Windows.Forms.Button
PS > $b.add_click({ Write-Host "button" })
#Get-EventSubscriber won't show this event, but it's added
PS > $b.PerformClick()
button
if you try this code:
$w = new-object 'System.Windows.Forms.Form'
$timer1 = New-Object System.Timers.Timer
$timer1.Interval = 2000
$timer1.add_Elapsed({ Write-Host "Timer1 tick" })
$timer1.SynchronizingObject = $w
$timer1.Start()
$w.ShowDialog()
you'll see the expected behavior because the System.Timers.Timer can sync with the window form (as in a c# code).
The problem in powershell console is that the timer is created in a different thread and can't sync with the console because it doesn't expose a ISynchronizeInvoke
interface representing the object used to marshal the event-handler calls that are issued when an interval has elapsed.
When SynchronizingObject
is null
, the method that handles the Elapsed event is called on a thread from the system-thread pool.
IMO Behind the scene Register-ObjectEvent create a system-thread pool to accomplish the event-handler calls.
I'm not able to re-create by hands what Register-ObjectEvent
do, but I think it possible with some c#/Pinvoke code.
This article indicates to me that you must subscribe to events in order for them to be raised in the PowerShell engine. It looks like you have the concept down for $timer2.
I've been trying to start $timer1
in my own session and it does not appear to work using the methods exposed, I'm specifically referring to .Start()
.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With