2013-03-19 16 views
10

In PowerShell è possibile abbonarsi a un evento utilizzando il metodo add_NameOfEvent({scriptblock}) dell'oggetto. Funziona bene per oggetti Form come pulsanti ecc. Tuttavia, quando l'ho provato con un System.Timers.Timer non ha funzionato. Perché? Esempio:Perché non add_EventName funziona con 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 funzionerà bene, ma $timer1 non potrà mai scrivere al console. Ciò che rende un Timer diverso da ex. un componente del modulo (dove funziona il metodo add_...)? Il Timer viene eseguito in un thread separato e, a causa di ciò, scrive su una console "nascosta"?

La prova che il metodo funziona con form-componenti per chi non ha familiarità con esso:

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 

risposta

6

se si tenta questo codice:

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

vedrete il comportamento previsto, perché lo System.Timers.Timer può sincronizzare con la forma della finestra (come in un codice C#).

Il problema nella console PowerShell è che il timer viene creato in un thread diverso e non può essere sincronizzato con la console perché non espone un'interfaccia ISynchronizeInvoke che rappresenta l'oggetto utilizzato per eseguire il marshalling delle chiamate all'handler di eventi che vengono emesse quando è trascorso un intervallo.

Se SynchronizingObject è null, il metodo che gestisce l'evento Elapsed viene chiamato su un thread dal pool di thread di sistema.

IMO Dietro la scena Register-ObjectEvent creare un pool di thread di sistema per eseguire le chiamate di gestore di eventi.

Non riesco a ricreare manualmente ciò che fa Register-ObjectEvent, ma penso che sia possibile con un codice C#/Pinvoke.

+0

+1 per una buona risposta. Ho letto su synchronizingobject prima di chiederlo, e suppongo che il normale metodo di controllo aggiuntivo che puoi usare per aggiungere il controllo in un modulo imposta anche la proprietà syncronizingobject. Tuttavia, ci deve essere una variabile/oggetto che posso usare per sincronizzare il timer con la console. Considero questa la risposta corretta, ma aspetterò un paio di giorni per contrassegnarla nel caso qualcuno possa fornire una soluzione completa (senza creare una forma fittizia) :-) –

+0

Graimer Spero che x0n possa venire a dare una migliore, ha una profonda conoscenza degli eventi di PowerShell. Ad ogni modo ho trovato la tua domanda davvero interessante! –

1

This article indica a me che è necessario abbonarsi a eventi in modo per loro di essere sollevati nel motore PowerShell. Sembra che tu abbia il concept down per $timer2. Ho cercato di avviare $timer1 nella mia sessione e non sembra funzionare con i metodi esposti, mi riferisco in particolare a .Start().

+0

Sono consapevole che è necessario iscriversi all'evento, ma questo è ciò che il 'add_eventname' è una scorciatoia per. "Affinché un blocco di script gestisca direttamente un evento .NET, chiama il metodo Add_Event() dell'oggetto" fonte: http://www.pavleck.net/powershell-cookbook/ch31.html. Come detto, questa scorciatoia funziona con componenti di forma .net, quindi cosa li rende diversi da un .net Timer? : S –

+0

Questo metodo potrebbe anche essere specifico per i moduli, non sono sicuro del motivo per cui è esposto in PowerShell con il cmdlet Get-Member-Force. Tuttavia, poiché add_event() non è esposto in PowerShell senza utilizzare il parametro force su get-member, ho la sensazione che questo potrebbe essere intenzionale. Noto anche nella fonte che hai collegato menziona l'uso di Add_Event(), ma mai effettivamente lo usa. Invece l'autore registra l'evento. Questa documentazione indica che la classe Timer non ha un metodo add_event(). http://msdn.microsoft.com/en-us/library/system.timers.timer_methods.aspx –

+0

'add_eventname' è un metodo" nascosto/dinamico "gestito da PS. Non è visibile in nessun oggetto. Se digiti '$ timer1.add_Elapsed' otterrai il sovraccarico come prova della sua esistenza. –

Problemi correlati