MetadataBuilder Class

Definition

The MetadataBuilder class writes metadata for an assembly in a highly performant manner. It is designed for use by compilers and other assembly generation tools.

public ref class MetadataBuilder sealed
public sealed class MetadataBuilder
type MetadataBuilder = class
Public NotInheritable Class MetadataBuilder
Inheritance
MetadataBuilder

Examples

This example shows how to emit a console application assembly using MetadataBuilder:

private static readonly Guid s_guid = new Guid("87D4DBE1-1143-4FAD-AAB3-1001F92068E6");
private static readonly BlobContentId s_contentId = new BlobContentId(s_guid, 0x04030201);

private static MethodDefinitionHandle EmitHelloWorld(MetadataBuilder metadata, BlobBuilder ilBuilder)
{
    // Create module and assembly for a console application.
    metadata.AddModule(
        0,
        metadata.GetOrAddString("ConsoleApplication.exe"),
        metadata.GetOrAddGuid(s_guid),
        default(GuidHandle),
        default(GuidHandle));

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

    // Create references to System.Object and System.Console types.
    AssemblyReferenceHandle mscorlibAssemblyRef = metadata.AddAssemblyReference(
        name: metadata.GetOrAddString("mscorlib"),
        version: new Version(4, 0, 0, 0),
        culture: default(StringHandle),
        publicKeyOrToken: metadata.GetOrAddBlob(
            new byte[] { 0xB7, 0x7A, 0x5C, 0x56, 0x19, 0x34, 0xE0, 0x89 }
            ),
        flags: default(AssemblyFlags),
        hashValue: default(BlobHandle));

    TypeReferenceHandle systemObjectTypeRef = metadata.AddTypeReference(
        mscorlibAssemblyRef,
        metadata.GetOrAddString("System"),
        metadata.GetOrAddString("Object"));

    TypeReferenceHandle systemConsoleTypeRefHandle = metadata.AddTypeReference(
        mscorlibAssemblyRef,
        metadata.GetOrAddString("System"),
        metadata.GetOrAddString("Console"));

    // Get reference to Console.WriteLine(string) method.
    var consoleWriteLineSignature = new BlobBuilder();

    new BlobEncoder(consoleWriteLineSignature).
        MethodSignature().
        Parameters(1,
            returnType => returnType.Void(),
            parameters => parameters.AddParameter().Type().String());

    MemberReferenceHandle consoleWriteLineMemberRef = metadata.AddMemberReference(
        systemConsoleTypeRefHandle,
        metadata.GetOrAddString("WriteLine"),
        metadata.GetOrAddBlob(consoleWriteLineSignature));

    // Get reference to Object's constructor.
    var parameterlessCtorSignature = new BlobBuilder();

    new BlobEncoder(parameterlessCtorSignature).
        MethodSignature(isInstanceMethod: true).
        Parameters(0, returnType => returnType.Void(), parameters => { });

    BlobHandle parameterlessCtorBlobIndex = metadata.GetOrAddBlob(parameterlessCtorSignature);

    MemberReferenceHandle objectCtorMemberRef = metadata.AddMemberReference(
        systemObjectTypeRef,
        metadata.GetOrAddString(".ctor"),
        parameterlessCtorBlobIndex);

    // Create signature for "void Main()" method.
    var mainSignature = new BlobBuilder();

    new BlobEncoder(mainSignature).
        MethodSignature().
        Parameters(0, returnType => returnType.Void(), parameters => { });

    var methodBodyStream = new MethodBodyStreamEncoder(ilBuilder);

    var codeBuilder = new BlobBuilder();
    InstructionEncoder il;
    
    // Emit IL for Program::.ctor
    il = new InstructionEncoder(codeBuilder);

    // ldarg.0
    il.LoadArgument(0); 

    // call instance void [mscorlib]System.Object::.ctor()
    il.Call(objectCtorMemberRef);

    // ret
    il.OpCode(ILOpCode.Ret);

    int ctorBodyOffset = methodBodyStream.AddMethodBody(il);
    codeBuilder.Clear();

    // Emit IL for Program::Main
    var flowBuilder = new ControlFlowBuilder();
    il = new InstructionEncoder(codeBuilder, flowBuilder);

    // ldstr "hello"
    il.LoadString(metadata.GetOrAddUserString("Hello, world"));

    // call void [mscorlib]System.Console::WriteLine(string)
    il.Call(consoleWriteLineMemberRef);

    // ret
    il.OpCode(ILOpCode.Ret);

    int mainBodyOffset = methodBodyStream.AddMethodBody(il);
    codeBuilder.Clear();

    // Create method definition for Program::Main
    MethodDefinitionHandle mainMethodDef = metadata.AddMethodDefinition(
        MethodAttributes.Public | MethodAttributes.Static | MethodAttributes.HideBySig,
        MethodImplAttributes.IL,
        metadata.GetOrAddString("Main"),
        metadata.GetOrAddBlob(mainSignature),
        mainBodyOffset,
        parameterList: default(ParameterHandle));

    // Create method definition for Program::.ctor
    MethodDefinitionHandle ctorDef = metadata.AddMethodDefinition(
        MethodAttributes.Public | MethodAttributes.HideBySig | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName,
        MethodImplAttributes.IL,
        metadata.GetOrAddString(".ctor"),
        parameterlessCtorBlobIndex,
        ctorBodyOffset,
        parameterList: default(ParameterHandle));

    // Create type definition for the special <Module> type that holds global functions
    metadata.AddTypeDefinition(
        default(TypeAttributes),
        default(StringHandle),
        metadata.GetOrAddString("<Module>"),
        baseType: default(EntityHandle),
        fieldList: MetadataTokens.FieldDefinitionHandle(1),
        methodList: mainMethodDef);

    // Create type definition for ConsoleApplication.Program
    metadata.AddTypeDefinition(
        TypeAttributes.Class | TypeAttributes.Public | TypeAttributes.AutoLayout | TypeAttributes.BeforeFieldInit,
        metadata.GetOrAddString("ConsoleApplication"),
        metadata.GetOrAddString("Program"),
        baseType: systemObjectTypeRef,
        fieldList: MetadataTokens.FieldDefinitionHandle(1),
        methodList: mainMethodDef);
    
    return mainMethodDef;
}

