夕暮ログ

C#やJavascript、最近はAndroidなんかも好きなtinqのブログ。「夕暮れログ」

スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

【ビジュアライザ】シリアライズできないクラスをプロクシクラスを通してデバッグ

前回の記事もあわせてご覧ください

はじめに

前回シリアライズできないクラスはプロクシクラスを使う、と説明しました。 具体的に、やってみます。ここでは、System.Windows.Forms.Messageを使えるようにしてみます。
Messageは、.NETより低レベルにあるWin32APIで使われるウィンドウメッセージです。
基本的にはWM_PAINTはPaintイベントに、WM_LBUTTONDOWNはMouseDownイベントへなどとラップされているため出番は多くありません。
しかし、まだ低い処理を行う場合に必要になってきます。たとえば、WndProcなどの引数になっています。

この構造体はシリアライズ属性がついていないためシリアライズできません。
これは、情報がソフトを起動したりするたびに変わるため、保存しても意味は無く、シリアライズを行う必要がないと判断されたのでしょう。

それでも試してみると、以下のような例外が発生します。

これをデバッガビジュアライザでデバッグします。

下準備

少し前の記事を参考に空のビジュアライザを作ってください。
空なので、追加をするだけでかまいません。

プロクシクラスを作成

「これをシリアライズすることが正しいかどうか」ということは考えないことにして、プロクシクラスを作ります。
このMessage構造体が持つ必要のある情報は
  • IntPtr HWnd
    ウィンドウハンドル
  • int Msg
    メッセージの種類
  • IntPtr WParam
    パラメータ1
  • IntPtr LParam
    パラメータ2
  • IntPtr Result
    戻り値
です。詳しい説明はMSDNなどを参考にしてください。

さて、どのメンバもそのままシリアライズができる構造体・クラスのみです。
なのでそのまま同じ変数を定義したプロクシクラスを作成し、コンストラクタでコピーします。
ちなみに、これはデバッグするビジュアルスタジオ側からも見られるようにpublicにしておきましょう。
[Serializable]
public class ProxyMessage
{
	//必要な情報を保持するフィールド
    private IntPtr HWnd;
    private IntPtr LParam;
    private int Msg;
    private IntPtr Result;
    private IntPtr WParam;
    //コンストラクタ
    public ProxyMessage(object target)
    {
        Message m = (Message)target;
        this.HWnd = m.HWnd;
        this.LParam = m.LParam;
        this.Msg = m.Msg;
        this.Result = m.Result;
        this.WParam = m.WParam;
    }
    //取得する関数
    public Message GetMessage()
        {
            Message m = new Message();
            m.HWnd = this.HWnd;
            m.LParam = this.LParam;
            m.Msg = this.Msg;
            m.Result = this.Result;
            m.WParam = this.WParam;
            return m;
        }
    }
}
これだけです。
GetMessageは、プロパティでもできます。しかし、よくわかっていないのですが、プロパティもシリアライズされるのかもしれません。念のため、関数にしてあります。

オブジェクトソースを作る

続いて、このクラスを利用する、オブジェクトソースを作成します。
VisualizerObjectSourceというクラスを継承したクラスを作成してください。
名前は何でもいいですが、ProxyVisualizerObjectSourceとしておきます。
public class ProxyVisualizerObjectSource : VisualizerObjectSource
{
    public override void GetData(object target, System.IO.Stream outgoingData)
    {
        BinaryFormatter bf = new BinaryFormatter();
        bf.Serialize(outgoingData,new ProxyObject(target));
    }
}
とりあえず必要なGetDataをオーバーライドして書き換えます。
BinaryFormatterは、クラスをバイナリとして変換してくれます。
プライベートフィールドなどまでシリアライズを行うので、先ほどのProxyMessageクラスのフィールドもprivateにしました。
bf.Serialize(outgoingData,new ProxyObject(target)); ここで、Messageをプロクシクラスにtarget、すなわちデバッグ対象のMessageオブジェクトを渡して新しいインスタンスを作っています。
そして、それをoutgoingDataに渡しています。このoutgoingDataは、デバッガビジュアライザに渡すためのストリームです。

BinaryFormatterの詳しい仕様は、MSDNのページを参考にしてください。
BinaryFormatterクラス
GetDataに関する説明はこちらです。
VisualizerObjectSource.GetData メソッド

ビジュアライザを書き換える

ビジュアライザも書き換える必要があります。
このようになります。
public class Visualizer1 : DialogDebuggerVisualizer
{
    protected override void Show(IDialogVisualizerService windowService, IVisualizerObjectProvider objectProvider)
    {
    	//プロクシクラスからオブジェクトを取り出す
        Message m = ((ProxyMessage)objectProvider.GetObject()).GetMessage();
        //表示
        MessageBox.Show(m.ToString());
    }

    public static void TestShowVisualizer(object objectToVisualize)
    {
    	//ProxyVisualizerObjectSourceを指定
        VisualizerDevelopmentHost visualizerHost = new VisualizerDevelopmentHost(objectToVisualize, typeof(Visualizer1), typeof(ClassLibrary1.ProxyVisualizerObjectSource));
        visualizerHost.ShowVisualizer();
    }
}
一手間増えましたが、大して違いはありません。
ShowクラスのobjectProvider.GetObject()で取得できるオブジェクトは、先ほど送ったProxyMessageクラスです。
これをキャストし、GetMessageを呼び出してMessageを取得します。
これで、シリアライズできないクラスがデバッガビジュアライザに送られてきました。

TestShowVisualizerメソッドでの指定にProxyVisualizerObjectSourceをやる必要があるので注意してください。
ちなみに、ClassLibrary1というのは名前空間の名前です。

最後にもうひとつ。 ビジュアライザに指定するための属性です。
[assembly: System.Diagnostics.DebuggerVisualizer(
typeof(ClassLibrary1.Visualizer1),
typeof(ClassLibrary1.ProxyVisualizerObjectSource),
Target = typeof(Message),
Description = "Message Test")]
大体前のとおなじですが、少し違います。
そう、2つめの引数が、「VisualizerObjectSource」から「ClassLibrary1.ProxyVisualizerObjectSource」に変わっているのです。
こうしてやると、標準のものではなく、自作のオブジェクトソースが使えるようになります。

これで作業は完了。

テストする

さて、テストしてみます。少し前の記事を参照して設定をしてください。
デバッグのテストコードは以下のような感じでいいでしょう。 Message msg = new Message();
ClassLibrary1.Visualizer1.TestShowVisualizer( msg );
[Ctrl]+[F5]またはデバッグなしで実行をしてみても、ビジュアルスタジオから実行してもちゃんと表示できました。

ここで渡したのは空のメッセージなので、すべて0になっていますが、ちゃんと設定したものを渡してあげればちゃんと渡せていることがわかると思います。

次回、ReplaceObjectで変更して戻す方法を紹介します。
関連記事

コメント

ここをクリックしてコメントを投稿

非公開コメント

トラックバック

http://tinqwill.blog59.fc2.com/tb.php/40-36a173e3

« next  ホーム  prev »

プロフィール

tinq tinq(もしくはTinqWill)

Sky  For   Every 改装予定

プログラミングお勉強中の高校生。月一くらいは更新したい

最新記事

カテゴリ

月別アーカイブ

検索フォーム

最新コメント

リンク

最新トラックバック

FC2Ad

上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。