【问题标题】:Why Mono.Cecil argues for method import while I have done it already?为什么 Mono.Cecil 主张方法导入,而我已经这样做了?
【发布时间】:2015-09-19 08:34:18
【问题描述】:

这是我的代码:

    private void ModifyMethods()
    {
        SyntaxTree syntaxTree = CSharpSyntaxTree.ParseText(@"
using System;

namespace ToIL
{
    public class Class1
    {
        public void Write()
        {
            Console.WriteLine(""Hello"");
        }
    }
}");


        string assemblyName = System.IO.Path.GetRandomFileName();
        MetadataReference[] references = new MetadataReference[]
        {
            MetadataReference.CreateFromFile(typeof(object).Assembly.Location),
            MetadataReference.CreateFromFile(typeof(Enumerable).Assembly.Location)
        };

        CSharpCompilation compilation = CSharpCompilation.Create( assemblyName, syntaxTrees: new[] { syntaxTree }, references: references,
            options: new CSharpCompilationOptions(OutputKind.DynamicallyLinkedLibrary));


        Mono.Cecil.AssemblyDefinition asm = null;
        using (var ms = new MemoryStream())
        {
            var emitResult = compilation.Emit(ms);
            if (emitResult.Success)
            {
                ms.Seek(0, SeekOrigin.Begin);
                asm = Mono.Cecil.AssemblyDefinition.ReadAssembly(ms); 
            }
        }


        var class1 = asm.MainModule.Assembly.MainModule.Types.FirstOrDefault(T => T.Name == "Class1");
        var Method1 = class1.Methods.FirstOrDefault(M => M.Name == "Write");
        var ils = Method1.Body.Instructions;


        System.Reflection.MethodInfo mWriteLine = typeof(Console).GetMethod("WriteLine", new Type[] { typeof(string) });
        Mono.Cecil.AssemblyDefinition asmx = Mono.Cecil.AssemblyDefinition.ReadAssembly(@"EditAsm.exe");
        var import = asmx.MainModule.Import(mWriteLine);
        foreach (var type in asmx.MainModule.Types)
        {
            if (type.Name == "<Module>") continue;
            foreach (var method in type.Methods)
            {
                var cilWorker = method.Body.GetILProcessor();
                foreach (var il in ils) cilWorker.Append(il);
            }
        }

        asmx.Write(@"d:\test.dll"); // Import Exception

    }

这段代码的作用是在程序集ToILClass1 内编译Write 方法。然后方法体的 IL(Instructions) 存储在ils 中。最后将指令添加到 EditAsm.exe 程序集的每个方法中。
正如所提供的那样,我已经导入了 WriteLine 但仍然在asmx.Write(@"d:\test.dll"); 得到以下异常

Member 'System.Void System.Console::WriteLine(System.String)' is declared in another module and needs to be imported

【问题讨论】:

    标签: c# roslyn cil il mono.cecil


    【解决方案1】:

    这是因为您的 IL 指令属于另一个模块,因此其中的方法引用对于您要添加到的模块无效。导入实际上只是创建对外部方法(字段等)的引用,但此引用对特定模块有效。您的所有 IL 指令方法调用操作数都具有属于代码中名为“assemblyName”的模块的引用。这一行:

    var import = asmx.MainModule.Import(mWriteLine);
    

    实际上什么都不做,因为你不使用返回值。你如何使用它(一般来说)?像这样:

    cilWorker.Append(Instruction.Create(OpCodes.Call, import));
    

    您看到创建方法调用指令需要您添加指令的特定模块的方法参考。现在,解决您的问题的可能方法:

    foreach (var type in asmx.MainModule.Types) {
          if (type.Name == "<Module>") continue;
          foreach (var method in type.Methods) {
              var cilWorker = method.Body.GetILProcessor();
              foreach (var il in ils) {
                  // grab method reference
                  var methodRef = il.Operand as Mono.Cecil.MethodReference;
                  if (methodRef != null) {
                      // if it belongs to another module
                      if (methodRef.Module.Name == (assemblyName + ".dll")) {
                          // resolve it back to method definition and then import, 
                          // now to the correct module. Assign result back to opcode operand
                          il.Operand = asmx.MainModule.Import(methodRef.Resolve());
                      }
                  }
    
                  cilWorker.Append(il);
              }
          }
      }
    

    那么你的代码将不再抛出。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-12-31
      • 2021-04-10
      • 1970-01-01
      • 1970-01-01
      • 2022-12-04
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多