次の方法で共有


メタデータ API とトークンの使用

更新 : 2007 年 11 月

メタデータ API は C++ から呼び出すことができます。メタデータ API の使用方法は、メタデータ API を使用するクライアントの種類にある程度依存します。ほとんどのメタデータ API クライアントは、次の 2 つのカテゴリのいずれかに分類されます。

  • コンパイラ。Visual C++ 2005 のコンパイラのように、暫定的な .obj ファイルをビルドし、個別のリンカ フェーズを使用して、個々のコンパイル単位をターゲットである単一の移植可能な実行可能 (PE) ファイルにマージします。

  • RAD (Rapid Application Development) ツール。1 回の手順で PE ファイルをビルドおよび生成するときに、ビルド時までツール環境内ですべてのコード構造およびデータ構造を管理します。

他のクライアントは、この 2 つのスタイルの中間にある方法でメタデータ API を使用できます。一部のツールでは、メタデータ エンジンで最適化を実行できますが、トークンの再マップ情報が考慮されないことがあります。特定のトークンの種類に関する再マップ情報だけを使用することもできます。実際には、.obj ファイルを生成する場合でも、コンパイラは最適化を実行できない可能性があります。

コンパイルとリンクのスタイル

コンパイルとリンクのスタイルの対話では、コンパイラのフロントエンドは IMetaDataDispenserEx API を使用してメモリ内メタデータ スコープを設定した後、IMetaDataEmit API を使用して型とメンバを宣言し、「メタデータ トークンの概要」で説明されているメタデータ抽象化を処理します。ただし、フロントエンドは、メソッド実装情報 (実装がマネージかアンマネージか、MSIL かネイティブ コードかなど) または RVA (Relative Virtual Address) 情報を指定できません。これは、コンパイル時にその情報を確認できないためです。代わりに、コードが PE ファイルにコンパイルおよび出力されるため、バックエンドまたはリンカが後でこの情報を指定する必要があります。

PE ファイル内にメタデータ バイナリ用の余裕を残しておくために、ここではコンパイルによって、バックエンド ツールが対象のメタデータ バイナリの保存サイズに関する情報を取得できる必要があります。ただし、メソッド RVA とモジュール レベルの静的データ メンバ RVA が認識されメタデータに出力されるまで、ツールではメタデータ バイナリをファイルに保存する準備ができていません。対象の保存サイズを正確に計算するために、メタデータ エンジンはまず保存前の最適化を実行する必要があります。これは、このような最適化により、対象のバイナリをできる限り小さくできるためです。最適化には、データ構造の並べ替えなどが含まれます。これは、参照先が現在のスコープ内で宣言された型またはメンバの場合に、mdTypeRefトークンおよび mdMemberRef トークンを迅速に検索または最適化 (事前バインディング) するために行われます。このような最適化の並べ替えにより、ツールが実装および RVA 情報を出力するために再利用する必要があるメタデータ トークンの再マップが行われます。その結果、ツールとメタデータ エンジンが連携して、トークンの再マップを追跡する必要があります。

したがって、コンパイル時にメタデータを保持するための呼び出しシーケンスは、次のようになります。

  1. IMetaDataEmit::SetHandler。メタデータ エンジンが IID_IMapToken を問い合わせるために使用できる IUnknown インターフェイスを指定し、クライアントにトークンの再マップを通知します。SetHandler は、メタデータ スコープが作成された後、IMetaDataEmit::GetSaveSize を呼び出す前の任意の時点で呼び出すことができます。

  2. IMetaDataEmit::GetSaveSize。メタデータ バイナリの保存サイズを取得します。GetSaveSizeIMetaDataEmit::SetHandler で指定されている IMapToken インターフェイスを使用して、クライアントにトークンの再マップを通知します。IMapToken インターフェイスの指定に SetHandler を使用しなかった場合、最適化は行われません。これにより、暫定的な .obj ファイルを生成するコンパイラは、リンクおよびマージ フェーズ後に再実行される可能性がある不要な最適化をスキップできます。

  3. IMetaDataEmit::Save。必要に応じて IMetaDataEmit::SetRVA メソッドと他の IMetaDataEmit メソッドを使用して最終的な実装メタデータを出力した後、メタデータ バイナリを保持します。

