2

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?

4

3 に答える 3

3

以下は、使いやすく、Cmdllet.WriteObject の動作を模倣するように、クラスにカプセル化されたキュー システムの例です。このようにして、別のスレッド内から WriteObject を呼び出すことができ、オブジェクトは PowerShell スレッドにマーシャリングされ、パイプラインに書き込まれます。

[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);
    }
}
于 2012-10-14T18:04:55.547 に答える
1

Start-Jobコマンドレットを、Get-Job、Wait-Job、Receive-Jobと一緒に見ることができます。

Start-Jobは、新しいスレッドを効果的に開始し、Receive-Jobでクエリして出力を取得できるJobIdを出力します。次に、現在実行中のすべてのジョブをループして、進行状況バーを更新できます。

http://blogs.technet.com/b/heyscriptingguy/archive/2012/08/10/use-background-jobs-to-run-a-powershell-server-uptime-report.aspxをご覧ください

于 2012-10-13T23:09:35.583 に答える