.NET'te kalıcı dinamik derlemeler
Bu makale, bu API'nin başvuru belgelerine ek açıklamalar sağlar.
Uygulama AssemblyBuilder.Save , windows'a özgü yerel koda da büyük ölçüde bağlı olduğundan, API başlangıçta .NET'e (Core) aktarılamıyordu. .NET 9'da yeni olan sınıfı, kaydetmeyi PersistedAssemblyBuilder destekleyen tam olarak yönetilen Reflection.Emit
bir uygulama ekler. Bu uygulamanın önceden var olan, çalışma zamanına özgü Reflection.Emit
uygulamaya bağımlılığı yoktur. Yani, artık .NET'te çalıştırılabilir ve kalıcı olan iki farklı uygulama vardır. Kalıcı derlemeyi çalıştırmak için önce bir bellek akışına veya dosyaya kaydedin, sonra yeniden yükleyin.
öncesinde PersistedAssemblyBuilder
yalnızca oluşturulan bir derlemeyi çalıştırabilir ve kaydedemeyebilirsiniz. Derleme yalnızca bellek içi olduğundan hata ayıklaması zordu. Dinamik derlemeyi bir dosyaya kaydetmenin avantajları şunlardır:
- Oluşturulan derlemeyi ILVerify gibi araçlarla doğrulayabilir veya derleyip ILSpy gibi araçlarla el ile inceleyebilirsiniz.
- Kaydedilen derleme doğrudan yüklenebilir, yeniden derlemeye gerek yoktur ve bu da uygulama başlatma süresini azaltabilir.
Örnek PersistedAssemblyBuilder
oluşturmak için oluşturucuyu PersistedAssemblyBuilder(AssemblyName, Assembly, IEnumerable<CustomAttributeBuilder>) kullanın. coreAssembly
parametresi temel çalışma zamanı türlerini çözümlemek için kullanılır ve başvuru derlemesi sürümünü çözümlemek için kullanılabilir:
Yalnızca derleyicinin üzerinde çalıştığı çalışma zamanı sürümüyle (genellikle in-proc) aynı çalışma zamanı sürümünde yürütülecek bir derleme oluşturmak için kullanılırsa
Reflection.Emit
, çekirdek derleme basittypeof(object).Assembly
olabilir. Aşağıdaki örnekte bir derlemenin nasıl oluşturulup akışa kaydedilip geçerli çalışma zamanı derlemesiyle nasıl çalıştırılabileceği gösterilmektedir:public static void CreateSaveAndRunAssembly() { PersistedAssemblyBuilder ab = new PersistedAssemblyBuilder(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), new Type[] { 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, new object[] { 5, 10 })); }
Belirli bir TFM'yi hedefleyen bir derleme oluşturmak için kullanılırsa
Reflection.Emit
, kullanarakMetadataLoadContext
verilen TFM için başvuru derlemelerini açın ve içincoreAssembly
MetadataLoadContext.CoreAssembly özelliğinin değerini kullanın. Bu değer, oluşturucunun bir .NET çalışma zamanı sürümünde çalışmasına ve farklı bir .NET çalışma zamanı sürümünü hedeflemesine olanak tanır. Çekirdek türlerineMetadataLoadContext
başvururken örnek tarafından döndürülen türleri kullanmanız gerekir. Örneğin, yerinetypeof(int)
, türüMetadataLoadContext.CoreAssembly
ada göre bulunSystem.Int32
:public static void CreatePersistedAssemblyBuilderCoreAssemblyWithMetadataLoadContext(string refAssembliesPath) { PathAssemblyResolver resolver = new PathAssemblyResolver(Directory.GetFiles(refAssembliesPath, "*.dll")); using MetadataLoadContext context = new MetadataLoadContext(resolver); Assembly coreAssembly = context.CoreAssembly; PersistedAssemblyBuilder ab = new PersistedAssemblyBuilder(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 }
Yürütülebilir dosya için giriş noktası ayarlama
Yürütülebilir dosyanın giriş noktasını ayarlamak veya derleme dosyası için diğer seçenekleri ayarlamak için yöntemini çağırabilir public MetadataBuilder GenerateMetadata(out BlobBuilder ilStream, out BlobBuilder mappedFieldData)
ve doldurulan meta verileri kullanarak derlemeyi istenen seçeneklerle oluşturabilirsiniz, örneğin:
public static void SetEntryPoint()
{
PersistedAssemblyBuilder ab = new PersistedAssemblyBuilder(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);
PEHeaderBuilder peHeaderBuilder = new PEHeaderBuilder(imageCharacteristics: Characteristics.ExecutableImage);
ManagedPEBuilder peBuilder = new ManagedPEBuilder(
header: peHeaderBuilder,
metadataRootBuilder: new MetadataRootBuilder(metadataBuilder),
ilStream: ilStream,
mappedFieldData: fieldData,
entryPoint: MetadataTokens.MethodDefinitionHandle(entryPoint.MetadataToken));
BlobBuilder peBlob = new BlobBuilder();
peBuilder.Serialize(peBlob);
// in case saving to a file:
using var fileStream = new FileStream("MyAssembly.exe", FileMode.Create, FileAccess.Write);
peBlob.WriteContentTo(fileStream);
}
Sembolleri yayma ve PDB oluşturma
Sembol meta verileri, bir PersistedAssemblyBuilder
örnekte yöntemini çağırdığınızda out parametresine GenerateMetadata(BlobBuilder, BlobBuilder) doldurulurpdbBuilder
. Taşınabilir PDB ile derleme oluşturmak için:
- yöntemiyle ModuleBuilder.DefineDocument(String, Guid, Guid, Guid) örnekler oluşturunISymbolDocumentWriter. Yöntemin IL'sini yayarken, ilgili sembol bilgilerini de yayar.
- yöntemi tarafından GenerateMetadata(BlobBuilder, BlobBuilder) oluşturulan örneği kullanarak
pdbBuilder
bir PortablePdbBuilder örnek oluşturun. - olarak serileştirin
PortablePdbBuilder
Blobve bir PDB dosya akışınaBlob
yazın (yalnızca tek başına bir PDB oluşturuyorsanız). - Bir DebugDirectoryBuilder örnek oluşturun ve bir (tek başına PDB) veya DebugDirectoryBuilder.AddEmbeddedPortablePdbEntryekleyin DebugDirectoryBuilder.AddCodeViewEntry .
- Örneği oluştururken PEBuilder isteğe bağlı
debugDirectoryBuilder
bağımsız değişkeni ayarlayın.
Aşağıdaki örnekte, sembol bilgilerini yayma ve PDB dosyası oluşturma gösterilmektedir.
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;
}
Ayrıca, kaynak ekleme ve kaynak dizin oluşturma gelişmiş PDB bilgilerini eklemek için örnekten pdbBuilder
yöntemini çağırarak MetadataBuilder.AddCustomDebugInformation(EntityHandle, GuidHandle, BlobHandle) ekleyebilirsinizCustomDebugInformation.
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 ile kaynak ekleme
Gerektiği kadar kaynak eklemek için çağırabilirsiniz MetadataBuilder.AddManifestResource(ManifestResourceAttributes, StringHandle, EntityHandle, UInt32) . Akışlar bağımsız değişkenine geçirdiğiniz ManagedPEBuilder bir BlobBuilder birleştirilmelidir. Aşağıdaki örnek, kaynakların nasıl oluşturulacağını ve oluşturulan derlemeye nasıl eklendiğini gösterir.
public static void SetResource()
{
PersistedAssemblyBuilder ab = new PersistedAssemblyBuilder(new AssemblyName("MyAssembly"), typeof(object).Assembly);
ab.DefineDynamicModule("MyModule");
MetadataBuilder metadata = ab.GenerateMetadata(out BlobBuilder ilStream, out _);
using MemoryStream stream = new MemoryStream();
ResourceWriter myResourceWriter = new ResourceWriter(stream);
myResourceWriter.AddResource("AddResource 1", "First added resource");
myResourceWriter.AddResource("AddResource 2", "Second added resource");
myResourceWriter.AddResource("AddResource 3", "Third added resource");
myResourceWriter.Close();
BlobBuilder resourceBlob = new BlobBuilder();
resourceBlob.WriteBytes(stream.ToArray());
metadata.AddManifestResource(ManifestResourceAttributes.Public, metadata.GetOrAddString("MyResource"), default, (uint)resourceBlob.Count);
ManagedPEBuilder peBuilder = new ManagedPEBuilder(
header: new PEHeaderBuilder(imageCharacteristics: Characteristics.ExecutableImage | Characteristics.Dll),
metadataRootBuilder: new MetadataRootBuilder(metadata),
ilStream: ilStream,
managedResources: resourceBlob);
BlobBuilder blob = new BlobBuilder();
peBuilder.Serialize(blob);
using var fileStream = new FileStream("MyAssemblyWithResource.dll", FileMode.Create, FileAccess.Write);
blob.WriteContentTo(fileStream);
}
Not
Tüm üyeler için meta veri belirteçleri işlemde Save doldurulur. Varsayılan değerlere sahip olacak veya özel durumlar oluşturacaklarından, kaydetmeden önce oluşturulan türün belirteçlerini ve üyelerini kullanmayın. Başvuruda kullanılan türler için belirteçleri kullanmak güvenlidir, oluşturulmaz.
Derleme yayma açısından önemli olmayan bazı API'ler uygulanmaz; örneğin uygulanmaz GetCustomAttributes()
. Çalışma zamanı uygulamasıyla, türü oluşturduktan sonra bu API'leri kullanabildiniz. Kalıcı olan AssemblyBuilder
için veya NotImplementedException
oluştururlarNotSupportedException
. Bu API'leri gerektiren bir senaryonuz varsa dotnet/runtime deposunda bir sorun oluşturun.
Derleme dosyaları oluşturmanın alternatif bir yolu için bkz MetadataBuilder. .