在.NET中是否有类似于Java的ClassFileTransformer? (替换课程的方法)

时间:2010-04-11 13:56:10

标签: c# .net reflection classloader instrumentation

到目前为止,我一直在搜索这个问题但没有运气。在.NET中是否有与Java ClassFileTransformer相当的东西?基本上,我想创建一个类CustomClassFileTransformer(在Java中实现接口ClassFileTransformer),只要加载一个类就会调用它,并允许调整它并用调整后的版本替换它。

我知道有些框架可以做类似的事情,但我正在寻找更直接的东西,比如实现我自己的ClassFileTransformer。有可能吗?


编辑#1 。关于我为什么需要这个的更多细节:

基本上,我有一个C#应用程序,我需要监视它想要运行的指令,以便检测对字段(操作LdfldStfld)的读或写操作,并在之前插入一些指令读/写发生。

我知道如何执行此操作(除了需要调用以替换类的部分):对于我想要监视其代码的每个方法,我必须:

  1. 使用MethodBody
  2. 获取方法MethodBase.GetMethodBody()
  3. 使用MethodBody.GetILAsByteArray()将其转换为字节数组。它返回的byte[]包含字节码。
  4. 按照here解释分析字节码,可能会插入新指令或通过更改数组内容删除/修改现有指令。
  5. 使用MethodBuilder.CreateMethodBody(byte[] il, int count)创建一个新方法并使用新字节码创建其正文,其中il是包含字节码的数组。 我将所有这些调整过的方法放在一个新类中,并使用新类替换最初要加载的类。
  6. 替换类的替代方法将以某种方式在调用方法时得到通知。然后我用调用我自己的调整方法替换对该方法的调用,我只会在第一次调用时进行调整,然后我将它放在字典中以备将来使用,以减少开销(对于将来的调用)我只是查找方法并调用它;我不需要再次分析字节码)。我正在研究如何做到这一点,LinFu看起来非常有趣,但是如果有类似ClassFileTransformer之类的内容会更简单:我只是重写类,替换它,然后让代码在没有监控任何事情的情况下运行。

    另外注意:课程可以密封。我希望能够替换任何类,我不能对其属性施加限制。


    编辑#2 。为什么我需要在运行时执行此操作。

    我需要监控正在进行的所有,以便我可以检测每次访问数据。这也适用于库类的代码。但是,我事先无法知道将要使用哪些类,即使我知道每个可能的类可能被加载,所以调整所有类而不是等待它将是一个巨大的性能影响。看看他们是否真的被调用了。


    可能(但是很难)解决方案。如果有人感兴趣(我看到这个问题已经存在,我猜有人会这样),this就是我现在正在看的。基本上我必须实现分析API,我将注册我感兴趣的事件,就我的情况而言,每当JIT编译开始时。博文摘录:

    • 在ICorProfilerCallback2 :: ModuleLoadFinished回调中,调用ICorProfilerInfo2 :: GetModuleMetadata获取指向该模块上元数据接口的指针。
    • 您想要的元数据接口的QI。在MSDN中搜索“IMetaDataImport”,然后浏览目录以查找元数据接口上的主题。
    • 一旦进入元数据领域,您就可以访问模块中的所有类型,包括其字段和函数原型。您可能需要解析元数据签名,并且此签名解析器可能对您有用。
    • 在您的ICorProfilerCallback2 :: JITCompilationStarted回调中,您可以使用ICorProfilerInfo2 :: GetILFunctionBody来检查原始IL,然后使用ICorProfilerInfo2 :: GetILFunctionBodyAllocator,然后使用ICorProfilerInfo2 :: SetILFunctionBody将IL替换为您自己的IL。

    好消息:当JIT编译开始时我收到通知,我可以在那里替换字节码,而不必担心替换类等等。不太好的消息:你不能从中调用托管代码API的回调方法,这有意义,但意味着我自己解析IL代码等,而不是能够使用Cecil,这将是一件轻而易举的事。

    我认为没有使用AOP框架(例如PostSharp)有更简单的方法可以做到这一点。如果有人有任何其他想法,请告诉我。我还没有回答这个问题。

2 个答案:

答案 0 :(得分:1)

我不知道.NET中的直接等价物。

但是,有一些方法可以实现类似的功能,例如使用Reflection.Emit按需生成程序集和类型,使用RealProxy为接口和MarshalByRefObject对象创建代理对象。但是,要建议使用什么,了解更多有关实际用例的信息非常重要。

答案 1 :(得分:1)

经过相当多的研究后,我正在回答我自己的问题:没有相当于.NET中的ClassFileTransformer,或任何直接替换类的方法。

通过托管CLR可以控制类加载过程,但这是非常低级的,你必须小心它,并且在每种情况下都不可能。例如,如果您在服务器上运行,则可能无权托管CLR。此外,如果您正在运行ASP.NET应用程序,则无法执行此操作,因为ASP.NET已提供CLR主机。

很可惜.NET不支持这个;他们这么容易做到这一点,他们只需要在加载一个类之前通知你,并让你有机会修改类,然后再将它传递给CLR加载它。