.NET Framework SDKで始める.NETプログラミング(後編)

2.デバッガと逆アセンブラ

デジタルアドバンテージ 遠藤孝信
2001/05/18

Page1 Page2 Page3 Page4 Page5

GUI版デバッガ:DbgUrt.exe

 DbgUrtは、グラフィカル・インターフェイスを備えた.NETアプリケーション用のデバッガだ。このDgbUrtでは、C#やVB.NETなどで記述されたアプリケーションを、ソースコード・レベルで1行ずつ実行させながら、変数の内容や、アプリケーションで生成したオブジェクトのインスタンス変数の値などをチェックすることができる。

 DbgUrtの実行ファイルと関連ファイルは、.NET SDKをインストールしたフォルダの直下にある[GuiDebug]フォルダ以下に格納されている。前述したとおり、[スタート]−[プログラム]メニューにショートカットなどは追加されない。

DbgUrtの実行画面
VS.NETのそれに比較すると一見シンプルに見えるが、DgbUrtも内部的にはVS.NETと同じモジュールを使用しており、非常に多機能なデバッガとなっている。VS.NET同様、プログラマの好みに応じてGUIを柔軟にカスタマイズできる。これにより、よく使うツールバーを常時表示させる、頻繁に使うウィンドウをペインとしてドッキングさせておくなどが可能。画面は、前出のWinDes用のサンプル・プログラムをデバッグしているところ。
  ソースコードが表示されたペイン。このようにソースコードを見ながら、1行ずつステップ実行してプログラムの挙動を確認することができる。
  クラスに含まれるプロパティの値を確認するためのThisウィンドウ。ここでは、親クラスから引き継いだプロパティも階層的に表示される。
  この例では、「Hello WinDes World!」という文字列がラベルのTextプロパティに設定されているのが分かる。
  呼び出されたメソッドが順に表示されるCall Stackウィンドウ

 DbgUrtは、使わないときには自動的に隠れるペインや、ドッキング可能なタブ・コントロールが提供されるなど、VS.NETのワークスペースと共通した操作感を持っている。.NET SDKによってインストールされたDLLファイルを眺めると、VS.NETのそれと同じファイルが使用されているので、どうやらDbgUrtは、VS.NETのデバッガ部分だけを取り出して作られたようである。このためデバッグ作業に限れば、VS.NETとほぼ同等の処理が可能だ。DbgUrtの詳細な使い方は、すでにご紹介したオンライン・ドキュメント(「Reference Documentation」)の「Tools and Debuggers」で解説されている。

 VS.NETのデバッガに比較したDbgUrtの主だった機能制限は次のとおり。

  • Win32ネイティブ・コード・アプリケーションのデバッグはサポートされない。
  • リモート・デバッグ機能は実装されていない。
  • レジスタ・ウィンドウは実装されているが、レジスタ情報は表示されない。
  • ディスアセンブリ・ウィンドウは実装されているが、表示されるものは.NETランタイム・コード(MSIL)ではなく、実行中のWin32ネイティブ・コードである。

 それでは実際に、DbgUrtを使って簡単なデバッグ作業を行ってみよう。例としては、前出のWinDesのテスト用として作成したアプリケーション(実行ファイル名はWin32Form1.exe)を使用する。

 まず、C#やVB.NETで作成した.NETアプリケーションの実行を、DbgUrtでソースコード・レベルで追跡するには、プログラムのコンパイル時に「/debug」オプションが必要となる(WinDesでは、デフォルトでこのオプション付きでコンパイルされる)。「/debug」オプション付きでプログラムをコンパイルすると、実行ファイルのデバッグ情報を含んだpdbファイル(Program DataBaseファイル)が併せて作成される。

 デバッグ作業を開始するには、デバッグ対象の実行ファイルを[Debug]メニューの[Program To Debug]コマンドを実行し、表示される[Program To Debug]ダイアログで実行ファイルの場所や起動時パラメータなどを指定する。

