💻

Binary Module (C#) でいい感じに Write-Verbose したい

2022/01/01に公開

はじめに

この記事は PowerShell Advent Calendar 2018 の参加記事です。

https://qiita.com/advent-calendar/2018/powershell

ちなみに投稿が遅れたのではなく 16 日が埋まっていなかったので穴埋めしています。ここ重要!

Write-Verbose について

PowerShell では Verbose パラメーターを付けることでトレース情報を出力させることができます。モジュールの内部では Write-Verbose を呼び出すことで出力内容を制御します。Script Module の場合、PowerShell のスクリプトからコマンドレットを呼び出すだけなので、特に何も考える必要はないのですが、Binary Module の場合は勝手が違います。なぜなら、Cmdlet.WriteVerbose メソッドは static ではないからです。単純なコマンドレットであれば、ProcessRecord メソッドで全部書けば済むかもしれません。ある程度の規模になってくるとそうもいきません。かといって Cmdlet のインスタンスを持ち回すのも美しいとは思えません。

Trace.WriteLine があるじゃないか

C# には Trace.WriteLine メソッドがあります。こいつを使えばいけるのではと思いましたが、標準では Trace.WriteLine をホストに流し込んでくれるような仕組みにはなっていないようです。[1] 仕方がないので TraceLister を自作します。

CmdletTraceListener.cs

public class CmdletTraceListener : TraceListener
{
    private readonly Cmdlet cmdlet;

    public CmdletTraceListener(Cmdlet cmdlet)
    {
        if (cmdlet == null)
        {
            throw new ArgumentNullException(nameof(cmdlet));
        }
        this.cmdlet = cmdlet;
    }

    public override void Write(string message)
    {
        this.cmdlet.WriteVerbose(message);
    }

    public override void WriteLine(string message)
    {
        this.cmdlet.WriteVerbose(message);
    }
}

TraceableCmdlet.cs

public abstract class TraceableCmdlet : PSCmdlet
{
    private readonly TraceListener traceListener;

    protected TraceableCmdlet()
    {
        this.traceListener = new CmdletTraceListener(this);
    }

    protected override void BeginProcessing()
    {
        Trace.Listeners.Add(this.traceListener);
        base.BeginProcessing();
    }

    protected override void EndProcessing()
    {
        base.EndProcessing();
        Trace.Listeners.Remove(this.traceListener);
    }
}

こうしてあげることで、どこからでも Trace.WriteLine を呼び出すだけで (Verbose パラメーターを指定していれば) ホストにメッセージが出力されますし、疎結合になるのでテストも容易になるのではないかと思います。

脚注
  1. Console.WriteLineWrite-Host と同様に動作します。 ↩︎

Discussion