2016-07-15 22 views
1

我已经在C#中编写了一个cmdlet,它充当大量/长时间运行的同步操作的包装。该方法(别人的代码)通过事件处理程序在这个长时间运行期间报告百分比进度,我希望将这些方法与标准的WriteProgress方法挂钩以获得漂亮的打印进度栏。然而,我发现了以下错误消息:从事件处理程序报告Powershell进度

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.Execute()'叫op.ProgressChanged'长大的吗? – PetSerAl

+0

@PetSerAl Whelp,似乎是(a?)问题的问题。没想到op.Execute()会启动一些新线程。任何想法如何解决? – Benjin

+0

您定位的是PowerShell和.NET版本? – PetSerAl

回答

0

您需要手动将ProgressChanged事件处理程序编组回到PowerShell管道线程。它可以通过应用生产者 - 消费者模式来完成,其中ProgressChanged事件处理程序将是生产者和事件循环,PowerShell管道线程将成为消费者。它可以与支持的BlockingCollection<T>中的.NET Framework 4.0中引入的可容易实现:

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(); 
}