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

我在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()相同的线程中?

您需要手动将ProgressChanged事件处理程序封送回PowerShell管道线程。 它可以通过应用生产者 – 消费者模式来完成,其中ProgressChanged事件处理程序将是生产者,PowerShell管道线程中的事件循环将是使用者。 它可以通过.NET Framework 4.0中引入的BlockingCollection支持轻松实现:

 overrride void ProcessRecord() { Task longRunningOperation; using(BlockingCollection queue = new BlockingCollection()) { //offload LongRunningOperation to different thread to keep control on PowerShell pipeline thread longRunningOperation=Task.Run(() => { try { //replace EventHandler with ProgressChanged type EventHandler 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(); }