💻

バイナリ モジュール (C#) でいい感じに Write-Verbose したい

に公開

はじめに

なお、投稿が遅れたわけではなく、16 日が埋まっていなかったため穴埋めとして投稿しています。

Write-Verbose について

PowerShell では Verbose パラメーターを付与することでトレース情報を出力できます。モジュール内部では Write-Verbose を呼び出すことで出力内容を制御します。Script Module の場合は PowerShell のスクリプトからコマンドレットを呼び出すだけなので特に考慮は不要ですが、Binary Module の場合は事情が異なります。Cmdlet.WriteVerbose メソッドは static ではありません。単純なコマンドレットであれば ProcessRecord メソッド内に記述すれば十分ですが、ある程度規模が大きくなるとそうもいきません。また Cmdlet のインスタンスを持ち回すのも推奨されません。

Trace.WriteLine の活用

C# には Trace.WriteLine メソッドがあります。このメソッドを利用すればよいのではと考えましたが、標準では Trace.WriteLine の出力を PowerShell ホストに流し込む仕組みは用意されていません。そのため、TraceListener を自作する必要があります。

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 パラメーターが指定されていればホストにメッセージが出力されます。疎結合な設計となるため、テストも容易になります。

Discussion