当调用我订阅了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脚本时,我得到了这个:
如果我按Debug
我从Visual Studio中获取此信息:
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
答案 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