private static void WritePEImage(
    Stream peStream,
    MetadataBuilder metadataBuilder,
    BlobBuilder ilBuilder,
    MethodDefinitionHandle entryPointHandle
    )
{
    // Create executable with the managed metadata from the specified MetadataBuilder.
    var peHeaderBuilder = new PEHeaderBuilder(
        imageCharacteristics: Characteristics.ExecutableImage
        );

    var peBuilder = new ManagedPEBuilder(
        peHeaderBuilder,
        new MetadataRootBuilder(metadataBuilder),
        ilBuilder,
        entryPoint: entryPointHandle,
        flags: CorFlags.ILOnly,
        deterministicIdProvider: content => s_contentId);

    // Write executable into the specified stream.
    var peBlob = new BlobBuilder();
    BlobContentId contentId = peBuilder.Serialize(peBlob);
    peBlob.WriteContentTo(peStream);
}

public static void BuildHelloWorldApp()
{
    using var peStream = new FileStream(
        "ConsoleApplication.exe", FileMode.OpenOrCreate, FileAccess.ReadWrite
        );
    
    var ilBuilder = new BlobBuilder();
    var metadataBuilder = new MetadataBuilder();

    MethodDefinitionHandle entryPoint = EmitHelloWorld(metadataBuilder, ilBuilder);
    WritePEImage(peStream, metadataBuilder, ilBuilder, entryPoint);
}

Remarks

The MetadataBuilder class enables you to programmatically generate assemblies. These assemblies can be saved to a file, unlike dynamic assemblies generated by AssemblyBuilder class, which does not support saving assemblies to a file on .NET 5+ and .NET Core.

The MetadataBuilder API operates low-level metadata constructs, such as tables or blobs. For a simpler way to generate assemblies dynamically using C#, see CSharpCompilation in Roslyn API.

The format of CLI metadata is defined by the ECMA-335 specification. For more information, see Standard ECMA-335 - Common Language Infrastructure (CLI) on the Ecma International Web site.

Constructors

MetadataBuilder(Int32, Int32, Int32, Int32)

Creates a builder for metadata tables and heaps.

Methods

AddAssembly(StringHandle, Version, StringHandle, BlobHandle, AssemblyFlags, AssemblyHashAlgorithm)
AddAssemblyFile(StringHandle, BlobHandle, Boolean)
AddAssemblyReference(StringHandle, Version, StringHandle, BlobHandle, AssemblyFlags, BlobHandle)
AddConstant(EntityHandle, Object)

Adds a default value for a parameter, field or property.

AddCustomAttribute(EntityHandle, EntityHandle, BlobHandle)

Adds a custom attribute.

AddCustomDebugInformation(EntityHandle, GuidHandle, BlobHandle)

Adds custom debug information.

AddDeclarativeSecurityAttribute(EntityHandle, DeclarativeSecurityAction, BlobHandle)

Adds a declarative security attribute to a type, method, or assembly.

AddDocument(BlobHandle, GuidHandle, BlobHandle, GuidHandle)

Adds document debug information.

AddEncLogEntry(EntityHandle, EditAndContinueOperation)
AddEncMapEntry(EntityHandle)
AddEvent(EventAttributes, StringHandle, EntityHandle)

Adds an event definition.

AddEventMap(TypeDefinitionHandle, EventDefinitionHandle)
AddExportedType(TypeAttributes, StringHandle, StringHandle, EntityHandle, Int32)

Adds an exported type.

AddFieldDefinition(FieldAttributes, StringHandle, BlobHandle)

Adds a field definition.

AddFieldLayout(FieldDefinitionHandle, Int32)

Defines a field layout of a field definition.

AddFieldRelativeVirtualAddress(FieldDefinitionHandle, Int32)

Adds a mapping from a field to its initial value stored in the PE image.

AddGenericParameter(EntityHandle, GenericParameterAttributes, StringHandle, Int32)

