在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
答案 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
接口在间隔过去时发出的。
当SynchronizingObject
为null
时,将在系统线程池的线程上调用处理Elapsed事件的方法。
IMO在场景后面Register-ObjectEvent创建一个系统线程池来完成事件处理程序调用。
我无法手动重新创建Register-ObjectEvent
做什么,但我认为可以使用一些c#/ Pinvoke代码。
答案 1 :(得分:1)
This article向我表明您必须订阅事件才能在PowerShell引擎中引发它们。看起来你有$timer2.
的概念我已经尝试在我自己的会话中启动$timer1
并且它似乎无法使用暴露的方法,我特别指的是{{ 1}}。