Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Why don't add_EventName work with Timer?

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
like image 928
Frode F. Avatar asked Mar 19 '13 16:03

Frode F.


2 Answers

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.

like image 134
CB. Avatar answered Nov 08 '22 21:11

CB.


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().

like image 1
Christopher Douglas Avatar answered Nov 08 '22 21:11

Christopher Douglas