使用Mono.Cecil

时间:2015-04-28 07:40:22

标签: mono.cecil

我有一个TestClass类的源代码存储为字符串,我需要用另一个类创建一个dll,其中包含TestClass的单​​个方法的代码。这是TestClass,存储为字符串:

namespace Test
{
    public static class TestClass
    {
        public static void TestMethod()
        {
            System.Console.WriteLine("something");
        }
    }
}

我使用CSharpCodeProvider编译此代码,然后使用Mono.Cecil创建新程序集,创建新类型,新方法并复制(实际克隆)到编译方法的指令。什么不起作用是在将程序集写入磁盘的阶段,抛出异常:

未处理的类型' System.ArgumentException'发生在Mono.Cecil.dll中 附加信息:成员' System.Void System.Console :: WriteLine(System.String)'在另一个模块中声明,需要导入。

Console.WriteLine方法按照所述here in docs

导入

以下是完整代码:

class Program
{
    static void Main()
    {
        var assemblyDefinition = AssemblyDefinition.CreateAssembly(
            new AssemblyNameDefinition("Test", new Version(1, 0)), "module", ModuleKind.Console);
        var type = new TypeDefinition("Test", "TestClass", TypeAttributes.Public);
        var methodDefinition = new MethodDefinition(
            "TestMethod",
            MethodAttributes.Static | MethodAttributes.Public,
            assemblyDefinition.MainModule.Import(typeof(void)));
        type.Methods.Add(methodDefinition);
        assemblyDefinition.MainModule.Types.Add(typeDefinition);
        AddMethodBody(methodDefinition);
        assemblyDefinition.Write("generated.dll");
    }

    private static void AddMethodBody(MethodDefinition methodDefinition)
    {
        var parameters = new CompilerParameters();
        parameters.ReferencedAssemblies.AddRange(AppDomain.CurrentDomain.GetAssemblies().Select(x => x.Location).ToArray());
        var result = _compiler.CompileAssemblyFromSource(parameters, TestClassSource);

        var compiledAssembly = AssemblyDefinition.ReadAssemblly(result.CompiledAssembly.Location);
        var type = compiledAssembly.MainModule.Types.First(x => x.FullName == "Test.TestClass");
        var method = type.Methods.First(x => x.Name == "TestMethod");

        var instructions = method.Body.Instructions;

        var ctorInfo = typeof(Instruction).GetConstructor(
            BindingFlags.NonPublic | BindingFlags.Instance, null,
            new[] { typeof(OpCode, typeof(object) }, null);

        foreach(var inst in instructions)
        {
            var newInstruction = (Instruction)ctorInfo.Invoke(new[] {inst.OpCode, inst.Operand});
            methodDefinition.Body.Instructions.Add(newInstruction);
        }

        var il = methodDefinition.Body.GetILProcessor();
        var ldstr = il.Create(OpCodes.Ldstr, methodDefinition.Name);
        var class = il.Create(OpCodes.Call,
            methodDefinition.Module.Import(typeof(Console).GetMethod("WriteLine", new[] { typeof(string) })));

        il.InsertBefore(methodDefinition.Body.Instructions[0], ldstr);
        il.InsertAfter(methodDefinition.Body.Instructions[0], call);
    }

    private static string TestClassSource = 
@"
namespace Test
{
    public static class TestClass
    {
        public static void TestMethod()
        {
            System.Console.WriteLine(""abc"");
        }
    }
}"

}

任何帮助都会得到应用

0 个答案:

没有答案