良い一日、
この記事では、アプリケーションのデバッグの過程で複雑なカスタムクラスを視覚化するのに役立つVisual Studioの拡張機能の作成について説明します。
背景
このプロジェクトでは、国内のジオメトリックコア
C3Dカーネルを積極的に使用します。 このライブラリは、曲線、ボディ、サーフェスなどを操作するための多数のクラスを提供します。 これらのクラスは複雑な構造を持ち、標準のVisual Studio視覚化ツールを使用してアプリケーションをデバッグするプロセスでは、たとえば、表面が特定の変数に格納されているかどうかを理解することは困難です。 また、複雑なアルゴリズムをデバッグする場合、アルゴリズムの各ステップでオブジェクトに何が起こるかを理解することが非常に重要です。
この問題をさまざまな方法で回避しようとしました。 たとえば、単純な2次元曲線の場合は、紙の上の点の座標を書きました。 そして、この曲線を点で描きました。 問題の2番目の解決策:オブジェクトを適切なタイミングでファイルに保存し、ライブラリからテストユーティリティでこのファイルを開きます。 これは本当にデバッグに役立ちますが、かなりの手作業が必要です。 オブジェクトをファイルに保存するコードを挿入し、アプリケーションを再コンパイルし、特定のアルゴリズムを実行するためにアプリケーションで必要なアクションを実行し、ユーティリティで保存されたファイルを開き、結果を確認し、必要に応じてアルゴリズムを修正し、手順全体を繰り返します。 一般的には耐えられますが、デバッグモードのVisual Studioで目的の変数を直接ポイントし、そこに格納されているオブジェクトがどのように見えるかを簡単に確認できるようにしたかったのです。
Visual Studio拡張
この問題の解決策を探して、OpenCource OpenCVライブラリのMicrosoftからVisual Studio
Image Watchの拡張機能に出会いました。 この拡張により、デバッグ中にcv :: Matタイプの変数の内容を表示し、ビットマップを読み取ることができます。 それから、同様の拡張機能を記述するようになりましたが、私たちのタイプ用です。 残念ながら、パブリックドメインでこの拡張機能のソースコードを見つけることはできませんでした。 Visual Studioのこのような拡張機能を記述する方法について、少しずつ情報を収集する必要がありました。 msdnのこのトピックに関するドキュメントは、すべてが悲しいものです。 また、多くの例はなく、1つの
std :: vector visualizerがあります。 見つけるのはそれほど簡単ではありません。 例の本質:デバッグモードでのstd :: vector <int>にあるint数のグラフの視覚化:
拡張機能を作成
拡張機能を作成するには、Visual Studio SDKをインストールする必要があります。 インストール後、プロジェクトウィザードに新しいプロジェクトタイプが表示されます。
新しいプロジェクトの作成ウィザードは、必要なファイルをすべて作成し、プロジェクトを構成します。
Microsoftの例の説明は繰り返さず、拡張機能の作成手順については簡単に説明しています。 この例の説明をご覧になりたい方はお勧めします。 この記事では、この例で説明されていない点に触れたいと思います。
変数の値を取得する
内容を確認したい変数、および拡張機能自体は異なるプロセスにあります。 この例から、より複雑なユーザータイプからデータを取得する方法はまだ明確ではありませんでした。 この例は、IDebugProperty3インターフェイスを使用して、ベクターの最初の要素のアドレスと最後の要素のアドレスを見つけるときのトリックを示しています。 アドレスを減算することにより、メモリのサイズを見つけ、このメモリをプロセスにコピーします。 例からのコードは次のとおりです。
オブジェクトからデータを取得するpublic int DisplayValue(uint ownerHwnd, uint visualizerId, IDebugProperty3 debugProperty) { int hr = VSConstants.S_OK; DEBUG_PROPERTY_INFO[] propertyInfo = new DEBUG_PROPERTY_INFO[1]; hr = debugProperty.GetPropertyInfo( enum_DEBUGPROP_INFO_FLAGS.DEBUGPROP_INFO_ALL, 10 , 10000 , new IDebugReference2[] { }, 0, propertyInfo); Debug.Assert(hr == VSConstants.S_OK, "IDebugProperty3.GetPropertyInfo failed");
すべて問題ありませんが、このデータが単一のメモリに格納されている場合、オブジェクトからデータを取得する方法を示しています。 どうやら、MS自体がImage Watch拡張機能で同様のアプローチを使用しています。 そこには、画像も単一のメモリに保存され、この部分の先頭へのポインタがあります。
しかし、ユーザータイプが複雑な階層構造を持ち、通常のデータ配列のように見えない場合はどうでしょうか? クラスが他のクラスの基本クラスへのポインタを格納している場合はさらに悪化します。 このようなオブジェクトを断片的に復元することは、非現実的なタスクのようです。 さらに、この設計は非常に脆弱です。新しいメンバーが中間クラスに追加されると、拡張機能が停止します。 理想的には、オブジェクト自体またはそのコピーを取得したかったのです。 残念ながら、私はこれを行う方法を見つけられませんでした。 しかし、必要なクラスがファイルまたはメモリ内のバッファにシリアル化できることを知って、共有メモリとベクトルを使用したハイブリッドアプローチを使用できると判断しました。 このソリューションはあまり洗練されておらず、クラスを編集する必要がありますが、非常に機能しています。 それに加えて、これ以上優れたものはありません。
実装
メソッドの本質:
各クラス(デビューしたい)に、1つのフィールドstd :: vector <char>を含む特別なクラスが追加されます。 ベクターには、マーカー行を保存します。これにより、共有メモリでシリアル化されたオブジェクトを見つけることができます。 次に、各非定数クラスメソッドで、関数への呼び出しを追加して、共有メモリにクラスを保存します。 これで、クラスが変更されるたびに、共有メモリに保存されます。
拡張機能自体では、MSの例のメソッドを使用して、オブジェクトからマーカーラインを取得します。 次に、マーカーによって、共有メモリからシリアル化されたオブジェクトを取得し、それを逆シリアル化します。 その結果、拡張機能にオブジェクトのコピーがあります。 さて、それは技術の問題です。 オブジェクトから、私たちにとって有用なデータを取得し、何らかの形で便利な形式で表示します。
HabraLineデバッグビジュアライザー
拡張機能の例は、この考えを示すために書かれました。 また、拡張機能の動作を示すために、簡単なライブラリが作成されました。 このライブラリには、HabraPointとHabraLineの2つのクラスしかありません。 さらに、共有メモリのシリアル化と操作に必要ないくつかのクラス。 HabraLineクラスは単なるセグメントです。 シリアル化と共有メモリブーストの使用には、ブーストが使用されます。 拡張機能をインストールした後、HabraLineタイプの変数の値を視覚化する機会があります。
拡張機能の動作を短いビデオで見ることができます:
拡張ソースへのリンク:
TYNTSデモプロジェクトへのリンク:
TYNTSこの記事が誰かにとって有用であり、Visual Studioの有用な拡張機能を作成するきっかけになることを願っています。
皆さん、頑張ってください。