get function by filename and line number

Alcantarea を実装するにあたって、更新する関数を最小限にするため、Visual Studio のテキストエディタの現在位置の関数を取得してその関数だけを更新する必要がありました。この ファイル名と行番号からその位置の関数を取得する という処理が無駄に奥深かったので書き残しておこうと思います。
大きく分けて 2 通りの方法があると思われます。1 つはソースファイルをパースして関数を特定する方法。もう 1 つはデバッグ情報を使ってバイナリから情報を抽出する方法です。以下は実際に検証したアプローチとそのサンプルコードです。

ちなみに、Visual Studio アドインは現在開いてるドキュメントの情報を以下のようにして取得できます。
(TextDocument が必要な情報を大体持っています。ファイルのパスしかりカーソル位置しかり)

DTE dte = (DTE)GetService(typeof(DTE));
TextDocument tdoc = dte.ActiveDocument.Object() as TextDocument;


  • TextDocument.Selection.ActivePoint を使う (sample code)

アドインから Visual Studio が保持している情報を使うアプローチです。TextDocument.Selection.ActivePoint に現在位置に関連する情報が入っているのでそれを使います。最もストレートな方法で、大抵はこれで事足りると思われます。現在の Alcantarea はこの方法を用いています。
注意すべき点として、マクロで包まれてる関数とかでたまに取得できないことがあるのと、.sdf ファイル (Intellisense のデータベース) が何らかの理由で読めていない場合に例外が飛んできます。意外と万能ではないという感触です…。

また、ドキュメントに含まれるシンボル群を保持する FileCodeModel なるオブジェクトがあるのですが、こちらには罠があります。宣言が .h にあって定義が .cpp にあるシンボルは、.cpp 側の FileCodeModel には情報がありません。なぜか .h 側にだけあります。
選択範囲に含まれるシンボルを全部更新対象に加える、みたいなことを FileCodeModel に含まれる情報を巡回することで実現したかったのですが、この謎仕様により断念しました。CodeModel 関連は ここ から辿れるドキュメントが参考になります。
他には、VisualAssistX が外部にインターフェースを公開しているようなので、それを使えばより信頼性の高い情報を取得できるかもしれません (未検証)。

デバッグ情報を使うアプローチです。御存知の通り、デバッグ情報 (.pdb) には行情報が含まれています。SymEnumLines() を使うことでこの行情報を巡回できます。
簡単で信頼度も高い方法ですが、Alcantarea 実行中は実行コードは書き換えられる一方でデバッグ情報は変わらないという特殊な状況のため、編集するにつれてズレていくので使えませんでした。

こちらもデバッグ情報を使うアプローチです。.obj ファイルにも当然デバッグ情報が含まれており、行情報もあります。このデバッグ情報をがんばって自力でパースして該当関数を見つけます。
Visual Studio 2005 以降の .obj ファイルのデバッグ情報は CV8 (Code View 8) という形式らしいのですが、これはフォーマットが非公開です。(CV4 は公開されているものの、CV8 はほとんど別物になってる様子)
しかし、誰かが独自に解析して こういうメモ を残してくれています。サンプルコードはこのメモを元に実装したもので、いい感じに機能しているように見えます。

Alcantarea は今後のリリースでこのアプローチに差し替えるかもしれません。また、このメモの情報を元にカスタムデバッガを書けば、編集後もソースコードデバッグできるようにできそうな気がするのでいずれ検証してみたいところです。