【问题标题】:Cmdlet verbose streamCmdlet 详细流
【发布时间】:2017-03-25 00:53:25
【问题描述】:

我目前正在使用 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.Run
    • Command.StandardError.PipeToAsync(???)
    • Command.Wait(在此步骤中,输出应流向 powershell 窗口)
    • 检查 Command.Result.Success。

谁能指出我正确的方向?

【问题讨论】:

  • 您自己的Stream 实现是直接调用WriteVerbose 还是将它们编组到管道线程?
  • 我的流实现采用了 cmdlet 并使用它来调用 WriteVerbose。我从 Write 调用中收到的字节,我使用 Encoding.UTF8.GetString(bytes) 转换为字符串。
  • 这不是我要问的。您不能只在任意线程上调用WriteVerbose。那么,您是否将对 WriteVerbose 的调用编组回管道线程?
  • 不,我没有,你如何将调用编组回管道线程?

标签: c# powershell cmdlets cmdlet pscmdlet


【解决方案1】:

您不能只从任意线程调用CmdletWrite 方法(如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”、“完成”操作和 foreach。在调用“CompleteAdding”之前,foreach 会一直运行吗? ==3 检查的目的是什么?
  • @AbdulKhan 在调用“CompleteAdding”之前,foreach 会一直运行吗? — 是的。 “完成”操作——我不想重复代码,但我也想访问局部变量 messageQueuenumberOfCompleteRequests,所以我用 lambda 表达式定义 complete对局部变量有闭包。 ==3 检查的目的是什么? — 在继续之前我希望发生 3 个事件:输出流关闭、错误流关闭和进程退出,所以我检查 complete() 调用了 3 次.当然,您可以选择不同的事件集。
  • 非常感谢,您的解释帮助我编写了适合我的问题的消息循环版本。
【解决方案2】:

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

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

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

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 2017-01-13
    • 1970-01-01
    • 2017-12-24
    • 1970-01-01
    • 1970-01-01
    • 2014-11-19
    • 2015-05-13
    • 2011-05-28
    相关资源
    最近更新 更多