有没有一种方法可以像在C++中钩取未管理的函数那样,在C#中钩取托管函数?

11

在C++中,我可以获取函数的地址并覆盖前几个字节以进行跳转到我的函数,然后执行一些操作,恢复原始字节并调用原始函数。

在C#中我能否做类似的事情?


这个问题非常有趣。但我不认为有一种方法.. - Maziar Taheri
3个回答

9

.NET Profiler API是截取运行时方法最接近“微软认可”的方式。正如已经提到的,这在实现上有些困难,我不知道是否有一个纯粹使用托管代码就能轻松实现的库。

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

最后,我所做的是开发Afterthought作为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);   

在这种情况下,当我修改一个现有类型并引入一个名为Sum的方法时,我会在方法之前和之后添加代码来测量执行时间。运行Afterthought来处理原始编译类后,生成的Sum方法将在方法体之前和之后调用这些lambda表达式(C#编译器只是将它们转换为静态方法)。我也可以轻松地调用Implement并替换整个方法实现。
希望其中一种方法能够满足您的需求。在我的情况下,我选择了AOP路线,因为我想做更多的拦截操作,例如实现接口、添加新的方法/属性等。我还想要一些不会在运行时引入依赖关系或关于稳定性或性能的问题的东西-编译时处理只是更安全、更容易测试的。

如果我要挂钩的函数在另一个程序集中,而我没有其源代码,那么“事后补救”是否可行? - Avery3R
是的。Afterthought与PostSharp有些不同,它允许您在一个程序集中创建修正(如上所述-更改的代码描述),并将其应用于另一个程序集。请查看Logging Example。它描述了Logging.Amender程序集中的修正,然后应用于Logging.UnitTest.Target程序集。这也有一个好处,即允许您修改程序集而不建立对Afterthought本身的引用-使其成为一个工具。 - Jamie Thomas
Afterthought是否使用.NET Profiler拦截?我正在寻找一个能做到这一点的工具。 - orellabac
Afterthought 看起来非常有趣,但实际上已经被放弃了 :(. - Zero3

2

我想在运行时进行钩子,但是分析 API 看起来像是 C++ 的,我想从 C# 钩取一个 C# 函数。 - Avery3R
听起来有问题,因为你正在操纵编译自己的C#代码所需的非常抖动。也许一个受控封装和多个应用程序域可以帮助解决问题。但这听起来比将本机C/C++与C#混合使用更麻烦。 - CodesInChaos

1
你所需要的技术叫做AOP——面向方面的编程。如果你搜索一下,可以找到几个适用于C# 的框架。
更新:你可以在这个问题中找到大量链接和信息:内置AOP在C#中是否已经实现?

我已经谷歌搜索过了,但是找不到任何相关信息,你能指点我一下吗? - Avery3R
这些都不是我想要的,我正在寻找一种拦截函数调用并运行自己代码的方法,然后可选择性地运行原始代码。 - Avery3R
是的,这正是AOP用于的目的! - Dan Byström
当然,这取决于您使用的框架。例如,在PostSharp中,您可以像这样做:http://www.sharpcrafters.com/solutions/logging - Dan Byström

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接