1

我目前正在使用 PowerShell 5.0 SDK 编写 C# cmdlet。

当从powershell“实时”运行时,我正在尝试将第三方可执行文件的StandardError通过管道传输到cmdlet输出。

我目前正在使用 MedallionShell 库来处理运行该过程。我已经使用普通的 C# win 表单进行了尝试,并使用 Command.StandardError.PipeToAsync(Console.OpenStandardOutput()) 来获取要打印的输出,因为可执行文件“实时”将其生成到控制台。

我尝试创建自己的调用 WriteVerbose 的 Stream 对象,但它似乎没有在 powershell 屏幕上打印任何内容(我在运行时将 -Verbose 传递给 cmdlet)。

我当前的流程看起来像这样:

  1. 打开 Powershell ISE
  2. 加载我的模块(C# dll)
  3. 使用参数调用我的 cmdlet
    • 命令运行
    • Command.StandardError.PipeToAsync(???)
    • Command.Wait(在此步骤中,输出应流向 powershell 窗口)
    • 检查命令.结果.成功。

谁能指出我正确的方向?

4

2 回答 2

2

您不能只从任意线程调用Cmdlet'sWrite方法(如)。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
于 2016-11-11T20:16:19.913 回答
0

如果您从PSCmdletinstrad 派生,Cmdlet您将有权访问this.Host.UI.WriteVerboseLine可以从任何线程调用的,风险自负(我认为它不会以任何方式防止输出的字符串以错误的方式混合)无论如何,以我的经验到目前为止,它一直运行良好,如果 cmdlet 是只有您会使用的东西,我认为风险可能是可以接受的。

同样,如果在控制台中使用它效果很好,我不知道如果您稍后将详细流重定向到控制台以外的其他地方或没有“UI”的地方,它是否会表现出预期的方式

如果您有更多时间来实施它,@PetSerAl 解决方案肯定更合适

于 2018-06-20T08:47:20.683 回答