我将如何为在运行时通过反射生成的类提取 IL 代码,以便将其保存到磁盘?如果可能的话。我无法控制生成这些类的代码。
最后,我想将这个 IL 代码从磁盘加载到另一个程序集中。
我知道我可以序列化/反序列化类,但我希望使用纯 IL 代码。我不关心安全问题。
运行 Mono 2.10.1
我将如何为在运行时通过反射生成的类提取 IL 代码,以便将其保存到磁盘?如果可能的话。我无法控制生成这些类的代码。
最后,我想将这个 IL 代码从磁盘加载到另一个程序集中。
我知道我可以序列化/反序列化类,但我希望使用纯 IL 代码。我不关心安全问题。
运行 Mono 2.10.1
或者更好的是,使用 Mono.Cecil。
它将允许您获取单独的指令,甚至可以操作和反汇编它们(使用单反编译器添加)。
请注意,反编译器正在开发中(上次我检查它不完全支持 lambda 表达式和 Visual Basic 异常块),但只要不达到这些边界条件,您就可以在 C# 中轻松获得相当反编译的输出. 此外,此后工作也取得了进展。
Mono Cecil 通常也让您将 IL 写入一个新程序集,如果您喜欢玩前沿技术,您可以随后将其加载到您的 appdomain 中。
更新我来尝试这个。不幸的是,我想我找到了你遇到的问题。事实证明,似乎没有办法获取生成类型的 IL 字节,除非程序集碰巧被写出您可以从中加载它的某个地方。
The invoked member is not supported in a dynamic module.
我假设您可以通过反射获得这些位(因为这些类支持所需的方法),但是相关的方法只会在调用时引发异常。你可以用下面的代码试试这个,但简而言之,我想这意味着它不会发生,除非你想用Marshal::GetFunctionPointerForDelegate()
. 您必须将指令二进制转储并手动将它们反汇编为 IL 操作码。有龙。
代码片段:
using System;
using System.Linq;
using Mono.Cecil;
using Mono.Cecil.Cil;
using System.Reflection.Emit;
using System.Reflection;
namespace REFLECT
{
class Program
{
private static Type EmitType()
{
var dyn = AppDomain.CurrentDomain.DefineDynamicAssembly(new AssemblyName("Emitted"), AssemblyBuilderAccess.RunAndSave);
var mod = dyn.DefineDynamicModule("Emitted", "Emitted.dll");
var typ = mod.DefineType("EmittedNS.EmittedType", System.Reflection.TypeAttributes.Public);
var mth = typ.DefineMethod("SuperSecretEncryption", System.Reflection.MethodAttributes.Public | System.Reflection.MethodAttributes.Static, typeof(String), new [] {typeof(String)});
var il = mth.GetILGenerator();
il.EmitWriteLine("Emit was here");
il.Emit(System.Reflection.Emit.OpCodes.Ldarg_0);
il.Emit(System.Reflection.Emit.OpCodes.Ret);
var result = typ.CreateType();
dyn.Save("Emitted.dll");
return result;
}
private static Type TestEmit()
{
var result = EmitType();
var instance = Activator.CreateInstance(result);
var encrypted = instance.GetType().GetMethod("SuperSecretEncryption").Invoke(null, new [] { "Hello world" });
Console.WriteLine(encrypted); // This works happily, print "Emit was here" first
return result;
}
public static void Main (string[] args)
{
Type emitted = TestEmit();
// CRASH HERE: even if the assembly was actually for SaveAndRun _and_ it
// has actually been saved, there seems to be no way to get at the image
// directly:
var ass = AssemblyFactory.GetAssembly(emitted.Assembly.GetFiles(false)[0]);
// the rest was intended as mockup on how to isolate the interesting bits
// but I didn't get much chance to test that :)
var types = ass.Modules.Cast<ModuleDefinition>().SelectMany(m => m.Types.Cast<TypeDefinition>()).ToList();
var typ = types.FirstOrDefault(t => t.Name == emitted.Name);
var operands = typ.Methods.Cast<MethodDefinition>()
.SelectMany(m => m.Body.Instructions.Cast<Instruction>())
.Select(i => i.Operand);
var requiredTypes = operands.OfType<TypeReference>()
.Concat(operands.OfType<MethodReference>().Select(mr => mr.DeclaringType))
.Select(tr => tr.Resolve()).OfType<TypeDefinition>()
.Distinct();
var requiredAssemblies = requiredTypes
.Select(tr => tr.Module).OfType<ModuleDefinition>()
.Select(md => md.Assembly.Name as AssemblyNameReference);
foreach (var t in types.Except(requiredTypes))
ass.MainModule.Types.Remove(t);
foreach (var unused in ass.MainModule
.AssemblyReferences.Cast<AssemblyNameReference>().ToList()
.Except(requiredAssemblies))
ass.MainModule.AssemblyReferences.Remove(unused);
AssemblyFactory.SaveAssembly(ass, "/tmp/TestCecil.dll");
}
}
}
如果您想要的只是User
班级的 IL,那么您已经拥有了。它在您编译到的 dll 中。
从您的其他程序集中,您可以动态加载带有User
类的 dll 并通过反射使用它。
更新:
如果您拥有的是使用 创建的动态类Reflection.Emit
,那么AssemblyBuilder
您可以使用它来将其保存到磁盘。
如果您的动态类型是使用 创建的Mono.Cecil
,那么您可以使用(在 Mono.Cecil 0.9 中)AssemblyDefinition
将其保存到磁盘。myAssemblyDefinition.Write("MyAssembly.dll")