最简单的注入code到所有的方法和属性的方式,不具有自定义属性属性、有的、自定义、最简单

2023-09-03 02:00:33 作者:凉了时光伤了心

有一大把的问题和答案各地 AOP 中的 NET 这里对堆栈 溢出,常提的PostSharp和其它第三方产品。因此,似乎有相当多的AOP optons在.NET和C#的世界。但是,其中每个都有自己的限制,下载前途PostSharp 后,我在他们的文档发现,方法必须是虚的,以便能够注入code (编辑:见ChrisWue的回答和我的意见 - 虚拟约束必须一直对竞争者之一,我想)。我没有调查这一说法的准确性任何进一步的,但它的categoricality让我返回到堆栈 溢

There are a a lot of questions and answers around AOP in .NET here on Stack Overflow, often mentioning PostSharp and other third-party products. So there seems to be quite a range of AOP optons in the .NET and C# world. But each of those has their restrictions, and after downloading the promising PostSharp I found in their documentation that 'methods have to be virtual' in order to be able to inject code (edit: see ChrisWue's answer and my comment - the virtual constraint must have been on one of the contenders, I suppose). I haven't investigated the accuracy of this statement any further, but it's categoricality made me return back to Stack Overflow.

所以我希望得到的答案,这个非常具体的问题:

So I'd like to get an answer to this very specific question:

我想注入简单如果(某些条件)Console.WriteLine风格的 code到每一个方法和属性(静态,封闭,内部,虚拟,非虚拟的,无所谓)的我的项目不具有自定义的注释,以便在运行时动态测试我的软件。这种注入code不应该停留在发布版本,它只是意味着在开发过程中的动态测试(线程相关的)。

I want to inject simple "if (some-condition) Console.WriteLine" style code to every method and property (static, sealed, internal, virtual, non-virtual, doesn't matter) in my project that does not have a custom annotation, in order to dynamically test my software at run-time. This injected code should not remain in the release build, it is just meant for dynamic testing (thread-related) during development.

什么是做到这一点的最简单的方法是什么?我偶然发现 Mono.Cecil能做到,看起来完美,但你似乎有写$要在IL注入C $℃。这不是一个大问题,它很容易使用Mono.Cecil能得到一个IL版code C#编写的。但无论如何,如果有更简单的东西,最好是连内置到.NET(我仍然在.NET 3.5),我想知道。 [更新:如果建议的工具不是.NET Framework的一部分,这将是很好,如果它是开源的,像Mono.Cecil能做到,或免费提供]

What's the easiest way to do this? I stumbled upon Mono.Cecil, which looks ideal, except that you seem to have to write the code that you want to inject in IL. This isn't a huge problem, it's easy to use Mono.Cecil to get an IL version of code written in C#. But nevertheless, if there was something simpler, ideally even built into .NET (I'm still on .NET 3.5), I'd like to know. [Update: If the suggested tool is not part of the .NET Framework, it would be nice if it was open-source, like Mono.Cecil, or freely available]

推荐答案

我是能够解决的问题是 Mono.Cecil能做到。我还惊奇地发现易学,易用,功能强大是。在几乎完全没有证件也没有改变这一点。

I was able to solve the problem with Mono.Cecil. I am still amazed how easy to learn, easy to use, and powerful it is. The almost complete lack of documentation did not change that.

这些文件都是我用的3个来源:

These are the 3 sources of documentation I used:

静态方法拦截-in-NET-与-C-和-monocecil 迁移到0.9 在源$ C ​​$ C本身 static-method-interception-in-net-with-c-and-monocecil Migration to 0.9 the source code itself

第一个链接提供了一个非常不错的介绍,但因为它描述了丝丝的旧版本 - 和很多已经在此期间改变 - 第二个环节是翻译介绍塞西尔0.9非常有帮助。经过起步,中(也没有记录)来源$ C ​​$ C是无价的,并回答了每一个问题,我有 - 想到也许那些对.NET平台一般,但有吨上某处的书籍和材料在网上我敢肯定,

The first link provides a very gentle introduction, but as it describes an older version of Cecil - and much has changed in the meantime - the second link was very helpful in translating the introduction to Cecil 0.9. After getting started, the (also not documented) source code was invaluable and answered every question I had - expect perhaps those about the .NET platform in general, but there's tons of books and material on that somewhere online I'm sure.

我现在可以把DLL或EXE文件,修改它,并把它写回磁盘。我还没有完成的唯一的事情是搞清楚如何保持调试信息 - 文件名,行号等当前正在写的DLL或EXE文件后迷路。我的背景是不是.NET,所以我猜在这里,我的猜测是,我需要看看 mono.cecil.pdb 来解决这个问题。后来的某个地方 - 它并不适合我,超级重要的现在。我创建这个EXE文件,运行应用程序 - 这是一个复杂的GUI应用程序,成长多年来的所有行李,你会希望找到这样一块的,啊哈,软件 - 它检查的东西和记录错误我的。

I can now take a DLL or EXE file, modify it, and write it back to disk. The only thing that I haven't done yet is figuring out how to keep debugging information - file name, line number, etc. currently get lost after writing the DLL or EXE file. My background isn't .NET, so I'm guessing here, and my guess would be that I need to look at mono.cecil.pdb to fix that. Somewhere later - it's not that super important for me right now. I'm creating this EXE file, run the application - and it's a complex GUI application, grown over many years with all the baggage you would expect to find in such a piece of, ahem, software - and it checks things and logs errors for me.

下面是我的code中的要点:

Here's the gist of my code:

DefaultAssemblyResolver assemblyResolver = new DefaultAssemblyResolver();
// so it won't complain about not finding assemblies sitting in the same directory as the dll/exe we are going to patch
assemblyResolver.AddSearchDirectory(assemblyDirectory);
var readerParameters = new ReaderParameters { AssemblyResolver = assemblyResolver };

AssemblyDefinition assembly = AssemblyDefinition.ReadAssembly(assemblyFilename, readerParameters);

foreach (var moduleDefinition in assembly.Modules)
{
    foreach (var type in ModuleDefinitionRocks.GetAllTypes(moduleDefinition))
    {
        foreach (var method in type.Methods)
        {
            if (!HasAttribute("MyCustomAttribute", method.method.CustomAttributes)
            {
              ILProcessor ilProcessor = method.Body.GetILProcessor();
              ilProcessor.InsertBefore(method.Body.Instructions.First(), ilProcessor.Create(OpCodes.Call, threadCheckerMethod));
// ...

private static bool HasAttribute(string attributeName, IEnumerable<CustomAttribute> customAttributes)
{
    return GetAttributeByName(attributeName, customAttributes) != null;
}

private static CustomAttribute GetAttributeByName(string attributeName, IEnumerable<CustomAttribute> customAttributes)
{
    foreach (var attribute in customAttributes)
        if (attribute.AttributeType.FullName == attributeName)
            return attribute;
    return null;
}

如果有人知道一个简单的方法如何得到这个工作,我仍然有兴趣在回答我也不会纪念这个作为解决方案 - 除非没有更简单的解决方案显示了

If someone knows an easier way how to get this done, I'm still interested in an answer and I won't mark this as the solution - unless no easier solutions show up.