I want to somehow add a method from an on disk assembly to an assembly I am generating, I am creating the assembly via the System.Reflection.Emit
and saving it to a file using the Lokad.ILPack
nuget package and loading it with AssemblyLoadContext
since this is .NET 7 Core, the on disk assembly is also generated.
I would like to avoid using externals libraries, but I understand that it may not be plausible using the standard library, and I can't use something like Pinvoke because the assembly might not even exist when the method call is needed, also if the answer requires copying the type containing the method then that's fine.
Example of how I am creating the assembly:
public static void CreateMethod(string destDllPath)
{
AssemblyName asmName = new AssemblyName("CAssembly");
AssemblyBuilder asmBuilder = AssemblyBuilder.DefineDynamicAssembly(asmName, AssemblyBuilderAccess.RunAndCollect);
//Its RunAndCollect because if the AssemblyLoadContext fails to unload it will do so automatically
ModuleBuilder modBuilder = asmBuilder.DefineDynamicModule("CModule");
TypeBuilder typeBuilder = modBuilder.DefineType("CType", TypeAttributes.Public | TypeAttributes.Abstract | TypeAttributes.Sealed);
//Abstract | Sealed means that the class is static
MethodBuilder methodBuilder = typeBuilder.DefineMethod("CMethod",
MethodAttributes.Static | MethodAttributes.Public,
CallingConventions.Standard,
typeof(bool),
new[] { typeof(bool) });
ILGenerator ILG = methodBuilder.GetILGenerator();
ILG.Emit(OpCodes.Ldarg_0);
//Push the first argument onto the evaluation stack
ILG.Emit(OpCodes.Ret);
//Return the first element from the evaluation stack
_ = typeBuilder.CreateType();
AssemblyGenerator asmGenerator = new AssemblyGenerator();
asmGenerator.GenerateAssembly(asmBuilder, destDllPath);
}
Then using the method generated above
public static void CopyMethod(AssemblyBuilder toAssembly, string fromDllPath)
{
string typeName = "CType";
string methodName = "CMethod";
Assembly fromAssembly = Assembly.LoadFile(fromDllPath);
//note that the assembly at fromDllPath is created via MethodBuilder ILGenerator and Lokad.ILPack
Type type = fromAssembly.GetType(typeName)!;
MethodInfo method = type.GetMethod(methodName)!;
//to test that the generated assembly is valid
//bool testTesult = (bool)method.Invoke(null, new object[] { true });
//somehow add method to toAssembly?
}
This is where I run into the problem of not knowing how to add the method to the assembly
I have spend a solid few days trying the find a solution to this problem, but there doesn't seem to be al ot of information on dynamic assembly creation in .NET Core 5 through 7.
After looking at different reflection libraries (Mono.Reflection, Mono.Cecil, Lokad.ILPack and a lot of code samples) have I found out that for my project I will be simply converting each opcode and operand manually. This only works because my project is guaranteed to generate simple assemblies that don't use any module defined tokens (or switch).
For all 0 people following or looking at this answer this may not work for you so you're going to have to define these operations yourself, if the method and types you need is defined in the same module as the target method then you can get it by using the module.resolve methods to get the appropriate tokens.
I found the MetadataBuilder class in System.Reflection.Metadata.Ecma335 (supports all version of .Net) and it seems to the best way to generate assemblies on an CIL level, but I don't have time to use it over the Emit API so here is my solution.
Solution:
This solution seems to generate the same IL as show in ildasm, but it's not ideal. Also sorry for my poor writing skills and rambling