I have a powershell cmdlet written in C# (deriving from PSCmdlet
) which will start a long-running task which should update its progress using WriteProgress()
while it is running. Since powershell will not allow a separate thread to use WriteObject
or WriteProgress
I had to create a Queue<object>
in the main thread and I add items to the queue from the task that I want to be written to the Pipeline/Progress. A while loop will dequeue objects as they come in and write to the pipline / progress bar.
This is working, but I wanted to see if there were any better practices for multi-threading with a powershell cmdlet that is written in C#/VB. For example with WPF I can always step onto the UI thread with UIComponent.Dispatcher.Invoke()
if I need to update a progress bar or UI Component. Is there anything equivalent that I can use to 'step onto' the powershell thread to update the UI or write to the pipeline?
Here is an example of the queue system encapsulated in a class so it is easier to use and mimics Cmdllet.WriteObject's behavior. This way you can call WriteObject from within the separate thread and the object will be marshalled onto the powershell thread and written to the pipeline.
[Cmdlet("Test", "Adapter")]
public class TestCmdlet : PSCmdlet
{
protected override void ProcessRecord()
{
PowerShellAdapter adapter = new PowerShellAdapter(this, 100);
Task.Factory.StartNew(() => {
for (int x = 0; x < 100; x++) {
adapter.WriteObject(x);
Thread.Sleep(100);
}
adapter.Finished = true;
});
adapter.Listen();
}
}
public class PowerShellAdapter
{
private Cmdlet Cmdlet { get; set; }
private Queue<object> Queue { get; set; }
private object LockToken { get; set; }
public bool Finished { get; set; }
public int Total { get; set; }
public int Count { get; set; }
public PowerShellAdapter(Cmdlet cmdlet, int total)
{
this.Cmdlet = cmdlet;
this.LockToken = new object();
this.Queue = new Queue<object>();
this.Finished = false;
this.Total = total;
}
public void Listen()
{
ProgressRecord progress = new ProgressRecord(1, "Counting to 100", " ");
while (!Finished || Queue.Count > 0)
{
while (Queue.Count > 0)
{
progress.PercentComplete = ++Count*100 / Total;
progress.StatusDescription = Count + "/" + Total;
Cmdlet.WriteObject(Queue.Dequeue());
Cmdlet.WriteProgress(progress);
}
Thread.Sleep(100);
}
}
public void WriteObject(object obj)
{
lock (LockToken)
Queue.Enqueue(obj);
}
}