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

C# 从事件处理程序报告Powershell进度,c#,.net,powershell,C#,.net,Powershell,我已经用C#编写了一个cmdlet,它充当重载/长时间运行的同步操作的包装器。该方法(其他人的代码)通过事件处理程序报告此长期运行操作期间的进度百分比,我想将其与powershell的标准WriteProgress方法挂钩,以获得打印精美的进度条。但是,我收到以下错误消息: 不能从BeginProcessing、ProcessRecord和EndProcessing方法重写之外调用WriteObject和WriteError方法,只能从同一线程内调用它们。 这是我的密码: overrride v

我已经用C#编写了一个cmdlet,它充当重载/长时间运行的同步操作的包装器。该方法(其他人的代码)通过事件处理程序报告此长期运行操作期间的进度百分比,我想将其与powershell的标准
WriteProgress
方法挂钩,以获得打印精美的进度条。但是,我收到以下错误消息:

不能从BeginProcessing、ProcessRecord和EndProcessing方法重写之外调用WriteObject和WriteError方法,只能从同一线程内调用它们。

这是我的密码:

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中引入的支持,可以轻松实现:

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();
}
override void ProcessRecord(){
任务长时间运行操作;
使用(BlockingCollection队列=新建BlockingCollection()){
//将LongRunning操作卸载到不同的线程,以保持对PowerShell管道线程的控制
longRunningOperation=任务。运行(()=>{
试一试{
//将EventHandler替换为ProgressChanged类型
事件处理程序=
//实现为匿名方法以捕获队列局部变量
(对象发送方,ProgressChangeEventArgs e)=>{
ProgressRecord progress=新的ProgressRecord(activityId:1,activity:“移动数据”,statusDescription:“当前操作”);
progress.PercentComplete=e.ProgressPercentage;
//用于在PowerShell管道线程中处理的队列ProgressRecord
添加(进度);
}
LongRunningOperation op=新的LongRunningOperation();
op.ProgressChanged+=处理程序;
op.Execute();
op.ProgressChanged-=处理程序;
}最后{
queue.CompleteAdding();
}
});
//事件循环
对于(;;){
进度记录进度;
if(!queue.TryTake(out progress,Timeout.Infinite)){
打破
}
写进度(进度);
}
}
//从LongRunningOperation获取任何异常
longRunningOperation.GetAwaiter().GetResult();
}

您确定
op.ProgressChanged
op.Execute()
调用的?@PetSerAl-Whelp在同一线程中引发,这似乎是(a?)问题。没想到op.Execute()会产生一些新线程。知道如何解决这个问题吗?您的目标是什么PowerShell和.NET版本?如果您向事件注册lambda/delegate而不是实例方法,PowerShell 3、.NET 4.5是否会产生影响?