Using .NET 4.7.2, I dynamically generate a .dll
at runtime using
internal class Program
{
private static void Main(string[] args)
{
AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(new AssemblyName("MyAssembly"), AssemblyBuilderAccess.RunAndSave);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("ModuleName");
TypeBuilder typeBuilder = moduleBuilder.DefineType("MyNamespace.TypeName", TypeAttributes.Public);
typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);
// Build the method 'public int ReturnTheAnswer() => 42;'.
MethodBuilder newMethod = typeBuilder.DefineMethod("ReturnTheAnswer", MethodAttributes.Public, typeof(int), new Type[0]);
ILGenerator ilGen = newMethod.GetILGenerator();
ilGen.Emit(OpCodes.Ldc_I4_S, 42);
ilGen.Emit(OpCodes.Ret);
Type newType = typeBuilder.CreateType();
assemblyBuilder.Save("MyAssembly.dll"); // Save the assembly in the programs work directory ('bin\Debug').
dynamic o = Activator.CreateInstance(newType); // Create an instance of the dynamically created type.
int r = (int) o.ReturnTheAnswer();
Debug.Assert(r == 42); // If this doesn't fail, the type has been built correctly, is in fact in the .dll and can be used perfectly fine.
}
}
and I can use the type in the .dll
perfectly fine, however, when browsing the generated .dll
with DotPeek and IL Spy, they both do not show any namespaces or types (albeit they should show the type TypeName
in namespace MyNamespace
).
Why is the assembly seemingly empty when using two distinct decompilers, but using its types from code works perfectly fine?
(above example code is mvce, you should be able to reproduce the exact behavior I encounter)
Your Type doesn't get saved because it is declared in a transient dynamic module.
AssemblyBuilder
's Save
method only saves non-transient dynamic modules.
See the remarks.
This method saves all non-transient dynamic modules defined in this dynamic assembly.
Transient dynamic modules are not saved.
The assembly file name can be the same as the name of one of the modules. If so, the assembly manifest is stored within that module. assemblyFileName can be different from the names of all of the modules contained within the assembly. If so, the assembly file contains only the assembly manifest.
To persist your Type, you must declare it in a non-transient persistable dynamic module, using one of the overloads of DefineDynamicModule
that accepts a fileName
argument, like this:
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("ModuleName", "MyAssembly.dll");
Your full code will look like here below.
Use the same filename to keep your Type and the manifest together in 1 single assembly file.
AssemblyBuilder assemblyBuilder = Thread.GetDomain().DefineDynamicAssembly(new
AssemblyName("MyAssembly"), AssemblyBuilderAccess.RunAndSave);
ModuleBuilder moduleBuilder = assemblyBuilder.DefineDynamicModule("ModuleName", "MyAssembly.dll");
TypeBuilder typeBuilder = moduleBuilder.DefineType("MyNamespace.TypeName", TypeAttributes.Public);
typeBuilder.DefineDefaultConstructor(MethodAttributes.Public);
// Build the method 'public int ReturnTheAnswer() => 42;'.
MethodBuilder newMethod = typeBuilder.DefineMethod("ReturnTheAnswer",
MethodAttributes.Public, typeof(int), new Type[0]);
ILGenerator ilGen = newMethod.GetILGenerator();
ilGen.Emit(OpCodes.Ldc_I4_S, 42);
ilGen.Emit(OpCodes.Ret);
Type newType = typeBuilder.CreateType();
assemblyBuilder.Save("MyAssembly.dll"); // Save the assembly in the programs work directory ('bin\Debug').
dynamic o = Activator.CreateInstance(newType); // Create an instance of the dynamically created type.
int r = (int) o.ReturnTheAnswer();
Debug.Assert(r == 42); // If this doesn't fail, the type has been built correctly, is in fact in the .dll and can be used perfectly fine.