PersistedAssemblyBuilder 类
定义
重要
一些信息与预发行产品相关,相应产品在发行之前可能会进行重大修改。 对于此处提供的信息,Microsoft 不作任何明示或暗示的担保。
提供一个 AssemblyBuilder 实现,该实现可将程序集保存到磁盘或流中。
public ref class PersistedAssemblyBuilder sealed : System::Reflection::Emit::AssemblyBuilder
public sealed class PersistedAssemblyBuilder : System.Reflection.Emit.AssemblyBuilder
type PersistedAssemblyBuilder = class
inherit AssemblyBuilder
Public NotInheritable Class PersistedAssemblyBuilder
Inherits AssemblyBuilder
- 继承
注解
AssemblyBuilder.Save API 最初未移植到 .NET Core,因为实现在很大程度上依赖 Windows 特定的本机代码,而后者同样未移植。 .NET 9 添加了 PersistedAssemblyBuilder 类,该类提供完全托管 Reflection.Emit 的实现,支持保存。 此实现不依赖于预先存在的特定于运行时的 Reflection.Emit 实现。 也就是说,现在 .NET 中有两个不同的实现: 可运行 和 持久化。 若要运行持久化程序集,请先将其保存到内存流或文件中,然后将其加载回。
在 PersistedAssemblyBuilder之前,只能运行生成的程序集,不能保存它。 由于程序集仅在内存中,因此很难调试。 将动态程序集保存到文件的优点包括:
- 可以使用 ILVerify 等工具验证生成的程序集,或者反编译,并使用 ILSpy 等工具手动检查它。
- 可以直接加载保存的程序集,而无需再次编译,这可以减少应用程序启动时间。
若要创建 PersistedAssemblyBuilder 实例,请使用 PersistedAssemblyBuilder(AssemblyName, Assembly, IEnumerable<CustomAttributeBuilder>) 构造函数。
coreAssembly 参数用于解析基运行时类型,可用于解析引用程序集版本控制:
如果使用
Reflection.Emit生成的程序集仅在与编译器运行的运行时版本相同的运行时(通常为进程内)上执行,则核心程序集可以简单地为typeof(object).Assembly。 以下示例演示如何创建程序集并将其保存到流,并使用当前运行时程序集运行该程序集:public static void CreateSaveAndRunAssembly() { PersistedAssemblyBuilder ab = new(new AssemblyName("MyAssembly"), typeof(object).Assembly); ModuleBuilder mob = ab.DefineDynamicModule("MyModule"); TypeBuilder tb = mob.DefineType( "MyType", TypeAttributes.Public | TypeAttributes.Class); MethodBuilder meb = tb.DefineMethod( "SumMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int), typeof(int)]); ILGenerator il = meb.GetILGenerator(); il.Emit(OpCodes.Ldarg_0); il.Emit(OpCodes.Ldarg_1); il.Emit(OpCodes.Add); il.Emit(OpCodes.Ret); tb.CreateType(); using var stream = new MemoryStream(); ab.Save(stream); // Or pass filename to save into a file. stream.Seek(0, SeekOrigin.Begin); Assembly assembly = AssemblyLoadContext.Default.LoadFromStream(stream); MethodInfo method = assembly.GetType("MyType").GetMethod("SumMethod"); Console.WriteLine(method.Invoke(null, [5, 10])); }如果使用
Reflection.Emit生成面向特定 TFM 的程序集,请使用MetadataLoadContext为给定的 TFM 打开引用程序集,然后使用 MetadataLoadContext.CoreAssembly 属性的值用于coreAssembly。 此值允许生成器在一个 .NET 运行时版本上运行,并面向不同的 .NET 运行时版本。 引用核心类型时,应使用MetadataLoadContext实例返回的类型。 例如,在typeof(int)中按名称查找System.Int32类型,而不是MetadataLoadContext.CoreAssembly:public static void CreatePersistedAssemblyBuilderCoreAssemblyWithMetadataLoadContext(string refAssembliesPath) { PathAssemblyResolver resolver = new(Directory.GetFiles(refAssembliesPath, "*.dll")); using MetadataLoadContext context = new(resolver); Assembly coreAssembly = context.CoreAssembly; PersistedAssemblyBuilder ab = new(new AssemblyName("MyDynamicAssembly"), coreAssembly); TypeBuilder typeBuilder = ab.DefineDynamicModule("MyModule").DefineType("Test", TypeAttributes.Public); MethodBuilder methodBuilder = typeBuilder.DefineMethod("Method", MethodAttributes.Public, coreAssembly.GetType(typeof(int).FullName), Type.EmptyTypes); // .. add members and save the assembly }
设置可执行文件的入口点
若要设置可执行文件的入口点或设置程序集文件的其他选项,可以调用 public MetadataBuilder GenerateMetadata(out BlobBuilder ilStream, out BlobBuilder mappedFieldData) 方法,并使用填充的元数据生成具有所需选项的程序集,例如:
public static void SetEntryPoint()
{
PersistedAssemblyBuilder ab = new(new AssemblyName("MyAssembly"), typeof(object).Assembly);
TypeBuilder tb = ab.DefineDynamicModule("MyModule").DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class);
// ...
MethodBuilder entryPoint = tb.DefineMethod("Main", MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Static);
ILGenerator il2 = entryPoint.GetILGenerator();
// ...
il2.Emit(OpCodes.Ret);
tb.CreateType();
MetadataBuilder metadataBuilder = ab.GenerateMetadata(out BlobBuilder ilStream, out BlobBuilder fieldData);
ManagedPEBuilder peBuilder = new(
header: PEHeaderBuilder.CreateExecutableHeader(),
metadataRootBuilder: new MetadataRootBuilder(metadataBuilder),
ilStream: ilStream,
mappedFieldData: fieldData,
entryPoint: MetadataTokens.MethodDefinitionHandle(entryPoint.MetadataToken));
BlobBuilder peBlob = new();
peBuilder.Serialize(peBlob);
// Create the executable:
using FileStream fileStream = new("MyAssembly.exe", FileMode.Create, FileAccess.Write);
peBlob.WriteContentTo(fileStream);
}
输出符号并生成 PDB
在对 pdbBuilder 实例调用 GenerateMetadata(BlobBuilder, BlobBuilder) 方法时,符号元数据将填充到 PersistedAssemblyBuilder out 参数中。 要创建带有可移植 PDB 的程序集:
- 使用 ISymbolDocumentWriter 方法创建 ModuleBuilder.DefineDocument(String, Guid, Guid, Guid) 实例。 在发出方法的 IL 的同时,也发出相应的符号信息。
- 使用由 PortablePdbBuilder 方法生成的
pdbBuilder实例来创建 GenerateMetadata(BlobBuilder, BlobBuilder) 实例。 - 将
PortablePdbBuilder序列化为 Blob,并将Blob写入 PDB 文件流(仅当生成独立 PDB 时)。 - 创建 DebugDirectoryBuilder 实例并添加 DebugDirectoryBuilder.AddCodeViewEntry(独立 PDB)或 DebugDirectoryBuilder.AddEmbeddedPortablePdbEntry。
- 创建
debugDirectoryBuilder实例时设置可选的 PEBuilder 参数。
以下示例演示如何发出符号信息并生成 PDB 文件。
static void GenerateAssemblyWithPdb()
{
PersistedAssemblyBuilder ab = new PersistedAssemblyBuilder(new AssemblyName("MyAssembly"), typeof(object).Assembly);
ModuleBuilder mb = ab.DefineDynamicModule("MyModule");
TypeBuilder tb = mb.DefineType("MyType", TypeAttributes.Public | TypeAttributes.Class);
MethodBuilder mb1 = tb.DefineMethod("SumMethod", MethodAttributes.Public | MethodAttributes.Static, typeof(int), [typeof(int), typeof(int)]);
ISymbolDocumentWriter srcDoc = mb.DefineDocument("MySourceFile.cs", SymLanguageType.CSharp);
ILGenerator il = mb1.GetILGenerator();
LocalBuilder local = il.DeclareLocal(typeof(int));
local.SetLocalSymInfo("myLocal");
il.MarkSequencePoint(srcDoc, 7, 0, 7, 11);
...
il.Emit(OpCodes.Ret);
MethodBuilder entryPoint = tb.DefineMethod("Main", MethodAttributes.HideBySig | MethodAttributes.Public | MethodAttributes.Static);
ILGenerator il2 = entryPoint.GetILGenerator();
il2.BeginScope();
...
il2.EndScope();
...
tb.CreateType();
MetadataBuilder metadataBuilder = ab.GenerateMetadata(out BlobBuilder ilStream, out _, out MetadataBuilder pdbBuilder);
MethodDefinitionHandle entryPointHandle = MetadataTokens.MethodDefinitionHandle(entryPoint.MetadataToken);
DebugDirectoryBuilder debugDirectoryBuilder = GeneratePdb(pdbBuilder, metadataBuilder.GetRowCounts(), entryPointHandle);
ManagedPEBuilder peBuilder = new ManagedPEBuilder(
header: new PEHeaderBuilder(imageCharacteristics: Characteristics.ExecutableImage, subsystem: Subsystem.WindowsCui),
metadataRootBuilder: new MetadataRootBuilder(metadataBuilder),
ilStream: ilStream,
debugDirectoryBuilder: debugDirectoryBuilder,
entryPoint: entryPointHandle);
BlobBuilder peBlob = new BlobBuilder();
peBuilder.Serialize(peBlob);
using var fileStream = new FileStream("MyAssembly.exe", FileMode.Create, FileAccess.Write);
peBlob.WriteContentTo(fileStream);
}
static DebugDirectoryBuilder GeneratePdb(MetadataBuilder pdbBuilder, ImmutableArray<int> rowCounts, MethodDefinitionHandle entryPointHandle)
{
BlobBuilder portablePdbBlob = new BlobBuilder();
PortablePdbBuilder portablePdbBuilder = new PortablePdbBuilder(pdbBuilder, rowCounts, entryPointHandle);
BlobContentId pdbContentId = portablePdbBuilder.Serialize(portablePdbBlob);
// In case saving PDB to a file
using FileStream fileStream = new FileStream("MyAssemblyEmbeddedSource.pdb", FileMode.Create, FileAccess.Write);
portablePdbBlob.WriteContentTo(fileStream);
DebugDirectoryBuilder debugDirectoryBuilder = new DebugDirectoryBuilder();
debugDirectoryBuilder.AddCodeViewEntry("MyAssemblyEmbeddedSource.pdb", pdbContentId, portablePdbBuilder.FormatVersion);
// In case embedded in PE:
// debugDirectoryBuilder.AddEmbeddedPortablePdbEntry(portablePdbBlob, portablePdbBuilder.FormatVersion);
return debugDirectoryBuilder;
}
此外,您可以通过从 CustomDebugInformation 实例调用 MetadataBuilder.AddCustomDebugInformation(EntityHandle, GuidHandle, BlobHandle) 方法来添加 pdbBuilder,以加入源嵌入和源索引的高级 PDB 信息。
private static void EmbedSource(MetadataBuilder pdbBuilder)
{
byte[] sourceBytes = File.ReadAllBytes("MySourceFile2.cs");
BlobBuilder sourceBlob = new BlobBuilder();
sourceBlob.WriteBytes(sourceBytes);
pdbBuilder.AddCustomDebugInformation(MetadataTokens.DocumentHandle(1),
pdbBuilder.GetOrAddGuid(new Guid("0E8A571B-6926-466E-B4AD-8AB04611F5FE")), pdbBuilder.GetOrAddBlob(sourceBlob));
}
使用 PersistedAssemblyBuilder 添加资源
可以调用 MetadataBuilder.AddManifestResource(ManifestResourceAttributes, StringHandle, EntityHandle, UInt32) 来根据需要添加任意数量的资源。 流必须串连成一个 BlobBuilder,然后传递给 ManagedPEBuilder 参数。 以下示例演示如何创建资源并将其附加到创建的程序集。
public static void SetResource()
{
PersistedAssemblyBuilder ab = new(new AssemblyName("MyAssembly"), typeof(object).Assembly);
ab.DefineDynamicModule("MyModule");
MetadataBuilder metadata = ab.GenerateMetadata(out BlobBuilder ilStream, out _);
using MemoryStream stream = new();
ResourceWriter myResourceWriter = new(stream);
myResourceWriter.AddResource("AddResource 1", "First added resource");
myResourceWriter.AddResource("AddResource 2", "Second added resource");
myResourceWriter.AddResource("AddResource 3", "Third added resource");
myResourceWriter.Close();
byte[] data = stream.ToArray();
BlobBuilder resourceBlob = new();
resourceBlob.WriteInt32(data.Length);
resourceBlob.WriteBytes(data);
metadata.AddManifestResource(
ManifestResourceAttributes.Public,
metadata.GetOrAddString("MyResource.resources"),
implementation: default,
offset: 0);
ManagedPEBuilder peBuilder = new(
header: PEHeaderBuilder.CreateLibraryHeader(),
metadataRootBuilder: new MetadataRootBuilder(metadata),
ilStream: ilStream,
managedResources: resourceBlob);
BlobBuilder blob = new();
peBuilder.Serialize(blob);
// Create the assembly:
using FileStream fileStream = new("MyAssemblyWithResource.dll", FileMode.Create, FileAccess.Write);
blob.WriteContentTo(fileStream);
}
以下示例演示如何从创建的程序集读取资源。
public static void ReadResource()
{
Assembly readAssembly = Assembly.LoadFile(Path.Combine(
Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location),
"MyAssemblyWithResource.dll"));
// Use ResourceManager.GetString() to read the resources.
ResourceManager rm = new("MyResource", readAssembly);
Console.WriteLine("Using ResourceManager.GetString():");
Console.WriteLine($"{rm.GetString("AddResource 1", CultureInfo.InvariantCulture)}");
Console.WriteLine($"{rm.GetString("AddResource 2", CultureInfo.InvariantCulture)}");
Console.WriteLine($"{rm.GetString("AddResource 3", CultureInfo.InvariantCulture)}");
// Use ResourceSet to enumerate the resources.
Console.WriteLine();
Console.WriteLine("Using ResourceSet:");
ResourceSet resourceSet = rm.GetResourceSet(CultureInfo.InvariantCulture, createIfNotExists: true, tryParents: false);
foreach (DictionaryEntry entry in resourceSet)
{
Console.WriteLine($"Key: {entry.Key}, Value: {entry.Value}");
}
// Use ResourceReader to enumerate the resources.
Console.WriteLine();
Console.WriteLine("Using ResourceReader:");
using Stream stream = readAssembly.GetManifestResourceStream("MyResource.resources")!;
using ResourceReader reader = new(stream);
foreach (DictionaryEntry entry in reader)
{
Console.WriteLine($"Key: {entry.Key}, Value: {entry.Value}");
}
}
注释
所有成员的元数据标记都在 Save 操作中填充。 保存前不要使用生成的类型的标记及其成员,因为它们将具有默认值或引发异常。 对引用而非生成的类型使用令牌是安全的。
未实现某些对生成程序集不重要的 API;例如,GetCustomAttributes() 就未实现。 使用运行时实现,可以在创建类型后使用这些 API。 对于持久化 AssemblyBuilder,它们会抛出 NotSupportedException 或 NotImplementedException。 如果您有需要这些 API 的情景,请在 dotnet/runtime 仓库中提交问题。
有关生成汇编文件的替代方法,请参阅 MetadataBuilder。
Warning
PersistedAssemblyBuilder API 需要具有受信任输入的完全受信任的环境,类似于编译器等其他技术。 除了生成的 IL AssemblyBuilder 的基本验证之外,没有可以包含的限制。 这包括成员名称、计数和关联的元数据(例如自定义属性)的验证。
构造函数
| 名称 | 说明 |
|---|---|
| PersistedAssemblyBuilder(AssemblyName, Assembly, IEnumerable<CustomAttributeBuilder>) |
PersistedAssemblyBuilder创建可保存到文件或流的实例。 |
属性
| 名称 | 说明 |
|---|---|
| CodeBase |
已过时.
获取程序集的位置,如最初指定的(如在对象 AssemblyName 中)。 (继承自 AssemblyBuilder) |
| CustomAttributes |
获取包含此程序集的自定义属性的集合。 (继承自 Assembly) |
| EntryPoint |
返回此程序集的入口点。 (继承自 AssemblyBuilder) |
| EscapedCodeBase |
已过时.
已过时.
获取表示代码库的 URI(包括转义字符)。 (继承自 Assembly) |
| ExportedTypes |
获取此程序集中定义的公共类型的集合,这些类型在程序集外部可见。 (继承自 Assembly) |
| FullName |
获取当前动态程序集的显示名称。 |
| HostContext |
获取正在在其中创建动态程序集的主机上下文。 (继承自 AssemblyBuilder) |
| IsCollectible |
获取一个值,该值指示此动态程序集是否保存在可 AssemblyLoadContext回收程序集中。 (继承自 AssemblyBuilder) |
| IsDynamic |
获取一个值,该值指示当前程序集是动态程序集。 (继承自 AssemblyBuilder) |
| IsFullyTrusted |
获取一个值,该值指示当前程序集是否使用完全信任加载。 (继承自 Assembly) |
| Location |
获取加载的文件的位置(以代码库格式获取,如果该文件不是卷影复制的)。 (继承自 AssemblyBuilder) |
| ManifestModule |
获取当前包含程序集清单的模块 PersistedAssemblyBuilder 。 |
| ReflectionOnly |
获取一个值,该值指示动态程序集是否位于仅反射上下文中。 (继承自 AssemblyBuilder) |
方法
活动
| 名称 | 说明 |
|---|---|
| ModuleResolve |
当公共语言运行时类加载程序无法通过正常方式解析对程序集内部模块的引用时发生。 (继承自 Assembly) |
扩展方法
| 名称 | 说明 |
|---|---|
| GetCustomAttribute(Assembly, Type) |
检索应用于指定程序集的指定类型的自定义属性。 |
| GetCustomAttribute<T>(Assembly) |
检索应用于指定程序集的指定类型的自定义属性。 |
| GetCustomAttributes(Assembly, Type) |
检索应用于指定程序集的指定类型的自定义属性集合。 |
| GetCustomAttributes(Assembly) |
检索应用于指定程序集的自定义属性的集合。 |
| GetCustomAttributes<T>(Assembly) |
检索应用于指定程序集的指定类型的自定义属性集合。 |
| GetExportedTypes(Assembly) |
提供一个 AssemblyBuilder 实现,该实现可将程序集保存到磁盘或流中。 |
| GetModules(Assembly) |
提供一个 AssemblyBuilder 实现,该实现可将程序集保存到磁盘或流中。 |
| GetTypes(Assembly) |
提供一个 AssemblyBuilder 实现,该实现可将程序集保存到磁盘或流中。 |
| IsDefined(Assembly, Type) |
指示指定类型的自定义属性是否应用于指定的程序集。 |
| TryGetRawMetadata(Assembly, Byte*, Int32) |
检索程序集的元数据部分,以用于 MetadataReader. |