Show / Hide Table of Contents

Использование библиотеки System.Reflection.Metadata

В .NET для программной обработки сборок обычно используется отражение (reflection). Однако, отражение может работать только со сборками, загруженными в текущий контекст выполнения, поэтому оно имеет определенные ограничения: можно прочитать только сборки для той же целевой платформы, что и выполняемое приложение, все зависимости считываемой сборки должны быть доступны и др. Кроме того, отражение является довольно тяжелым и медленным механизмом. Компиляторам и утилитам для статического анализа нужен более быстрый низкоуровневый инструмент. Таким инструментом является библиотека System.Reflection.Metadata, созданная командой разработчиков .NET. Эта библиотека собирается из исходников в репозитории .NET Runtime. Для ее использования в .NET Framework необходимо подключить пакет NuGet System.Reflection.Metadata.

Эта библиотека является низкоуровневой и оперирует таблицами и кучами, составляющими формат метаданных CLI. Этот формат определен стандартом ECMA-335, подробнее см. Standard ECMA-335 - Common Language Infrastructure (CLI) на сайте ECMA. System.Reflection.Metadata рекомендуется к использованию разработчикам компиляторов и различных библиотек для работы со сборками. Тем, кому нужен более высокоуровневый инструмент, работающий по аналогии с отражением, лучше использовать MetadataLoadContext.

Класс MetadataReader

Для чтения содержимого метаданных сборок используется класс MetadataReader. Для создания объекта этого класса можно использовать конструктор, например MetadataReader(Byte*, Int32) создает MetadataReader на основе данных в указанном участке памяти. Для считывания метаданных из файла сборки формата Portable Executable, создайте экземпляр PEReader и используйте метод расширения GetMetadataReader(PEReader).

Следующий пример показывает, как создать MetadataReader для сборки и считать из него все определения типов:

using var fs = new FileStream("Example.dll", FileMode.Open, FileAccess.Read, FileShare.ReadWrite);
using var peReader = new PEReader(fs);

MetadataReader mr = peReader.GetMetadataReader();

foreach (TypeDefinitionHandle tdefh in mr.TypeDefinitions)
{
    TypeDefinition tdef = mr.GetTypeDefinition(tdefh);

    string ns = mr.GetString(tdef.Namespace);
    string name = mr.GetString(tdef.Name);
    Console.WriteLine($"{ns}.{name}");
}

Класс MethodBodyBlock

Тело метода в сборке .NET содержит инструкции Common Intermediate Language (CIL), которые определяют логику метода, а также информацию о его локальных переменных и блоках обработки исключений. Класс MethodBodyBlock предназначен для считывания этой информации. Для получения экземпляра MethodBodyBlock конкретного метода используется метод GetMethodBody.

Следующий пример показывает, как получить тела всех методов в указанном определении типа и отобразить информацию о них:

static void PrintMethods(PEReader reader, MetadataReader mr, TypeDefinition tdef)
{
    MethodDefinitionHandleCollection methods = tdef.GetMethods();

    foreach (MethodDefinitionHandle mdefh in methods)
    {
        MethodDefinition mdef = mr.GetMethodDefinition(mdefh);
        string mname = mr.GetString(mdef.Name);
        Console.WriteLine($"Method: {mname}");

        // Get the relative address of the method body in the executable
        int rva = mdef.RelativeVirtualAddress;

        if (rva == 0)
        {
            Console.WriteLine("Method body not found");
            Console.WriteLine();
            continue;
        }

        // Get method body information
        MethodBodyBlock mb = reader.GetMethodBody(rva);
        Console.WriteLine($"  Maximum stack size: {mb.MaxStack}");
        Console.WriteLine($"  Local variables initialized: {mb.LocalVariablesInitialized}");

        byte[] il = mb.GetILBytes();
        Console.WriteLine($"  Method body size: {il.Length}");
        Console.WriteLine($"  Exception regions: {mb.ExceptionRegions.Length}");
        Console.WriteLine();

        foreach (var region in mb.ExceptionRegions)
        {
            Console.WriteLine(region.Kind.ToString());
            Console.WriteLine($"  Try block offset: {region.TryOffset}");
            Console.WriteLine($"  Try block length: {region.TryLength}");
            Console.WriteLine($"  Handler offset: {region.HandlerOffset}");
            Console.WriteLine($"  Handler length: {region.HandlerLength}");
            Console.WriteLine();
        }
    }
}

Класс MetadataBuilder

Класс MetadataBuilder позволяет программно генерировать сборки .NET. Эти сборки можно сохранить в файл, в отличие от динамических сборок, сгенерированных с помощью класса AssemblyBuilder, который не поддерживает сохранение сборок в файл в .NET 5+ и .NET Core. API MetadataBuilder оперирует низкоуровневыми структурами метаданных, например, таблицами или бинарными объектами BLOB. Если вам нужен более простой способ динамической генерации сборок с использованием C#, см. CSharpCompilation в Roslyn API.

Следующий пример показывает, как создать MetadataBuilder:

var peStream = new FileStream(
    "ConsoleApplication.dll", FileMode.OpenOrCreate, FileAccess.ReadWrite
);

using (peStream)
{
    var ilBuilder = new BlobBuilder();
    var metadataBuilder = new MetadataBuilder();
}

Следующий пример показывает, как добавить определение модуля и сборки в MetadataBuilder:

Guid guid = new Guid("17D5DBE1-1143-3FAD-AAB3-1002F92068E7");

metadataBuilder.AddModule(
    0,
    metadataBuilder.GetOrAddString("ConsoleApplication.dll"),
    metadataBuilder.GetOrAddGuid(guid),
    default(GuidHandle),
    default(GuidHandle));

metadataBuilder.AddAssembly(
    metadataBuilder.GetOrAddString("ConsoleApplication"),
    version: new Version(1, 0, 0, 0),
    culture: default(StringHandle),
    publicKey: default(BlobHandle),
    flags: 0,
    hashAlgorithm: AssemblyHashAlgorithm.None);
    

Более подробный пример создания сборки см. на GitHub: Generate .NET Core assembly using MetadataBuilder.

См. также

  • Сборки в .NET
  • Манифест сборки
  • Отражение
  • Практическое руководство. Проверка содержимого сборки с помощью MetadataLoadContext
  • System.Reflection.Metadata
Back to top Неофициальная документация по .NET на русском языке. Лицензия: CC-BY 4.0. Основано на документации по .NET с Microsoft Docs
Generated by DocFX