有没有办法像在 C++ 中挂钩非托管函数一样在 C# 中挂钩托管函数?

Posted

技术标签:

【中文标题】有没有办法像在 C++ 中挂钩非托管函数一样在 C# 中挂钩托管函数?【英文标题】:Is there a way to hook a managed function in C# like I would a unmanaged function in C++? 【发布时间】:2011-05-29 18:23:09 【问题描述】:

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

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

【问题讨论】:

这个问题很有趣。但我不认为有办法.. 【参考方案1】:

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

几个月前我自己研究这个选项时,我偶然发现了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 路线,因为我想做的不仅仅是拦截,例如实现接口、添加新方法/属性等。我还想要一些不会在运行时引入依赖或担心稳定性或性能的东西。运行时环境——编译时处理更安全,更容易测试。

【讨论】:

如果我要挂钩的函数在另一个程序集中,而我没有源代码,那么事后诸葛亮会起作用吗? 是的。 Afterthought 与 PostSharp 的工作方式略有不同,它允许您在一个程序集中创建修改(如上 - 对您的更改的代码描述),并以另一个程序集为目标以应用更改。看看Logging Example。它描述了 Logging.Amender 程序集中的修改,然后将其应用于 Logging.UnitTest.Target 程序集。这还有一个好处是允许您在不建立对 Afterthought 本身的引用的情况下修改程序集——使其只是一个工具。 Afterthought 是否使用 .NET Profiler 拦截?我正在寻找可以做到这一点的工具 事后思考看起来很有趣,但实际上已被放弃:(。【参考方案2】:

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

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

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

【讨论】:

我想在运行时挂钩,但是分析 API 看起来像是在 C++ 中,我想从 C# 挂钩一个 C# 函数 听起来有问题,因为您正在处理编译自己的 C# 代码所需的抖动。也许托管包装器和多个应用程序域可以提供帮助。但这听起来比将原生 C/C++ 与 C# 混合更麻烦。【参考方案3】:

您正在寻找的技术称为 AOP - 面向方面的编程。如果你仔细观察,你可以找到几个 C# 框架。

更新:您可以在这个问题中找到大量链接和信息:Built-in AOP in C# - is it on the way?

【讨论】:

我用谷歌搜索了,但找不到任何东西,你能指出我正确的方向吗? 这一切都不像我想要的,我正在寻找一种方法来拦截一个函数,只要它被调用并运行我自己的代码,并且可以选择运行原始代码 是的,这正是 AOP 的用途! 这当然取决于您使用的框架。例如,在 PostSharp 中,您可以这样做:sharpcrafters.com/solutions/logging

以上是关于有没有办法像在 C++ 中挂钩非托管函数一样在 C# 中挂钩托管函数?的主要内容,如果未能解决你的问题,请参考以下文章

如何从 C# 导入和使用非托管 C++ 类?

使用非托管 C++ .dll 从 C# .exe 调用函数

PInvokeStackImbalance C# 调用非托管 C++ 函数

在托管 DLL 中加载 C++ DLL 时出现 EEFileLoadException

用 C++/CLI 包装非托管 C++ - 一种正确的方法

使用非托管 DLL 来自 .NET 的系统范围外壳挂钩