PowerShell 5 中从未处理过的 C# 事件

标签 c# .net powershell events

我正在尝试在 .NET 中创建一个 PowerShell 加载项,它将允许我访问某些 Windows 10 功能。我知道 C#,但 PowerShell 对我来说比较陌生。

我的所有函数调用和 cmdlet 都在工作。我可以让 C# 类通过 PowerShell 执行操作。让我绊倒的是事件引发和处理让 C# 代码回调。我已经在这上面待了三个晚上了。

在线示例和 SO 中的示例始终显示计时器和文件系统观察器,或者有时显示 Windows 窗体。我从未见过使用某人自己编写的类的示例。不确定引发事件是否需要一些糖分或其他东西。

我发现我的代码与其他代码之间的主要区别是我有一个工厂类,它返回引发事件的对象的实例。我使用它而不是调用新对象。但是,PowerShell 可以识别该类型并乐于在其上列出成员,因此我认为这不是问题所在。

我已经创建了一个简单的重现 cmdlet 和类,以及脚本。他们在这里:

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);
        }
    }
}

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.");
        }

    }
}

这是脚本及其输出

测试事件1.ps1 使用简单的 .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()

输出:(事件在 C# 中触发,但从未在 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.

测试事件2.ps1 在出现问题的情况下使用 async/BeginInvoke 语法

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

输出:(事件在 C# 中触发,但从未在 PS 中处理。此外,我得到一个异常。)

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.

测试事件3.ps1 使用 Register-ObjectEvent 方法。还在此处的输出中添加了更多诊断类型信息。

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

输出:(事件在 C# 中触发,但从未在 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)

在任何情况下,我实际上都没有触发事件处理程序操作。我也试过使用变量来保存 Action ,但似乎以同样的方式对待。 C# 中的 Console.Writeline 代码只是为了帮助调试。我在粘贴的脚本中删除了 DLL 的完整路径,只是为了让它们更易于阅读。 DLL 加载正常。

在装有 .NET Framework 4.6 (CLR 4) 的 Windows 10 专业版 64 位上使用 PowerShell 5。对比 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

对这个 PowerShell 菜鸟有什么建议吗?

最佳答案

Write-Output 不在控制台上显示对象,它将对象发送到管道中的下一个命令。如果您想立即在控制台上显示一些文本,则必须使用 Write-HostOut-Host cmdlet。

"Never use Write-Host because evil!"

我会这样说:“永远不要使用 Write-Host 中间数据,因为这是邪恶的!”。用 Write-Host 显示一些进度通知是可以的(尽管你可以考虑使用 Write-Progress 代替)或者用它来显示最终结果,但是 Write-Host 不向管道中的下一个命令发送任何内容,因此您无法进一步处理数据。

Why do all the other Write-Output commands write to the console?

当某些对象到达管道的最终末端时,PowerShell 必须对其进行处理。默认情况下它显示在主机上(在这种情况下是控制台),尽管您可以覆盖它:

New-Alias Out-Default Set-Clipboard # to copy anything into clipboard
New-Alias Out-Default Out-GridView  # to use grid view by default

PowerShell 不支持在某些任意线程中调用脚本 block ,因此对于异步事件,您必须使用 Register-ObjectEvent,以便 PowerShell 可以处理它们。

关于PowerShell 5 中从未处理过的 C# 事件,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/31955986/

相关文章:

c# - 没有标签的 Windows Phone BingMapsDirectionsTask 位置

arrays - 在数组中的指定索引位置添加元素。电源外壳

c# - 多框架实体库缺少条目

c# - 在 C# 中清除或检查事件处理程序

c# - 你如何在 C# 中隐藏封装?

powershell - 在powershell中读取目录中所有文件的所有行的最简单方法

powershell - 带有Jobs的Get-Counter返回不可用的数据

c# - MVC 中无法识别内容文件夹

c# - 可以在 Asp.Net Web Api 路由中使用句点吗?

javascript - 无法将 JavaScript 对象发布到 asp.net web api 端点