Adds a generic parameter definition.

AddGenericParameterConstraint(GenericParameterHandle, EntityHandle)

Adds a type constraint to a generic parameter.

AddImportScope(ImportScopeHandle, BlobHandle)

Adds local scope debug information.

AddInterfaceImplementation(TypeDefinitionHandle, EntityHandle)

Adds an interface implementation to a type.

AddLocalConstant(StringHandle, BlobHandle)

Adds local constant debug information.

AddLocalScope(MethodDefinitionHandle, ImportScopeHandle, LocalVariableHandle, LocalConstantHandle, Int32, Int32)

Adds local scope debug information.

AddLocalVariable(LocalVariableAttributes, Int32, StringHandle)

Adds local variable debug information.

AddManifestResource(ManifestResourceAttributes, StringHandle, EntityHandle, UInt32)

Adds a manifest resource.

AddMarshallingDescriptor(EntityHandle, BlobHandle)

Adds marshalling information to a field or a parameter.

AddMemberReference(EntityHandle, StringHandle, BlobHandle)

Adds a MemberRef table row.

AddMethodDebugInformation(DocumentHandle, BlobHandle)

Adds method debug information.

AddMethodDefinition(MethodAttributes, MethodImplAttributes, StringHandle, BlobHandle, Int32, ParameterHandle)

Adds a method definition.

AddMethodImplementation(TypeDefinitionHandle, EntityHandle, EntityHandle)

Defines an implementation for a method declaration within a type.

AddMethodImport(MethodDefinitionHandle, MethodImportAttributes, StringHandle, ModuleReferenceHandle)

Adds import information to a method definition.

AddMethodSemantics(EntityHandle, MethodSemanticsAttributes, MethodDefinitionHandle)

Associates a method (a getter, a setter, an adder, etc.) with a property or an event.

AddMethodSpecification(EntityHandle, BlobHandle)

Adds a method specification (an instantiation).

AddModule(Int32, StringHandle, GuidHandle, GuidHandle, GuidHandle)
AddModuleReference(StringHandle)
AddNestedType(TypeDefinitionHandle, TypeDefinitionHandle)

Defines a nesting relationship to specified type definitions.

AddParameter(ParameterAttributes, StringHandle, Int32)

Adds a parameter definition.

AddProperty(PropertyAttributes, StringHandle, BlobHandle)

Adds a property definition.

AddPropertyMap(TypeDefinitionHandle, PropertyDefinitionHandle)
AddStandaloneSignature(BlobHandle)
AddStateMachineMethod(MethodDefinitionHandle, MethodDefinitionHandle)

Adds state machine method debug information.

AddTypeDefinition(TypeAttributes, StringHandle, StringHandle, EntityHandle, FieldDefinitionHandle, MethodDefinitionHandle)

Adds a type definition.

AddTypeLayout(TypeDefinitionHandle, UInt16, UInt32)

Defines a type layout of a type definition.

AddTypeReference(EntityHandle, StringHandle, StringHandle)

Adds a type reference.

AddTypeSpecification(BlobHandle)
Equals(Object)

Determines whether the specified object is equal to the current object.

(Inherited from Object)
GetHashCode()

Serves as the default hash function.

(Inherited from Object)
GetOrAddBlob(BlobBuilder)

Adds the specified blob from an immutable byte array to the Blob heap, if it's not there already.

GetOrAddBlob(Byte[])

Adds the specified blob to the Blob heap, if it's not there already.

GetOrAddBlob(ImmutableArray<Byte>)

Adds the specified blob from a byte array to the Blob heap, if it's not there already.

GetOrAddBlobUTF16(String)

Encodes a string using UTF16 encoding to a blob and adds it to the Blob heap, if it's not there already.

GetOrAddBlobUTF8(String, Boolean)

Encodes a string using UTF8 encoding to a blob and adds it to the Blob heap, if it's not there already.

GetOrAddConstantBlob(Object)

Encodes a constant value to a blob and adds it to the Blob heap, if it's not there already. Uses UTF16 to encode string constants.

GetOrAddDocumentName(String)

Encodes a debug document name and adds it to the Blob heap, if it's not there already.

GetOrAddGuid(Guid)

Adds the specified Guid to the Guid heap, if it's not there already.

GetOrAddString(String)

Adds the specified string to the string heap, if it's not there already.

GetOrAddUserString(String)

Adds the specified string to the user string heap, if it's not there already.

GetRowCount(TableIndex)

Returns the current number of items in the specified table.

GetRowCounts()

Returns the current number of items in each table.

GetType()

Gets the Type of the current instance.

(Inherited from Object)
MemberwiseClone()

Creates a shallow copy of the current Object.

(Inherited from Object)
ReserveGuid()

Reserves space on the Guid heap for a GUID.

ReserveUserString(Int32)

Reserves space on the user string heap for a string of the specified length.

SetCapacity(HeapIndex, Int32)

Sets the capacity of the specified heap.

SetCapacity(TableIndex, Int32)

Sets the capacity of the specified table.

ToString()

Returns a string that represents the current object.

(Inherited from Object)

Applies to