我试图在.NET中创建一个PowerShell加载项,这将允许我访问某些Windows 10功能。我知道C#,但PowerShell对我来说相对较新。
我的所有函数调用和cmdlet都在工作。我可以让C#类从PowerShell做一些事情。让我沮丧的是事件提升和处理让C#代码回调。我现在已经有三个晚上了。
在线和在此处的示例总是显示计时器和文件系统观察者,或者有时是Windows表单。我从未见过使用某人自己编写的课程的例子。不确定在举起活动时是否需要一些糖,或者别的什么。
我的代码和其他代码之间的主要区别在于我有一个工厂类,它返回引发事件的对象实例。我使用它而不是调用new-object。但是,PowerShell会识别该类型并很乐意在其上列出成员,因此我认为这不是问题。
我创建了一个简单的repro 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.");
}
}
}
以下是脚本及其输出
测试events1.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.
测试events2.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.
测试events3.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)
在任何情况下,我都不会实际触发事件处理程序操作。我也尝试使用变量来保持动作,但这似乎也是以同样的方式对待。 C#中的Console.Writeline代码只是为了帮助调试。我在粘贴的脚本中删除了DLL的完整路径,只是为了使它们更容易阅读。 DLL加载正常。
在Windows 10 Pro 64位上使用PowerShell 5和.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
对此PowerShell noob的任何建议?
答案 0 :(得分:3)
Write-Output
不在控制台上显示对象,它将对象发送到管道中的下一个命令。如果要在控制台上立即显示某些文本,则必须使用Write-Host
或Out-Host
cmdlet。
“永远不要使用Write-Host,因为邪恶!”
我会这样说:“永远不要在中间数据上使用Write-Host ,因为邪恶!”。可以使用Write-Host
显示一些进度通知(尽管您可以考虑使用Write-Progress
代替)或使用它来显示最终结果,但Write-Host
不向管道中的下一个命令发送任何内容,所以你不能再进一步处理数据了。
为什么所有其他Write-Output命令都写入控制台?
当某个对象到达管道的最终端时,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不支持某些任意线程中的脚本块调用,因此对于必须使用Register-ObjectEvent
的异步事件,PowerShell可以处理它们。