Program To Debugダイアログ
デバッグ対象となる実行ファイルや起動時オプションなどをここで指定し、ファイルをロードする。
  デバッグ対象となる実行ファイルを指定する。
  Mainメソッドに引数を渡すときにはここで指定する。
  プログラム実行時のワーキング・ディレクトリ。デフォルトでは実行ファイルのあるディレクトリが設定される。

 プログラムがロードされると、プログラムのソースコードがソースコード・ペインに表示される(コンパイル時に「/debug」オプションを指定して、pdbファイルが 用意されている場合)。通常のデバッグ作業では、バグが潜んでいそうな箇所にブレークポイントを設定していったんプログラムの実行を停止し、そこから1行ずつステップ実行していくのが一般的だろう([Debug]メニューの[Step Into]コマンドか[F11]キーを使う)。この際DbgUrtでは、生成された変数やオブジェクトの状態を表示させることができる。上で示した画面は、WinDesプログラムで、ラベル・コントロールに文字列を設定するコードを実行し終えた時点のものだ。上の画面では、ウィンドウ下部に「This」というペインがある。これは、現在コードが実行されているクラスが持つプロパティの内容を表示する部分である。したがってこの例では、button1クラス(プッシュボタン)のプロパティが表示されている。なお、これはC#での例だが、VB.NETでは“This”ではなく“Me”となる。画面からも分かるとおり、ここでは親クラスから引き継いでいるプロパティなども階層的に表示させることができる。このためクラスに実際にどのようなプロパティが存在し、それらがどのような値を持っているのかを確認できる。これはデバッグだけでなく、クラス・ライブラリの構造を学ぶという意味でも役に立つだろう。

逆アセンブラ:ildasm.exe

 ildasmは、マネージド・コード用の逆アセンブラである。このツールは、コマンドラインとGUIの2つのインターフェイスを持っている。次のようにしてコマンド・プロンプトから実行すれば、ウィンドウを開くことなく、実行ファイルを逆アセンブルし、MSILのみで記述されたテキスト・ファイルを得ることができる。

ildasm /TEXT /OUT=Win32Form1.il Win32Form1.exe
ildasmをコマンド・プロンプトで実行する場合の例

 たとえばこの例では、Win32Form1.exeを逆アセンブルした結果がWin32Form1.ilというファイルに出力される。こうして得られたMSILファイルから、アセンブラ(ilasm.exe)を使って再度実行ファイルを作成することができる。このような使い方は例外的かもしれないが、.NET SDKのドキュメントによれば、.NETの機能を完全にサポートしていないような言語コンパイラを使用する場合には、そのコンパイラで生成された実行ファイルを逆アセンブリし、出力ファイルをMSILレベルで修正した後、アセンブラで最終的な実行ファイルを作成する必要があるとされている。

 一方、ウィンドウ・アプリケーションとして、エクスプローラなどからilasmを実行した場合には、次のようなウィンドウが表示され、実行ファイルの内部構造をグラフィカルに確認することができる。下の画面は、前出のWin32Form1.exeをildasmで開き、ツリーを展開したところだ。

Win32Form1.exeをildasmで開いたところ
ウィンドウ・アプリケーションとしてilasmを実行すれば、このようにグラフィカルに実行ファイルの構造を確認することができる。ここで、ツリーの各行の左端にある小さなアイコンは、それぞれ次のような要素を示している。
アイコン アイコンが示す要素
詳細あり
ネームスペース
クラス
フィールド(クラスが持つ変数)
メソッド
スタティック・メソッド

 この画面から分かるように、Win32Form1.exeは、「MANIFEST」と、「Win32Form1Namespeceネームスペース」内の「Win32Form1クラス」から構成されている。正確にはこれ以外にも、Windowsアプリケーションでは伝統的なEXEヘッダや、型情報などを記述したメタデータからなるCLRヘッダなどもexeファイルには含まれている。しかしこれらヘッダ情報は、「/HEADER」オプションを付けてコマンドラインからildasmを実行し、結果をテキストで出力した場合にのみ参照できる。GUIからは見ることはできない。

 MANIFEST(「積荷目録」などの意味があり、日本語では「マニフェスト」と表記される)は、そのアプリケーションが使用するコンポーネントや、そのアプリケーション自身についての各種情報が記述されたものである。マニフェストはコンパイラによって生成され、アプリケーションの中に納められる。このようにアプリケーションの情報がそのアプリケーション自身に埋め込まれていることから、.NETのアプリケーションは「自己記述型」と呼ばれることがある。マニフェストとして記録されるようになった情報は、従来はレジストリなどの外部ファイルに記録されていたが、.NETではそれをアプリケーション自身が持つことにより、システムに影響を与えることなく、いわゆる「ゼロ・インパクト」でインストール(zero-impact install)できるようにしている。

 それでは実際に、このマニフェストの中身を覗いてみよう。上のildasmのウィンドウで、「MANIFEST」の部分をマウスでダブル・クリックすると、次のような別のウィンドウが現れ、MANIFESTの内容が表示される。

