Debugger Visualizerを自作する (Serializableではないクラスの場合)

今回は System.Security.Cryptography.RijndaelManaged に対するDebugger Visualizerを自作することを考えます。暗号化・復号化に必要なIVとKeyのバイト列の値を16進数表記で表示させるという特に面白くもなんともないサンプルです。

このクラスはSerializableではないので、前回のように単純にはいきません。

とりあえず作ってみる

核となるのはDialogDebuggerVisualizerを継承したクラスのShowメソッドの部分です。前回を参考に書くと以下のようになります。

public class RijndaelManagedDebuggerVisualizer : DialogDebuggerVisualizer
{
    protected override void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)
    {
        RijndaelManaged rm = (RijndaelManaged)objectProvider.GetObject();

        string message = string.Format(
            "IV:{0}\n\nKey:{1}", 
            BitConverter.ToString(rm.IV), 
            BitConverter.ToString(rm.Key)
        );
        MessageBox.Show(message, "RijndaelManagedDebuggerVisualizer", MessageBoxButtons.OK);
    }
}

しかし、RijndaelManaged はSerializableではないので、Visualizerを表示させようとするとエラーとなります。

解決策

objectProviderから送られてくるオブジェクトが RijndaelManaged である限り、この問題は解決できません。解決するには、以下のステップを踏みます。

  1. SerializableではないクラスをうまくSerializableなデータだけで表すProxyクラスを作る
  2. VisualizerのShowメソッドに送るオブジェクトを、そのProxyクラスに変更する
  3. 受信したShowメソッド内で、Proxyから元のオブジェクトを復元する

Proxyクラスの作成

これだけではよくわからないと思うので、実際にコードで説明します。まず新たに独自のProxyクラスをつくります。

[Serializable]
public class RijndaelManagedProxy
{
    public byte[] IV { get; private set; }
    public byte[] Key { get; private set; }

    public RijndaelManagedProxy(RijndaelManaged rm)
    {
        IV = rm.IV;
        Key = rm.Key;
    }
}

RijndaelManaged そのものはSerializableではありませんが、中身のIVとKeyはただのbyte配列であり、Serializableです。この2つの情報さえあれば今回のDebugger Visualizerの作成には事足ります。よって、Debugger VisualizerにRijndaelManagedを送る際にはこのクラスを使って擬似的にSerializableなクラスに変換してから送ります。

ObjectSourceの自作

次に、上記のProxyクラスを使ってもらうようにします。VisualizerObjectSourceを継承したクラスを作ることで、Debugger Visualizerにおけるデータの流れを制御できます。

デフォルトでは、以下のようなコードでシリアル化がされていると見なせます。

public class RijndaelManagedObjectSource : VisualizerObjectSource
{
    public override void GetData(object target, Stream outgoingData)
    {
        BinaryFormatter bf = new BinaryFormatter();
        bf.Serialize(outgoingData, target);
    }
}

このBinaryFormatterのSerializeにおいて、targetであるRijndaelManagedがSerializableではないためにエラーが起きていました。そこで、この部分を先ほど作成したProxyに置き換えます。

public class RijndaelManagedObjectSource : VisualizerObjectSource
{
    public override void GetData(object target, Stream outgoingData)
    {
        BinaryFormatter bf = new BinaryFormatter();
        bf.Serialize(outgoingData, new RijndaelManagedProxy((RijndaelManaged)target));
    }
}

RijndaelManagedProxyはSerializableなように作ってありますので、このシリアル化は成功します。


ObjectSourceを自作した場合は、AssemblyInfo.csに書く記述が少し変わります。以下のようにします。

[assembly: System.Diagnostics.DebuggerVisualizer(
    typeof(MyDebuggerVisualizers.RijndaelManagedDebuggerVisualizer),
    typeof(MyDebuggerVisualizers.RijndaelManagedObjectSource),
    Target = typeof(System.Security.Cryptography.RijndaelManaged),    
    Description = "RijndaelManaged Visualizer"
)]

Proxyからのデータの復元

これでShowメソッドに来るオブジェクトがProxyクラスになります。そこから 必要なデータを取得することで、Serializableでないオブジェクトの送信は完了です。

public class RijndaelManagedDebuggerVisualizer : DialogDebuggerVisualizer
{
    protected override void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)
    {
        RijndaelManagedProxy rmp = (RijndaelManagedProxy)objectProvider.GetObject();

        string message = string.Format(
            "IV:{0}\n\nKey:{1}", 
            BitConverter.ToString(rmp.IV), 
            BitConverter.ToString(rmp.Key)
        );
        MessageBox.Show(message, "RijndaelManagedDebuggerVisualizer", MessageBoxButtons.OK);
    }
}