Ich versuche, ein PowerShell-Add-in in .NET zu erstellen, mit dem ich auf einige Windows 10-Funktionen zugreifen kann. Ich kenne C#, aber PowerShell ist relativ neu für mich.C# -Ereignisse nie in PowerShell 5 behandelt
Ich habe alle meine Funktionsaufrufe und Cmdlets arbeiten. Ich kann die C# -Klassen dazu bringen, Dinge aus PowerShell zu machen. Was mich stolpert, ist Event Raising und Handling, um den C# -Code zurückrufen zu lassen. Ich bin jetzt an drei Abenden dabei.
Beispiele online und hier in SO zeigen immer Timer und Dateisystembeobachter, oder manchmal ein Windows Form. Ich habe noch nie ein Beispiel mit einer Klasse gesehen, die jemand selbst geschrieben hat. Nicht sicher, ob etwas Zucker benötigt wird, um die Ereignisse anzuheben, oder etwas anderes.
Der Hauptunterschied, den ich zwischen meinem Code und anderen gesehen habe, ist, dass ich eine Factory-Klasse habe, die eine Instanz des Objekts zurückgibt, das die Ereignisse auslöst. Ich benutze das, anstatt das neue Objekt aufzurufen. Allerdings erkennt PowerShell den Typ und freut sich, Mitglieder darauf aufzulisten, also habe ich angenommen, dass das nicht das Problem ist.
Ich habe ein einfaches Repro-Cmdlet und Klasse und Skripts erstellt. Hier sind sie:
GetTestObject Cmdlets
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);
}
}
}
Testobject Klasse
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.");
}
}
}
Und hier sind die Skripten und deren Ausgang
Test-events1.ps1 einfache .NET-Ereignishandler Syntax
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()
Ausgang mit: (Ereignis wird in C# gefeuert, aber nie in PS behandelt)
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 mit Asynchron/BeginInvoke Syntax im Fall, dass das Problem war
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()
Ausgang: (Ereignis wird in C# gefeuert, aber nie in PS behandelt. Außerdem erhalte ich eine Ausnahme.)
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 Verwendung von Register-Object Ansatz. Zusätzlich wurden hier noch einige Diagnoseinformationen zur Ausgabe hinzugefügt.
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
Ausgang: (Ereignis wird in C# gefeuert, aber nie in PS behandelt)
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 keinem Fall bekomme ich tatsächlich die Event-Handler-Aktion feuerte. Ich habe auch versucht, eine Variable zu verwenden, um die Aktion zu halten, aber das scheint auf die gleiche Weise behandelt zu werden. Der Console.Writeline-Code in C# dient nur zur Fehlersuche. Und ich habe den vollständigen Pfad für die DLL in den eingefügten Skripten entfernt, um sie einfacher zu lesen. Die DLL lädt gut.
Verwenden von PowerShell 5 unter Windows 10 pro 64 Bit mit .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
Irgendwelche Vorschläge für dieses PowerShell noob?
Ich weiß nicht, ob das das Problem ist, aber die Art, wie Sie das Ereignis aufrufen, ist nicht Thread-sicher. 'if (FooEvent! = null) FooEvent (this, EventArgs.Empty);' sollte eine lokale Variable verwenden, um den 'FooEvent'-Wert zuerst zu speichern, dann überprüfe das auf null und rufe es auf. Wie in, 'var foo = FooEvent; if (foo! = null) foo (dies, EventArgs.Empty); '. Oder in C# 6, 'Foo? .Invoke (dies, EventArgs.Empty);' http://codeblog.jonskeet.uk/2015/01/30/clean-event-handlers-invocation-with-c-6/ –
Angesichts des Async-Fehlers, den Sie sehen, vermute ich, dass Sie mit Remoting- und Remoting-Proxies zu kämpfen haben. Vielleicht möchten Sie auf [Making Objekte Remotable] (https://msdn.microsoft.com/en-us/library/vstudio/wcf3swha (v = vs.100) .aspx), insbesondere das Bit auf [Remoting] und Ereignisse] (https://msdn.microsoft.com/en-us/library/vstudio/ms172343 (v = vs.100) .aspx) –
Welche Ergebnisse von 'Receive-Job $ job'? – PetSerAl