MANIFESTの内容を表示したところ
ildasmで「MANIFEST」の部分をマウスでダブル・クリックすると、このように別ウィンドウでマニフェストの内容を確認できる。ここでWin32Form1.exeには4つのアセンブリが含まれていることが確認できる。

 マニフェストの詳細を解説するのは本稿の主旨ではないので別の機会に譲るとして、今回とりあえず注目してほしいのは、「.assembly」というブロックが4つ含まれており、それぞれに「.ver」によりバージョンが記載してある点だ。

 本稿前編で述べたとおり、アセンブリ(assembly)とは、.NETアプリケーションを構成する構成要素の最小の単位である。1つのアセンブリを複数のファイルで構成することも可能だが、アセンブリが単独のファイルだけで構成される場合には、そのファイルはexeファイルあるいdllファイルとなる(内部的には、今回のWin32Fomr1.exeも1つのアセンブリで構成されている)。

 つまりマニフェストとは、プログラムを構成するアセンブリについて記述した情報である。ここでWin32Form1.exeのマニフェストに注目すると、4つあるアセンブリのうち、最初の3つアセンブリの記述には「extern」が付いていることが分かる。これは、プログラムで使用する外部のアセンブリに関する記述だ。そして4つ目が、Win32Form1.exe自身のアセンブリに関する記述である。

 外部アセンブリであるSystem.WinForms、mscorlib、System.Drawingの実体は、それぞれ\WINNT\Microsoft.NET\Framework\v1.0.2204ディレクトリにあるSystem.WinForms.dll、mscorlib.dll、System.Drawing.dllである(当然、これらのdllファイルもそれぞれマニフェストをもっており、同様にildasmで確認することができる)。このように、外部アセンブリのバージョンをマニュフェストに格納しておくことによって、ファイル名が同一でバージョンの異なるDLLが存在したとしても、アプリケーションは正しいDLLを呼び出せるようになる。このようなしくみがなかった従来のWindows環境では、アプリケーションとDLL正しい組み合わせを運用レベルで維持するしかなった。これがいわゆるDLL地獄(DLL hell)である。ちなみに、マニフェストによるバージョン管理の仕組みは、Windows XPでも導入されている。ただしWindows XPのマニフェストは、実行モジュールに埋め込まれているものではなく、XMLで記述された外部ファイルとなっている。

 マニフェストの操作と同様にして、各メソッドをマウスでダブル・クリックすれば、その部分を逆アセンブルしたMSILコードを表示することができる。次の画面は、ボタンが押されたときに呼び出されるbutton1_Clickメソッドを逆アセンブルしてMSILコードを表示したところだ。このとき[View]メニューの[Show source lines]をチェックしておけば、MSIL中にC#のソースコードがコメントとして埋め込まれるため、コード間での対応が分かりやすくなる。

Win32Form1.exeのbutton1_ClickメソッドをMSILでダンプしているところ
C#では1行だが、これが結果的には4行(.maxstack文を含めると5行)のMSILコードにコンパイルされているのが分かる。
 

 INDEX
  [特集] .NET Framework SDKで始める.NETプログラミング(後編)
    1.フォーム・デザイナでGUIを設計する
  2.デバッガと逆アセンブラ
    3.サンプル・プログラムとチュートリアル
    4.実用性も高いサンプル・プログラム
 
  [特集] .NET Framework SDKで始める.NETプログラミング(前編)


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間