在.NET事件中使用ref参数时,PowerShell崩溃

时间:2015-06-22 18:37:06

标签: .net powershell events powershell-v4.0

当调用我订阅了ref参数的.NET事件时,PowerShell崩溃。

我已将问题缩减为以下代码。

.NET:

namespace PSEventTest
{
    public delegate void BadHandler(ref string str);

    public class PSEventTest
    {
        public event BadHandler BadEvent;

        public void InvokeBad()
        {
            var handler = BadEvent;
            if (handler != null)
            {
                var str = "bad";
                handler(ref str);
            }
        }
    }
}

的PowerShell:

Add-Type -path $PSScriptRoot"\PSEventTest.dll"

$obj = New-Object PSEventTest.PSEventTest

$bad = Register-ObjectEvent -InputObject $obj -EventName BadEvent -Action {
    Write-Host "bad!" # it also crashes if this script-block is empty 
}

$obj.InvokeBad(); # crash

当我运行PowerShell脚本时,我得到了这个:

enter image description here

如果我按Debug我从Visual Studio中获取此信息:

enter image description here

copy exception detail to the clipboard给了我这个

System.NullReferenceException was unhandled
Message: An unhandled exception of type 'System.NullReferenceException' occurred in Unknown Module.
Additional information: Object reference not set to an instance of an object.

我想我必须以某种方式将字符串提供给-Action脚本块,但我不知道如何。

P.S。我正在使用PowerShell 4:

PS C:\Windows\system32> $PSVersionTable.PSVersion
Major  Minor  Build  Revision
-----  -----  -----  --------
4      0      -1     -1

1 个答案:

答案 0 :(得分:6)

我找到了解决此问题的解决方法

.NET:

namespace PSEventTest
{
    public delegate void BadHandler(ref string str);

    public class PSEventTest
    {
        public event BadHandler BadEvent;

        public string InvokeBad()
        {
            var handler = BadEvent;
            if (handler != null)
            {
                var str = "hi from .NET!";
                handler(ref str);
                return "from .NET: " + str;
            }
            return null;
        }
    }

    // as i mention below, this class can be implemented directly in the PS script
    public class BadEventSubscriber
    {
        public void Subscribe(PSEventTest legacyObj, Func<string, string> func)
        {
            legacyObj.BadEvent += delegate(ref string str)
            {
                var handler = func; 
                if (handler != null)
                {
                    var result = handler(str);
                    str = result;
                }
            };
        }
    }
}

的PowerShell:

Add-Type -path $PSScriptRoot"\PSEventTest.dll"

$legacyObj = New-Object PSEventTest.PSEventTest
$subscriber = New-Object PSEventTest.BadEventSubscriber

$subscriber.Subscribe($legacyObj, {
    param([string]$str)
    write-host "from PS: $str"
    "hi from PS!" 
})

write-host $legacyObj.InvokeBad();

结果:

from PS: hi from .NET!
from .NET: hi from PS!

之前我尝试创建一个(object sender, EventArgs e)类型的处理程序来包装(ref string str)处理程序,但这不起作用,因为PS事件与.NET事件不同步。

传递Func而不是使用事件处理程序似乎工作得更好,如上面的代码所示。

我尝试概括BadEventSubscriber使用Expression<Func<object, handler>>来选择Subscribe方法中传递的对象的事件,但是泛型和Delegate不要&#39一起玩得很好。所以你必须为你想要订阅的每个类和处理程序创建一个BadEventSubsciber类型的类(PS不支持&#34;支持&#34;),但它对我来说已经足够了时刻。

它不是真正的解决方案,因为你仍然需要额外的组件来包装&#34;将事件转换为Func,但作为解决方案,我认为没关系。

编辑:

现在我考虑一下,您可以直接在PowerShell中定义订阅者类,就像@RomanKuzmin在comment我原来的问题中所说的那样。因此,您只需在PowerShell中使用以下内容,而不是附加程序集:

Add-Type @"
    using System;

    public class BadEventSubscriber
    {
        public void Subscribe(PSEventTest.PSEventTest legacyObj, Func<string, string> func)
        {
            legacyObj.BadEvent += delegate(ref string str)
            {
                var handler = func; 
                if (handler != null)
                {
                    var result = handler(str);
                    str = result;
                }
            };
        }
    }
"@ -ReferencedAssemblies $PSScriptRoot"\PSEventTest.dll"

$subscriber = New-Object BadEventSubscriber