EternalWindows
メタデータ API / アセンブリの情報

メタデータに格納されるトークンは、主に2種類に分けることができます。 1つは、コード内で定義した型やメソッドなどを表すトークンであり、 これまで取り上げたTypeDefトークンやMethodトークンが該当します。 そして、もう1つは定義ではなく参照に関するトークンです。 たとえば、コード上で参照している型を表すTypeRefトークンがこれに該当します。 話を分かりやすくするため、次に例を示します。

using System;

public class Class1
{
    public int Method1()
    {
        Random r = new Random();

        return r.Next(50);
    }
}

コードではClass1という型を定義しているため、それを表すTypeDefトークンがメタデータに格納されています。 また、Method1というメソッドを表すMethodトークンも格納されるでしょう。 このメソッドでは、System.Random型のオブジェクトを作成しているわけですが、 このように別のファイルで定義された型を参照している場合は、TypeRefトークンが格納されます。 実際にこれを確認したい場合は、IMetaDataImport::EnumTypeRefsを呼び出します。

WCHAR     szName[256];
mdTypeRef typeRefs[2048];

hCorEnum = NULL;
pMetaDataImport->EnumTypeRefs(&hCorEnum, typeRefs, sizeof(typeRefs), &uCount);
for (i = 0; i < uCount; i++) {
	pMetaDataImport->GetTypeRefProps(typeRefs[i], NULL, szName, 256, NULL);
	MessageBox(NULL, szName, TEXT("OK"), MB_OK);
}

これを実行すると、System.Randomという文字列が表示されるはずです。 間接的に参照されている型もTypeRefトークンとして格納されているため、System.Objectなども表示されるはずです。

CLRがマネージコードをJITコンパイルする際、型への参照を検出したら、それに関連するTypeRefトークンが取得されます。 そしてこれを基に型を実装するアセンブリを特定し、そのアセンブリにアクセスできるようにアセンブリをロードします。 型を実装するアセンブリは、GetTypeRefPropsの第2引数から特定できます。

mdAssemblyRef assemblyRef;

pMetaDataImport->GetTypeRefProps(typeRefs, &assemblyRef, NULL, 0, NULL);

第2引数で返される値は、mdAssemblyRefで識別できます。 TypeRefが型の参照ならば、これはアセンブリの参照ということになります。

AssemblyRefトークンの情報を取得するには、モジュールのメタデータではなく、アセンブリのマニフェストを対象にします。 アセンブリとは複数のモジュールの集まりであり、それら複数のモジュールのうち1つにマニフェストと呼ばれる情報が割り当てられます。 もし、アセンブリを構成するモジュールが1つだけである場合は、そのモジュールにマニフェストが割り当てられ、そのモジュールがアセンブリになります。 この場合、モジュールにはメタデータとマニフェストという2つのデータ領域が存在し、 前者にアクセスするのがこれまで使用してきたIMetaDataImportです。 そして、後者にアクセスするのがIMetaDataAssemblyImportです。 IMetaDataAssemblyImportを使用すれば、アセンブリを構成するファイルや外部に公開している型などを取得できます。

今回のプログラムは、アセンブリが参照している外部アセンブリの情報を取得します。

#include <windows.h>
#include <mscoree.h>
#include <cor.h>

int WINAPI WinMain(HINSTANCE hinst, HINSTANCE hinstPrev, LPSTR lpszCmdLine, int nCmdShow)
{
	IMetaDataDispenser      *pMetaDataDispenser;
	IMetaDataAssemblyImport *pMetaDataAssemblyImport; 
	HRESULT                 hr;
	ULONG                   i, uCount;
	mdAssemblyRef           assemblyRefs[2048];
	WCHAR                   szName[256];
	HCORENUM                hCorEnum;

	CoInitialize(NULL);
	
	hr = CoCreateInstance(CLSID_CorMetaDataDispenser, 0, CLSCTX_INPROC_SERVER, IID_IMetaDataDispenser, (void **)&pMetaDataDispenser);
	if (FAILED(hr)) {
		CoUninitialize();
		return 0;
	}

	hr = pMetaDataDispenser->OpenScope(L"C:\\ClassLibrary1.dll", ofRead, IID_IMetaDataAssemblyImport, (IUnknown **)&pMetaDataAssemblyImport);
	if (FAILED(hr)) {
		pMetaDataDispenser->Release();
		CoUninitialize();
		return 0;
	}

	hCorEnum = NULL;
	pMetaDataAssemblyImport->EnumAssemblyRefs(&hCorEnum, assemblyRefs, sizeof(assemblyRefs), &uCount);
	for (i = 0; i < uCount; i++) {
		pMetaDataAssemblyImport->GetAssemblyRefProps(assemblyRefs[i], NULL, NULL, szName, 256, NULL, NULL, NULL, NULL, NULL);
		MessageBoxW(NULL, szName, L"OK", MB_OK);
	}

	pMetaDataAssemblyImport->Release();
	pMetaDataDispenser->Release();
	CoUninitialize();
	
	return 0;
}

IMetaDataAssemblyImportを取得するために、OpenScopeにIID_IMetaDataAssemblyImportを指定します。 EnumAssemblyRefsを呼び出せば、マニフェストに格納されたAssemblyRefトークンを列挙できます。 GetAssemblyRefPropsはAssemblyRefトークンの情報を取得することができ、 第4引数には参照しているアセンブリの名前が返ります。 .NETの基本的な型はmscorlibに実装されていますから、これは必ず参照しているはずです。 フォームを表示するアセンブリの場合は、System.Windows.Formsなども参照しているでしょう。


戻る