次のレベルのコンパイルは、複数のコンパイル単位がマージされて、統合された PE ファイルになるときに、リンカ フェーズに入ります。その場合、メタデータ スコープをマージする必要があるだけでなく、新しい PE ファイルが生成されるときに RVA が再び変更されます。マージ フェーズでは、IMetaDataEmit::Merge メソッドは、呼び出されるたびに単一のインポート スコープと生成スコープを処理し、インポート スコープから生成スコープにメタデータ トークンを再マップします。また、マージ プロセスでは、クライアントに送信する必要がある継続的なエラーが発生する可能性があります。マージが完了した後、最終的な PE ファイルの生成により、IMetaDataEmit::GetSaveSize が呼び出され、トークンの再マップが再び行われます。

リンカによってメタデータを生成および保持するための呼び出しのシーケンスは、次のとおりです。

  1. IMetaDataEmit::SetHandler。メタデータ エンジンが、以前と同じ IID_IMapToken だけでなく、IID_IMetaDataError も問い合わせるために使用できる IUnknown インターフェイスを指定します。後者のインターフェイスは、マージによって発生した継続的なエラーをクライアントに通知するために使用されます。

  2. IMetaDataEmit::Merge。指定されたメタデータ スコープを現在の生成スコープにマージします。Merge は、IMapToken インターフェイスを使用してトークンの再マップをクライアントに通知し、IMetaDataError を使用して継続的なエラーをクライアントに通知します。

  3. IMetaDataEmit::GetSaveSize。メタデータ バイナリの対象の保存サイズを取得します。GetSaveSizeIMetaDataEmit::SetHandler で指定されている IMapToken インターフェイスを使用して、クライアントにトークンの再マップを通知します。何らかのツールを用意し、Merge でトークンの再マップを処理し、形式の最適化が行われた後、再び GetSaveSize で処理する必要があります。トークンの最後の通知は、そのようなツールが依存する最終的なマップを表します。

  4. IMetaDataEmit::Save。必要に応じて IMetaDataEmit::SetRVA メソッドと他の IMetaDataEmit メソッドを使用して最終的な実装メタデータを出力した後、メタデータ バイナリを保持します。

RAD ツール スタイル

コンパイルとリンクのスタイルの対話では、RAD ツールは IMetaDataDispenserEx インターフェイスを使用してメモリ内メタデータ スコープを設定した後、IMetaDataEmit インターフェイスを使用して型とメンバを宣言し、「メタデータ トークンの概要」で説明されているメタデータ抽象化を処理します。

コンパイルとリンクのスタイルとは対照的に、通常 RAD ツールは、1 回の手順で PE ファイルを生成します。1 回の引き渡しで宣言と実装情報が出力され、IMetaDataEmit::Merge を呼び出す必要はありません。したがって、RAD ツールがトークンの再マップの複雑さを処理する必要がある理由は、現在 IMetaDataEmit::GetSaveSize によって実行されている保存前の最適化を利用するためだけです。

通常、完全に最適化されたメタデータを出力できるツールは、適切に最適化されたファイルを生成するためにメタデータ エンジンを必要としません。ただし、メタデータ エンジンとファイル形式の将来の実装では、一部の最適化ストラテジが廃止される可能性があるため、最適化されたメタデータを出力する方法に関する明確な規則のセットがあります。

メタデータ宣言と実装情報を出力した後、呼び出しのシーケンスは次のようになります。

  1. IMetaDataEmit::SetRVA メソッドと他の IMetaDataEmit メソッド (必要に応じて)。最終的な実装メタデータを出力します。

  2. IMetaDataEmit::Save。メタデータ バイナリを保持します。

参照

その他の技術情報

メタデータの概要