WPFのBitmapSource用のデバッガービジュアライザー作ってみた
ダウンロード先
展開してMyVisualizer.dllを
ドキュメントフォルダにある使いたいバージョンのVisual StudioのVisualizersフォルダに入れるだけ
ユーザー名がwatenでVisual Studioのバージョンが2019なら
C:\Users\ユーザー名\Documents\Visual Studio 2019\Visualizers
対象のフレームワークは.NET Framework 4.7.2だからこれより古いのだと動かないかも?
2020/03/18追記
.NET Coreで使うなら
C:\Users\ユーザー名\Documents\Visual Studio 2019\Visualizers\netcoreapp
netcoreappフォルダは初期状態だと存在しないから新規作成
追記ここまで
作って動かしている環境は
OS:Windows 10 Home 64bit
VS:Visual Studio Community 2019
これを使うとデバッグ中のBitmapSource型の変数の中にある画像を表示することができる、たとえば
52行目で画像ファイルを読み込んで
53行目で切り抜き
54行目でPixelFormatを変換
この途中経過の
63行目で一時停止するようにしておいてデバッグ実行して
それぞれの変数にカーソルを合わせると出てくる虫眼鏡アイコンをクリックすると
ダイアログで画像が表示される、画像処理のアプリを作るときにあると便利
虫眼鏡アイコンはピン留めや画面下にあるローカルウィンドウのでも同じみたい
作り方手順メモ
- 新しいプロジェクトの作成
- プロジェクトにデバッガービジュアライザーを追加する
- 参照の追加
- デバッガービジュアライザーにコードを書いていく
- 確認テスト
- releaseでビルドしたものをVisual Studioに追加
1.新しいプロジェクトの作成
クラスライブラリ(.NET Framework)を選択、これ重要、似たものに
クラスライブラリ(.NET Standard)ってのがあるけど、これではできなかった
クラスライブラリ(.NET Framework)を選択(大事なことなので)
Sutandardはなんか違ったんだよねえ(1敗)
適当なプロジェクト名をつける
MyVisualizer
にした、これがdllの名前になる
作成ボタンを押して
ソリューションエクスプローラーを表示して、参照の項目を展開したところ
表示されているコードはClass1.csっていう自動で作成されたファイルのものなんだけど、これ使わないんだよねえ、よくわからん
2.プロジェクトにデバッガービジュアライザーを追加する
方法
メニューのプロジェクト→新しい項目の追加、
もしくは
ソリューションエクスプローラーでプロジェクト名の右クリックメニューから追加→新しい項目
開かれたダイアログの
一覧からデバッガービジュアライザーを選択して適当な名前をつける
MyDialog
にした、追加ボタンを押すと
デバッガービジュアライザーを追加したところ
なんかいっぱい書いてある
このときのソリューションエクスプローラーを見ると
デバッガービジュアライザー(MyDialog.cs)がプロジェクトに追加されているのがわかる、
その他にも必要な参照3つ
Microsoft.VisualStudio.DebuggerVisualizers
System.Drawing
System.Windows.Forms
が追加されている、かしこい
WPFのBitmapSourceを表示するには少し足りないので
3.参照の追加
方法は
メニューのプロジェクト→参照の追加、
もしくは
ソリューションエクスプローラーでプロジェクト名の右クリックメニューから追加→参照
これで参照マネージャーが表示される
右下の参照ボタンから追加できる
追加する参照は2つあってPresentationCore、WindowsBaseの2つ、これのパスは
- C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\PresentationCore.dll
- C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\WindowsBase.dll
なんだけどv4.7.2とかのバージョンらしきものはプロジェクトのものと合わせたほうがいいのかも?
たとえばPresentationCoreでデスクトップ検索すると
いっぱい出てくる
確認はプロジェクトのプロパティのアプリケーションの項目の対象のフレームワークで確認できる
メニューのプロジェクト→(プロジェクト名)のプロパティで
対象のフレームワークってとこ、今回はこの数値と同じ場所にあるdllを追加した
追加後のソリューションエクスプローラーの様子
それぞれ追加したdllの役目は
PresentationCoreはBitmapSourceなどWPF側で使う
WindowsBaseもInt32RectなどWPF側
4.デバッガービジュアライザーにコードを書いていく
usingの追加は
using System.Drawing;
using System.IO;
using System.Windows;
using System.Windows.Media;
using System.Windows.Media.Imaging;
BitmapSourceをシリアライズできる型に分解するクラスを作成
BitmapSource作成に最低限必要な要素を持つシリアライズ可能なクラス
名前はMyProxyにした、Proxyは代理、仲介とか
これを書いた場所は
ここ、MyDialogクラスの外なのでソリューションエクスプローラーでみると
MyDialog.cs
┣MyDialog
┗MyProxy
BitmapSourceは
- 幅Width、
- 高さHeight、
- Stride1行のByte数、
- PixelsPixelの色データ、
- PixelFormatピクセルフォーマット
この5つがあれば作成できる、それぞれの型はint, int, int, byte, PixelFormat、この中でPixelFormatだけはシリアライズできない型。
デバッガービジュアライザーで表示可能なのはできるのはシリアライズできる型だけらしくて、BitmapSourceはできない型!
なのでなんとかシリアライズできる型に分解したけどPixelFormatだけはムリ、ではないけどめんどくさかったり難しいので、どんなピクセルフォーマットの画像でもBgra32に変換して統一することにして、このクラスが持つのはシリアライズできるint型とbyte型の2種類。
クラスの頭に[Serializable]をつけているのは、このクラスはシリアライズできますっていう宣言?みたいなもの
BitmapSource→デバッガービジュアライザー→エラー
こうだったのを
BitmapSource→MyProxy→デバッガービジュアライザー→表示
こんな感じにするため?
VisualizerObjectSourceを継承したクラス作成
クラスの名前はMySourceにした、書いた場所はMyProxyと同じようにMyDialogクラスの外側、結果ソリューションエクスプローラーでみると
MyDialog.cs
┣MyDialog
┣MySource
┗MyProxy
になった
GetDataメソッドをオーバーライド
targetにBitmapSourceが入ってきて、それをStream型にして返すメソッド?
このままだとBitmapSourceが送り出されて結果エラーになるので、さっき書いたMyProxyクラスに変換して送り出す
受け取ったBitmapSourceからMyProxyを作成して、それを送り出す感じ?
Showメソッドの書き換え
よくわからん日本語が書いてあるけど、受け取った物を表示するところみたい
40行目、object data = (object)objectProvider.GetObject();
ここのdataに受け取ったものが入って
44行目以降でdataをFormで表示する
このGetObject()メソッドが、さっき書いたGetDataメソッドに繋がっているらしくてMyProxyが返ってくるので
MyProxy→BitmapSource→Bitmap
に変換する
BitmapSourceはFormに表示できないのでBitmapに変換する必要がある
MyProxy→BitmapSource
BitmapSourceのCreateメソッドを使ってMyProxyからBitmapSourceを作成
PixelFormatはBgra32に統一することにしたので決め打ち
BitmapSource→Bitmap
BitmapのLockBitsとBitmapSourceのCopyPixelsで、BitmapSourceのデータをBitmapに流し込んでいる感じ、これでBitmap完成
BitmapのPixelFormatはFormat32bppArgbってのにしているけど、これは
BitmapSourceのPixelFormat.Bgra32と互換性があるみたいで、これで問題なくできた
表示部分
BitmapはPictureBoxに表示して、それをFormに追加することにしたので
こう書き換えた、Dispose()はこれであっているかなあ
TestShowVisualizerメソッドの書き換え
このままだとテストで動かない?ので書き換え
書き換え後、引数を一つ足しただけで
3つ目の引数にMySourceクラスを指定
属性?アセンブリ情報?を追加
namespaceの上に付け加える、よくわからん
最初の引数にはビジュアライザーの型を指定するとあるこれは
このDialogDebuggerVisualizerを継承したクラスで
2つ目の引数
ビジュアライザーオブジェクトソースの型とあるので
このVisualizerObjectSourceを継承したクラスをそれぞれ指定するみたい
3つ目の引数は表示したいものだからBitmapSourceで
4つ目は虫眼鏡アイコンのリストに表示したい文字列を指定、
今回は🐭びっとまっぷそぉす🐭にした
これでデバッガービジュアライザーができたはずなので
ビルド
これで
dllが作成される
今回は
C:\Users\waten\Source\Repos\wpf_test2\MyVisualizer\MyVisualizer\bin\Debug
に作成された、次は期待通りに動くのかテスト
5.確認テスト
確認テスト用のプロジェクトをソリューションに追加
テストするには別のプロジェクトが必要らしので、ソリューションエクスプローラーのソリューションの右クリックメニューから追加→新しいプロジェクトから
コンソールアプリ(.NET Framework)を選択して次へ
名前はそのままConsoleApp1にした
作成ボタン押して
ソリューションにConsoleApp1が追加された
ソリューションエクスプローラーでも確認、参照の項目も展開したところ
プロジェクトをスタートアッププロジェクトに設定
ソリューションエクスプローラーでプロジェクト(新しく追加したほう)の右クリックメニューからスタートアッププロジェクトに設定をクリック
参照の追加
追加するのは3つ
- PresentationCore
- Microsoft.VisualStudio.DebuggerVisualizers
- さっきビルドしたdll
PresentationCoreはデバッガービジュアライザーに追加したものと同じ、次のDebuggerVisualizersはデバッガービジュアライザーのものと同じものでいいのかなと思ったのでオブジェクトブラウザーで確認してみる
ソリューションエクスプローラーのデバッガービジュアライザーの参照にあるDebuggerVisualizersの右クリックメニューからオブジェクトブラウザーで表示をクリック
右下にパスが表示されている、これを追加することにした
それぞれのパスは
- C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.7.2\PresentationCore.dll
- C:\Program Files (x86)\Microsoft Visual Studio\2019\Community\Common7\IDE\PublicAssemblies\Microsoft.VisualStudio.DebuggerVisualizers.dll
- C:\Users\waten\Source\Repos\wpf_test2\MyVisualizer\MyVisualizer\bin\Debug\MyVisualizer.dll
3つをテスト用のプロジェクトの参照に追加したところ
テストのコード
BitmapSourceをつかうので
using System.Windows.Media.Imaging;
を追加しておいて
14行目は画像ファイルからBitmapIamgeを作成、BitmapIamgeはBitmapSourceに派生みたいなものなので、これを15行目でデバッガービジュアライザーのTestShowVisualizerメソッドにBitmapImageを渡している
これでテスト準備完了!
デバッグの開始すると
真っ黒のウィンドウが表示された直後、その上に
画像が表示されたウィンドウが表示される、このウィンドウがデバッガービジュアライザーで表示されたもの、このウィンドウを閉じると真っ黒のウィンドウも閉じられてデバッグ終了になる
16行目で一時停止するようにして実行、15行目でさっきと同じようにウィンドウが表示されるのでそれを閉じて16行目まですすめて
imageの上にカーソルを持っていくと虫眼鏡アイコン
虫眼鏡アイコンの右の▼をクリックで登録しておいた文字列🐭びっとまっぷそぉす🐭が表示されてる!クリックで
Formが表示された!
もし期待通りに動かなかったら
- デバッガービジュアライザーのコードを修正
- ビルドしてから
- デバッグ開始(F5)
これを繰り返すんだけど、よく忘れるのが2番。テスト用のプロジェクトが参照しているのはdllなので、デバッガービジュアライザーのコードを修正したあとに、ビルドでdllを更新してからデバッグ開始ってことみたいねえ
ReleaseでビルドしたものをVisual Studioに追加
期待通りに動くようになったらReleaseでビルド
DebugからReleaseに変更してから
ビルド
出力ウィンドウで見るとdllの作成場所は
C:\Users\waten\Source\Repos\wpf_test2\MyVisualizer\MyVisualizer\bin\Release\MyVisualizer.dll
Releaseフォルダに作成される
ビルドできたらDebugに戻しておく
dllをVisual Studioに登録(コピペ)
登録する場所はドキュメントフォルダのVisual StudioフォルダのVisualizersフォルダ
今回はVisual Studio 2019に登録したいので
C:\Users\waten\Documents\Visual Studio 2019\Visualizers
ここにdllをコピー
Visualizersフォルダにdllをコピーしたところ、これで全て完了!
全然別のプロジェクトで確認
この記事の冒頭と同じように表示された!できた!!!!!!
表示できるのは
BitmapSource クラス
定義
名前空間:
System.Windows.Media.Imaging
アセンブリ:
PresentationCore.dll
派生
System.Windows.Interop.InteropBitmap
System.Windows.Media.Imaging.BitmapFrame
System.Windows.Media.Imaging.BitmapImage
System.Windows.Media.Imaging.CachedBitmap
System.Windows.Media.Imaging.ColorConvertedBitmap
System.Windows.Media.Imaging.CroppedBitmap
System.Windows.Media.Imaging.FormatConvertedBitmap
System.Windows.Media.Imaging.RenderTargetBitmap
System.Windows.Media.Imaging.TransformedBitmap
System.Windows.Media.Imaging.WriteableBitmapより引用
ここの派生にあるものなら表示できるはず
全体
- MyVisualizer_ソリューション
- ┣ConsoleApp1_テスト用プロジェクト
- ┃┗Program.cs
- ┗MyVisualizer_デバッガービジュアライザーのプロジェクト
- ┣Class1.cs
- ┗MyDialog.cs
- ┣MyDialog
- ┣MySource
- ┗MyProxy
6 ┗MyDialog.cs_デバッガービジュアライザー
デバッガービジュアライザーの本体?
MyDialog.cs
3┃┗Program.cs
テスト用
5 ┣Class1.cs
使っていないので初期状態のまま
ソリューションエクスプローラー
この配置
参照した主なところ
夕暮ログ デバッガビジュアライザを追加するのと同じことをする
夕暮ログ シリアライズできないクラスをデバッガビジュアライザでデバッグする
夕暮ログ 【ビジュアライザ】シリアライズできないクラスをプロクシクラスを通してデバッグ
かなり詳しく丁寧に解説なさっている、ここがなかったら今回のはできなかった
方法: ビジュアライザーをインストールする - Visual Studio | Microsoft Docs
シリアライズできない型の対処法はここが簡潔でわかりやすかった
今回は使わなかったけどPixelFormatをシリアライズする方法がある
BitmapSourceのまま表示する方法も書いてあるけど、これは読んでもわからなかった…コードも公開されていたようだけど、今はなくなっているみたい、もったいない
BitmapSourceからBitmapへの変換はこちらから
完走した感想
こんなに難しいとは思わなかった、WPFを触り始めたときにも作ろうとしたけど諦めた記憶があって、いつかできたらいいなあと思いつつ4年くらい経ったのかな?ようやくできた!諦めと今じゃなくていつかできたらいいなあってのは大事だねえ、ふと思い立って"デバッガービジュアライザー WPF BitmapSource"とかでググったら、やっぱりわからなくて特にオーバライドしたGetDataをどこから呼んだらいいのかと、PixelFormatのシリアライズ…は今回は使わなかったけど、この辺が時間かかったけど4日くらいでできて、確認のためにもう一度最初から作ったらdllのバージョンが違うとかのわけのわからんエラーが出て、これは結局バージョン違いじゃなくてビルドの順番かなんかがおかしかったのか、ビルドのクリーンとかリビルドとかそのへんをいじっていたら直って、わけわからんと思いつつ、この記事を書きながらまた一から作っていったんだけど、普通に動くのができてよかったわ
WPFをそのまま表示できればかなりラクなんだけどデバッガービジュアライザーはFormだけみたいなんだよねえ、FormにWPFの要素を表示できるようにするElementHostっていうのがFormのコントロールにあるみたいで、これも試してみたんだけど
Imageを置いただけのWindow
これのコードは
受け取ったBitmapSourceをImageのSourceプロパティに指定するだけのもの、このユーザーコントロールを
デバッガービジュアライザーのほうで使うようにしてみたけどエラーになる
133行目でElementHost作成して、そのChildプロパティにユーザーコントロールを指定して、これをFormのControlsに指定していて、エラーになる場所はユーザーコントロールを作成するところなんだけど、これはもうわからん、これができればBitmapSourceからBitmapへの変換が必要なくなるし、WPFの要素もそのまま使えるっぽいからいいんだけどねえ、わからん
あとは拡大とか保存とかの機能をつけたい
dpiも96で決め打ちしているのもProxyに加えたほうがいいかなあ
関連記事
次回
2ヶ月後