从事件处理程序报告PowerShell进度

3
我用C#编写了一个cmdlet,作为一个包装器来处理一个耗时的同步操作。此方法(别人的代码)通过事件处理程序在这个长时间运行的操作过程中报告百分比进度,并且我想将这些内容与Powershell的标准WriteProgress方法连接起来,以获取美观的进度条效果。然而,我收到以下错误消息: The WriteObject and WriteError methods cannot be called from outside the overrides of the BeginProcessing, ProcessRecord, and EndProcessing methods, and they can only be called from within the same thread. 下面是我的代码:
overrride void ProcessRecord()
{
    LongRunningOperation op = new LongRunningOperation();
    op.ProgressChanged += ProgressUpdate;
    op.Execute();
    op.ProgressChanged -= ProgressUpdate;
}

void ProgressUpdate(object sender, ProgressChangeEventArgs e)
{
   ProgressRecord progress = new ProgressRecord(activityId: 1, activity: "Moving data", statusDescription: "Current operation");
   progress.PercentComplete = e.ProgressPercentage;
   WriteProgress(progress);
}

有谁能发现我错在哪里了吗?

更新:看起来事件处理程序被从与ProcessRecord()不同的线程触发。我该如何将所需信息传回到与ProcessRecord()相同的线程中呢?


1
你确定 op.ProgressChanged 是在调用 op.Execute() 的同一线程中触发的吗? - user4003407
@PetSerAl 嗯,看起来这是个问题。我没想到 op.Execute() 会启动一些新线程。你有什么解决办法吗? - Benjin
你的目标是使用哪个版本的PowerShell和.NET? - user4003407
PowerShell 3,.NET 4.5 - Benjin
将lambda / delegate注册到事件中,而不是实例方法,是否会有所不同? - Mathias R. Jessen
@MathiasR.Jessen 同样的错误。 - Benjin
1个回答

1

您需要手动将ProgressChanged事件处理程序转换回PowerShell管道线程。这可以通过应用生产者-消费者模式来实现,其中ProgressChanged事件处理程序将作为生产者,PowerShell管道线程中的事件循环将作为消费者。它可以通过使用.NET Framework 4.0中引入的BlockingCollection<T>支持轻松实现:

overrride void ProcessRecord() {
    Task longRunningOperation;
    using(BlockingCollection<ProgressRecord> queue = new BlockingCollection<ProgressRecord>()) {
        //offload LongRunningOperation to different thread to keep control on PowerShell pipeline thread
        longRunningOperation=Task.Run(() => {
            try {
                //replace EventHandler<ProgressChangeEventArgs> with ProgressChanged type
                EventHandler<ProgressChangeEventArgs> handler =
                    //implemented as anonymous method to capture queue local variable
                    (object sender, ProgressChangeEventArgs e) => {
                        ProgressRecord progress = new ProgressRecord(activityId: 1, activity: "Moving data", statusDescription: "Current operation");
                        progress.PercentComplete = e.ProgressPercentage;
                        //queue ProgressRecord for processing in PowerShell pipeline thread
                        queue.Add(progress);
                    }
                LongRunningOperation op = new LongRunningOperation();
                op.ProgressChanged += handler;
                op.Execute();
                op.ProgressChanged -= handler;
            } finally {
                queue.CompleteAdding();
            }
        });
        //event loop
        for(;;) {
            ProgressRecord progress;
            if(!queue.TryTake(out progress, Timeout.Infinite)) {
                break;
            }
            WriteProgress(progress);
        }
    }
    //get any exception from LongRunningOperation
    longRunningOperation.GetAwaiter().GetResult();
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接