2013-03-19 11 views
10

W programie PowerShell można subskrybować wydarzenie, korzystając z metody obiektu obiektu add_NameOfEvent({scriptblock}). Działa to dobrze dla obiektów Form, takich jak przyciski itp. Jednak gdy próbowałem go z System.Timers.Timer, to nie działało. Dlaczego? Próbka:Dlaczego add_EventName nie działa z Timerem?

$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 będzie działać dobrze, ale nigdy nie $timer1 napisać do konsoli. Co różni Timer od np. komponent formularza (gdzie działa metoda add_...)? Czy Timer działa w oddzielnym wątku i z tego powodu pisze do "ukrytej" konsoli?

Dowód, że metoda działa z form-komponentów dla tych, którzy nie znają go:

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 

Odpowiedz

6

jeśli próbować ten kod:

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

zobaczysz oczekiwane zachowanie, ponieważ System.Timers.Timer można synchronizować z postaci okna (jak w C# kod).

Problem w konsoli programu Power Shell polega na tym, że licznik czasu jest tworzony w innym wątku i nie może zsynchronizować się z konsolą, ponieważ nie ujawnia interfejsu ISynchronizeInvoke reprezentującego obiekt używany do przekazywania wywołań obsługi zdarzeń, które są wydawane po upływie pewnego czasu.

Gdy jest null, metoda obsługująca zdarzenie, które minęło, jest wywoływana w wątku z puli wątków systemowych.

IMO Za sceną Register-ObjectEvent utwórz pulę wątków systemowych, aby wykonać wywołania procedury obsługi zdarzeń.

Nie jestem w stanie odtworzyć ręcznie przez to, co robi Register-ObjectEvent, ale myślę, że jest to możliwe z niektórymi kodami C#/Pinvoke.

+0

+1 za dobrą odpowiedź. Przeczytałem o obiekcie synchronizującym przed zapytaniem, i domyślam się, że normalna metoda kontroli dodawania, którą można użyć do dodania formantu w formularzu, ustawia również właściwość syncronisingobiekt. Jednak musi istnieć zmienna/obiekt, którego mogę użyć do zsynchronizowania timera z konsolą. Uważam to za poprawną odpowiedź, ale poczekam kilka dni, aby oznaczyć ją na wypadek, gdyby ktoś mógł dostarczyć pełne rozwiązanie (bez tworzenia formy dummy) :-) –

+0

Graimer Mam nadzieję, że @ x0n może przyjść, by dać lepszy, ma głęboką wiedzę na temat wciągania Power Shell. W każdym razie znalazłem twoje pytanie naprawdę interesujące! –

1

This article wskazuje mi się, że trzeba zapisać się do wydarzeń, aby mogły być podniesione w silniku PowerShell. Wygląda na to, że masz koncepcję na $timer2. Próbuję uruchomić $timer1 w mojej własnej sesji i nie wydaje się działać przy użyciu ujawnionych metod, konkretnie odwołuję się do .Start().

+0

Jestem świadomy, że musisz zasubskrybować wydarzenie, ale właśnie to jest skrótem do 'add_eventname'. "Aby blok skryptu obsługiwał bezpośrednio zdarzenie .NET, wywołaj metodę Add_Event() tego obiektu" źródło: http://www.pavleck.net/powershell-cookbook/ch31.html. Jak już wspomniano, ten skrót działa z komponentami formularza .net, więc co sprawia, że ​​różnią się one od .net timera? : S –

+0

Ta metoda może być również specyficzna dla formularzy, nie jestem pewien, dlaczego jest ona udostępniona w programie Powerhell za pomocą polecenia cmdlet Get-Member -Force. Jednakże, ponieważ add_event() nie jest odsłonięty w trybie powershell bez użycia siły w paragrafie get-member, mam wrażenie, że może to być celowe. Zauważam też, że w źródle, z którym się łączysz, wymienia użycie Add_Event(), ale nigdy go nie używa. Zamiast tego autor rejestruje wydarzenie. Ta dokumentacja wskazuje, że klasa Timer nie ma metody add_event(). http://msdn.microsoft.com/en-us/library/system.timers.timer_methods.aspx –

+0

'add_eventname' jest metodą" ukrytą/dynamiczną "obsługiwaną przez PS. Nie jest widoczny w żadnym obiekcie. Jeśli wpiszesz '$ timer1.add_Elapsed', dostaniesz przeciążenie jako dowód, że istnieje. –