10

add_NameOfEvent({scriptblock})在 PowerShell 中,您可以使用对象的 -method订阅事件。这适用于按钮等 Form 对象。但是当我尝试使用它时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不同。一个表单组件(该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
4

2 回答 2

7

如果您尝试此代码:

$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 可以与窗口窗体同步(如在 ac# 代码中)。

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

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

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

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

于 2013-03-26T10:06:41.817 回答
1

本文向我表明,您必须订阅事件才能在 PowerShell 引擎中引发它们。看起来你已经有了这个概念,因为$timer2.我一直在尝试$timer1在我自己的会话中开始,并且使用公开的方法似乎无法正常工作,我特别指的是.Start().

于 2013-03-19T17:43:49.210 回答