Sto provando a creare un componente aggiuntivo di PowerShell in .NET che consentirà di accedere ad alcune funzionalità di Windows 10. Conosco C#, ma PowerShell è relativamente nuovo per me.Eventi C# mai gestiti in PowerShell 5
Ho tutte le chiamate di funzione e i cmdlet attivi. Posso fare in modo che le classi C# facciano cose da PowerShell. Quello che mi fa inciampare è l'innalzamento e la gestione degli eventi per far richiamare il codice C#. Sono stato su questo per tre sere ora.
Esempi online e qui in SO mostrano sempre i timer e gli osservatori del file system, o talvolta un Windows Form. Non ho mai visto un esempio usando una classe che qualcuno ha scritto da sé. Non sono sicuro che ci sia dello zucchero necessario per aumentare gli eventi o qualcos'altro.
La principale differenza che ho riscontrato tra il mio codice e altri è che ho una classe factory che restituisce un'istanza dell'oggetto che solleva gli eventi. Lo uso invece di chiamare new-object. Tuttavia, PowerShell riconosce il tipo ed è felice di elencare i membri su di esso, quindi ho presunto che non è questo il problema.
Ho creato un cmdlet e una classe di repro semplice e script. Eccoli:
GetTestObject cmdlet
using System.Management.Automation;
namespace PeteBrown.TestFoo
{
[Cmdlet(VerbsCommon.Get, "TestObject")]
public class GetTestObject : PSCmdlet
{
protected override void ProcessRecord()
{
var test = new TestObject();
WriteObject(test);
}
}
}
classe TestObject
using System;
namespace PeteBrown.TestFoo
{
public class TestObject
{
public string Name { get; set; }
public event EventHandler FooEvent;
public void CauseFoo()
{
Console.WriteLine("c#: About to raise foo event.");
try
{
if (FooEvent != null)
FooEvent(this, EventArgs.Empty);
else
Console.WriteLine("c#: no handlers wired up.");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.WriteLine("c#: Raised foo event. Should be handler executed above this line.");
}
public void CauseAsyncFoo()
{
Console.WriteLine("c#: About to raise async foo event.");
try
{
if (FooEvent != null)
{
// yeah, I know this is silly
var result = FooEvent.BeginInvoke(this, EventArgs.Empty, null, null);
FooEvent.EndInvoke(result);
}
else
Console.WriteLine("c#: no handlers wired up.");
}
catch (Exception ex)
{
Console.WriteLine(ex.ToString());
}
Console.WriteLine("c#: Raised async foo event.");
}
}
}
E qui ci sono gli script e la loro produzione
test-events1.ps1 utilizzando la sintassi gestore semplice evento .NET
Import-Module "D:\U.....\Foo.dll"
Write-Output "Getting test object -------------------------------------- "
[PeteBrown.TestFoo.TestObject]$obj = Get-TestObject
# register for the event
Write-Output "Registering for .net object event ------------------------ "
$obj.add_FooEvent({Write-Output "Powershell: Event received"})
Write-Output "Calling the CauseFoo method to raise event --------------- "
$obj.CauseFoo()
uscita: (evento viene generato in C#, ma mai trattati in PS)
Getting test object --------------------------------------
Registering for .net object event ------------------------
Calling the CauseFoo method to raise event ---------------
c#: About to raise foo event.
c#: Raised foo event. Should be handler executed above this line.
test-events2.ps1 utilizzando asincrona/sintassi BeginInvoke nel caso in cui quello era il numero
Import-Module "D:\U.....\Foo.dll"
Write-Output "Getting test object -------------------------------------- "
[PeteBrown.TestFoo.TestObject]$obj = Get-TestObject
# register for the event
Write-Output "Registering for .net object event ------------------------ "
$obj.add_FooEvent({Write-Output "Powershell: Event received"})
Write-Output "Calling the CauseAsyncFoo method to raise event ---------- "
$obj.CauseAsyncFoo()
Uscita: (l'evento è attivato in C#, ma mai gestito in PS. Inoltre, ottengo un'eccezione.)
Getting test object --------------------------------------
Registering for .net object event ------------------------
Calling the CauseAsyncFoo method to raise event ----------
c#: About to raise async foo event.
System.ArgumentException: The object must be a runtime Reflection object.
at System.Runtime.Remoting.InternalRemotingServices.GetReflectionCachedData(MethodBase mi)
at System.Runtime.Remoting.Messaging.Message.UpdateNames()
at System.Runtime.Remoting.Messaging.Message.get_MethodName()
at System.Runtime.Remoting.Messaging.MethodCall..ctor(IMessage msg)
at System.Runtime.Remoting.Proxies.RemotingProxy.Invoke(Object NotUsed, MessageData& msgData)
at System.EventHandler.BeginInvoke(Object sender, EventArgs e, AsyncCallback callback, Object object)
at PeteBrown.TestFoo.TestObject.CauseAsyncFoo() in D:\Users\Pete\Documents\GitHub\Windows-10-PowerShell-MIDI\PeteBrow
n.PowerShellMidi\Test\TestObject.cs:line 37
c#: Raised async foo event.
test-events3.ps1 Utilizzando approccio Registrati-ObjectEvent. Anche qui sono state aggiunte ulteriori informazioni sul tipo di diagnostica all'output.
Import-Module "D:\U......\Foo.dll"
Write-Output "Getting test object -------------------------------- "
[PeteBrown.TestFoo.TestObject]$obj = Get-TestObject
# register for the event
Write-Output "Registering for .net object event ------------------ "
$job = Register-ObjectEvent -InputObject $obj -EventName FooEvent -Action { Write-Output "Powershell: Event received" }
Write-Output "Calling the CauseFoo method to raise event --------- "
$obj.CauseFoo()
Write-Output "Job that was created for the event subscription ----"
Write-Output $job
# show the event subscribers
Write-Output "Current event subscribers for this session ---------"
Get-EventSubscriber
# this lists the events available
Write-Output "Events available for TestObject --------------------"
$obj | Get-Member -Type Event
uscita: (evento viene generato in C#, ma mai trattati in PS)
Getting test object --------------------------------
Registering for .net object event ------------------
Calling the CauseFoo method to raise event ---------
c#: About to raise foo event.
c#: Raised foo event. Should be handler executed above this line.
Job that was created for the event subscription ----
Id Name PSJobTypeName State HasMoreData Location Command
-- ---- ------------- ----- ----------- -------- -------
1 2626de85-523... Running True Write-Output "Powersh...
Current event subscribers for this session ---------
SubscriptionId : 1
SourceObject : PeteBrown.TestFoo.TestObject
EventName : FooEvent
SourceIdentifier : 2626de85-5231-44a0-8f2c-c2a900a4433b
Action : System.Management.Automation.PSEventJob
HandlerDelegate :
SupportEvent : False
ForwardEvent : False
Events available for TestObject --------------------
TypeName : PeteBrown.TestFoo.TestObject
Name : FooEvent
MemberType : Event
Definition : System.EventHandler FooEvent(System.Object, System.EventArgs)
In nessun caso posso effettivamente ottenere l'azione gestore di eventi sparato. Ho anche provato a utilizzare una variabile per contenere l'azione, ma sembra trattata allo stesso modo. Il codice Console.Writeline in C# serve solo per aiutare il debug. E ho rimosso il percorso completo della DLL negli script incollati solo per renderli più facili da leggere. La DLL carica bene.
Utilizzo di PowerShell 5 su Windows 10 pro 64 bit con .NET Framework 4.6 (CLR 4). VS 2015 RTM.
Name Value
---- -----
PSVersion 5.0.10240.16384
WSManStackVersion 3.0
SerializationVersion 1.1.0.1
CLRVersion 4.0.30319.42000
BuildVersion 10.0.10240.16384
PSCompatibleVersions {1.0, 2.0, 3.0, 4.0...}
PSRemotingProtocolVersion 2.3
Qualche suggerimento per questo noob PowerShell?
Non so se questo è il problema, ma il modo in cui si chiama l'evento non è thread-safe. 'if (FooEvent! = null) FooEvent (this, EventArgs.Empty);' dovrebbe usare una variabile locale per memorizzare prima il valore 'FooEvent', quindi controllare che sia null e chiamarlo. Come in, 'var foo = FooEvent; se (foo! = null) foo (this, EventArgs.Empty); '. O in C# 6, 'Foo? .Invoke (this, EventArgs.Empty);' http://codeblog.jonskeet.uk/2015/01/30/clean-event-handlers-invocation-with-c-6/ –
Dato l'errore asincrono che si vede, ho il sospetto che si possa essere in errore di remoting e proxy remoti. Si consiglia di leggere [rendere gli oggetti remotabili] (https://msdn.microsoft.com/en-us/library/vstudio/wcf3swha (v = vs.100) .aspx), in particolare, il bit su [remoting ed eventi] (https://msdn.microsoft.com/en-us/library/vstudio/ms172343 (v = vs.100) .aspx) –
Quali risultati del lavoro "Ricevi-Lavoro $"? – PetSerAl