使用 Mono.Cecil 在 C# 程序集中注入方法

Posted

技术标签:

【中文标题】使用 Mono.Cecil 在 C# 程序集中注入方法【英文标题】:Inject method in C# assembly with Mono.Cecil 【发布时间】:2017-03-02 20:36:14 【问题描述】:

我正在开发一些 .Net 应用程序,我需要使用我自己的代码在任何程序集中注入新方法。我正在使用 Mono.Cecil 来获取组装体,我找到了一些样本,但它们已经足够老了。不幸的是,在 github wiki 上的 migraton 部分没有任何信息。

所以,我有这个代码:

using System; 
using Mono.Cecil; 
using Mono.Cecil.Cil; 

namespace CustomFieldsInjection 
 
    public partial class Injector 
     
        public static void MethodInjection(string assemblyFilename, string typeName, string methodName) 
         
            AssemblyDefinition assembly = AssemblyFactory.GetAssembly(assemblyFilename); 

            TypeReference returnTypeReference = assembly.MainModule.Import(typeof(void)); 

            MethodDefinition methodDefinition = new MethodDefinition(methodName, MethodAttributes.Public | MethodAttributes.Static, returnTypeReference); 

            Instruction instruction1 = methodDefinition.Body.CilWorker.Create(OpCodes.Nop); 

            Instruction instruction2 = methodDefinition.Body.CilWorker.Create(OpCodes.Ldstr, methodName); 

            MethodReference writeline = assembly.MainModule.Import(typeof(Console).GetMethod("WriteLine", new Type[]  typeof(string) )); 

            methodDefinition.Body.CilWorker.Append(instruction1); 
            methodDefinition.Body.CilWorker.Append(instruction2); 

            methodDefinition.Body.CilWorker.InsertAfter(instruction2, methodDefinition.Body.CilWorker.Create (OpCodes.Call, writeline)); 

            methodDefinition.Body.CilWorker.Append (methodDefinition.Body.CilWorker.Create(OpCodes.Ret))

            assembly.MainModule.Inject(methodDefinition, assembly.MainModule.Types[typeName]); 
            MethodReference methodReference = null; 

            foreach (MethodDefinition method in assembly.MainModule.Types[typeName].Methods) 
             
                if (method.Name == methodName) 
                 
                    methodReference = assembly.MainModule.Import(method); 
                    break; 
                 
             

            Instruction callTest = methodDefinition.Body.CilWorker.Create(OpCodes.Call, methodReference); 

            if (assembly.EntryPoint != null) 
             
                assembly.EntryPoint.Body.CilWorker.InsertBefore(assembly.EntryPoint.Body.Instructions[0], callTest); 
             

            AssemblyFactory.SaveAssembly(assembly, assemblyFilename); 
         
     
 

这是旧样本。大多数功能都是最新的。我对这个结构很感兴趣:

assembly.MainModule.Inject(methodDefinition, assembly.MainModule.Types[typeName]);

我找不到这种设计的新类似物。谁能告诉我可以换什么?

【问题讨论】:

【参考方案1】:

我不熟悉您所指的构造,但是将 MethodDefinition 添加到现有类型非常容易

using (var assemblyDefinition = AssemblyDefinition.ReadAssembly("assemblyPath")) 
    var module = AssemblyDefinition.MainModule;

    //Select the type you need to open for addition
    var typeDef = module.Types.First(td => td.Name == "footer");

    //Add your MethodDefinition
    typeDef.Methods.Add(your_method_definition);

    //Write the assembly back
    assemblyDefinition.Write();

注意:如果您还没有使用 cecil 0.10.0.0,您将使用略有不同的 ReadAssembly()Write() 变体(没有 using,并将 assemblyPath 传递给 Write,主要是... )

【讨论】:

@Stepane Delcroix 非常感谢!看来这就是我需要的! 那行不通。 Write() 将抛出 System.ArgumentException: 'Stream must be writable and seekable.' @latency 将ReadAssemblyreaderParameters 设置为包含ReadWrite = true

以上是关于使用 Mono.Cecil 在 C# 程序集中注入方法的主要内容,如果未能解决你的问题,请参考以下文章

编译时MSIL注入--实践Mono Cecil

如何使用 Mono.Cecil 获取 IL 指令的源代码/行号

使用 Mono.Cecil 辅助 Unity3D 手游进行性能测试(续)

如何以编程方式替换 .NET 程序集中的嵌入式资源?

使用MOno Cecil 的相关开源项目

Mono.Cecil - 0.6