问题 有没有办法在C#中挂钩托管函数,就像我在C ++中使用非托管函数一样?


在C ++中,我将得到函数的地址,并将前几个字节覆盖到jmp到我的函数,做一些事情,恢复原始字节,并调用原始函数。

我可以在C#中做这样的事吗?


5687
2018-05-29 18:23


起源

这个问题非常有趣。但我不认为有办法.. - Maziar Taheri


答案:


.NET Profiler API是最接近“Microsoft批准”的在运行时拦截方法的方法。如前所述,这有点难以实现,我不知道有一个库可以使用纯托管代码轻松实现。

几个月前,在我自己研究这个选项时,我偶然发现了 CLR方法注射,这篇文章的源代码解释了如何在运行时拦截方法。我自己考虑使用它,甚至让示例项目工作,但最终得出结论,我需要的不仅仅是方法拦截,还需要一个不同的解决方案。但是,这种方法直接回答了您的问题。

最后,我最终做的是开发 事后 作为开源替代品 PostSharp,作为后编译步骤运行,允许您修改现有方法以调用其他代码,或者完全替换方法实现。我现在正在研究一种新的流体语法,我将包含一个现在的样本,以提供一个示例来帮助您了解这种AOP方法如何满足您的需求:

Methods
    .Named("Sum")
    .WithParams<int[]>()
    .Before((T instance, ref int[] values)
        => { var s = new Stopwatch(); s.Start(); return s; })
    .After((instance, stopwatch, values)
        => instance.Result = (int)stopwatch.ElapsedMilliseconds);   

在这种情况下,用a修改现有类型 Sum 方法我在方法之前和之后引入代码来测量执行时间。在对原始编译类运行Afterthought之后,生成的Sum方法将在方法体之前和之后调用这些lambda表达式(C#编译器将它们转换为静态方法)。我可以轻松地打电话 Implement 并取代了整个方法的实现。

我希望其中一种方法能满足您的需求。在我的例子中,我去了AOP路线,因为我想做的不仅仅是拦截,比如实现接口,添加新的方法/属性等等。我还想要一些不会在运行时引入依赖关系或者关注稳定性或性能的问题。运行时环境 - 编译时处理更安全,更容易测试。


9
2018-05-30 02:10



如果我挂钩的功能是在另一个程序集中,那么事后才会工作,我没有源代码? - Avery3R
是。事后想法与PostSharp的工作方式略有不同,因为它允许您在一个程序集中创建修订(如上所述 - 您的更改的代码描述),并定位另一个程序集以应用更改。看看吧 记录示例。它描述了Logging.Amender程序集中的修订,然后将其应用于Logging.UnitTest.Target程序集。这也有一个好处,允许您修改程序集而不建立对Afterthought本身的引用 - 使其成为一种工具。 - Jamie Thomas
Afterterthought是否使用.NET Profiler拦截?我正在寻找一种能够做到这一点的工具 - orellabac
事后看起来非常有趣,但几乎被遗弃:(。 - Zero3


您想在运行时挂钩还是要修补二进制文件?

要在运行时挂钩,您可以使用profiling api(相对困难): http://msdn.microsoft.com/en-us/magazine/cc188743.aspx

要在编译时挂钩,可以使用IL重写器,例如PostSharp。


2
2018-05-29 18:56



我想在运行时挂钩,但是分析API看起来像是在C ++中,我想从C#挂钩C#函数 - Avery3R
听起来有问题,因为你正在操纵编译自己的C#代码所需的抖动。也许托管包装器和多个应用程序域可以提供帮助。但这听起来比将本地C / C ++与C#混合更麻烦。 - CodesInChaos


您正在寻找的技术称为AOP - 面向方面编程。如果你稍微调整一下,你可以找到C#的几个框架。

更新:您可以在此问题中找到大量链接和信息: C#内置AOP - 它正在路上吗?


0
2018-05-29 18:29



我用谷歌搜索,我找不到任何东西,你能指出我正确的方向吗? - Avery3R
更新了链接。 - Dan Byström
这看起来都不像我想要的,我正在寻找一种方法来拦截函数,无论何时调用它并运行我自己的代码,并且可选地运行原始代码 - Avery3R
是的,这正是AOP的用途! - Dan Byström
那我该怎么办? - Avery3R