为什么add_EventName不能与Timer一起使用?

时间:2013-03-19 16:59:12

标签: powershell powershell-v2.0 powershell-v3.0

在PowerShell中,您可以使用对象的add_NameOfEvent({scriptblock}) - 方法订阅事件。这适用于像按钮等表单对象。但是,当我用System.Timers.Timer尝试它时它不起作用。这是为什么?样品:

$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可以正常工作,但$timer1永远不会写入控制台。是什么让Timer与ex不同。表单组件(add_...方法有效)? Timer是否在单独的线程中运行,因此写入“隐藏”控制台?

证明该方法适用于不熟悉它的人使用表单组件:

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

2 个答案:

答案 0 :(得分:6)

如果您尝试此代码:

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

您将看到预期的行为,因为System.Timers.Timer可以与窗口表单同步(如在c#代码中)。

powershell控制台中的问题是计时器是在不同的线程中创建的,无法与控制台同步,因为它没有公开表示用于编组事件处理程序调用的对象的ISynchronizeInvoke接口在间隔过去时发出的。

SynchronizingObjectnull时,将在系统线程池的线程上调用处理Elapsed事件的方法。

IMO在场景后面Register-ObjectEvent创建一个系统线程池来完成事件处理程序调用。

我无法手动重新创建Register-ObjectEvent做什么,但我认为可以使用一些c#/ Pinvoke代码。

答案 1 :(得分:1)

This article向我表明您必须订阅事件才能在PowerShell引擎中引发它们。看起来你有$timer2.的概念我已经尝试在我自己的会话中启动$timer1并且它似乎无法使用暴露的方法,我特别指的是{{ 1}}。