Cmdlet详细流
我目前正在使用PowerShell 5.0 SDK编写C#cmdlet。Cmdlet详细流
我试图将“第三方可执行文件的StandardError”管道从“实时”从powershell运行时输出到cmdlet输出。
我目前使用MedallionShell库来处理运行过程。我已经用普通的C#win窗体尝试了这一点,并使用Command.StandardError.PipeToAsync(Console.OpenStandardOutput())来获取打印输出,因为可执行文件以“实时”方式将其生成到控制台。
我试图创建我自己的Stream对象,它调用WriteVerbose,但它似乎没有打印任何东西到powershell屏幕(我正在传递-Verbose到cmdlet,当我运行它)。
我的电流看起来是这样的:
- 打开PowerShell ISE中
- 加载我模块(C#DLL)
- 叫我的cmdlet和参数
- Command.Run
- Command.StandardError.PipeToAsync(???)
- Command.Wait(在此步骤中,o输出应该流向PowerShell窗口)
- 检查Command.Result.Success。
任何人都可以点我在这个正确的方向?
您不能从任意线程调用Cmdlet
的Write
方法(如WriteVerbose
)。您需要将对此方法的调用编组回到管道线程。一种方法是实现消息循环,当其他线程想要在管道线程中调用某些东西时,它将处理来自其他线程的消息。
Add-Type @‘
using System;
using System.Collections.Concurrent;
using System.Diagnostics;
using System.Management.Automation;
using System.Threading;
[Cmdlet(VerbsLifecycle.Invoke, "Process")]
public class InvokeProcessCmdlet : Cmdlet {
[Parameter(Position = 1)]
public string FileName { get; set; }
[Parameter(Position = 2)]
public string Arguments { get; set; }
protected override void EndProcessing() {
using(BlockingCollection<Action> messageQueue = new BlockingCollection<Action>()) {
using(Process process = new Process {
StartInfo=new ProcessStartInfo(FileName, Arguments) {
UseShellExecute=false,
RedirectStandardOutput=true,
RedirectStandardError=true
},
EnableRaisingEvents=true
}) {
int numberOfCompleteRequests = 0;
Action complete =() => {
if(Interlocked.Increment(ref numberOfCompleteRequests)==3) {
messageQueue.CompleteAdding();
}
};
process.OutputDataReceived+=(sender, args) => {
if(args.Data==null) {
complete();
} else {
messageQueue.Add(() => WriteObject(args.Data));
}
};
process.ErrorDataReceived+=(sender, args) => {
if(args.Data==null) {
complete();
} else {
messageQueue.Add(() => WriteVerbose(args.Data));
}
};
process.Exited+=(sender, args) => complete();
process.Start();
process.BeginOutputReadLine();
process.BeginErrorReadLine();
foreach(Action action in messageQueue.GetConsumingEnumerable()) {
action();
}
}
}
}
}
’@ -PassThru | Select-Object -First 1 -ExpandProperty Assembly | Import-Module
你还可以用这样的测试:
Invoke-Process icacls 'C:\* /c' -Verbose
你的解释是有道理的,我做了WriteVerbose会知道如何处理电话的假设,无论我在哪里。然而,我并不完全理解你的代码示例,主要是变量'numberOfCompleteRequests','complete'操作和foreach。这个foreach是否永远运行直到'CompleteAdding'被调用? == 3检查的目的是什么? –
@AbdulKhan *这个foreach是否永远运行直到'CompleteAdding'被调用?* - 是的。 *'complete'action * - 我不想重复代码,但我也想访问局部变量'messageQueue'和'numberOfCompleteRequests',所以我用lambda表达式定义'complete'以关闭局部变量。 * == 3检查的目的是什么?* - 我想在继续之前发生3个事件:输出流关闭,错误流关闭和流程退出,所以我检查了'complete()'调用了3次。当然,你可以选择不同的事件。 – PetSerAl
非常感谢,这个解释帮助我编写了适合我的问题的消息循环版本。 –
难道你自己'Stream'实现通话'WriteVerbose'直接或他们元帅管线螺纹? – PetSerAl
我的流的实现采用了该cmdlet并用它来调用WriteVerbose。我从Write调用收到的字节,我使用Encoding.UTF8.GetString(字节)转换为字符串。 –
这不是我问的问题。你不能在任意线程上调用'WriteVerbose'。那么,你是否调用了'WriteVerbose'回到管道线程? – PetSerAl