【问题标题】:C++/C# Python HookC++/C# Python 钩子
【发布时间】:2013-02-19 21:53:00
【问题描述】:

我搜索了好和 Stack Overflow,但找不到我正在寻找的答案。有没有办法从 C++/C# 中挂钩 Python 函数的调用?捕获函数调用及其参数?

举例编辑:

def a(pOne): //做事

定义 b(): 一(“一”)

所以在调用“a”时,我希望 C++ 挂钩该函数(假设函数名已知)并在调用“a”时执行 C++ 函数/事件,并将参数传递给 C++ 函数/事件是为“pOne”传入的值。

谢谢

【问题讨论】:

  • 你到底想做什么?您是否正在寻找构建具有嵌入式(和挂钩)解释器的 C++ 程序?挂钩系统上运行的每个 Python.exe 实例?还是……?
  • 我在原始帖子中添加了一个示例。但是不,挂钩一个已知/特定的实例。
  • 你真的需要挂钩Python代码中定义的函数a,还是只是想要一种用C++编写函数并将其暴露给Python的方法,所以它可以被称为,比如说,foocpp.a("a")?后者真的很容易;前者要复杂得多。
  • 不,我需要上钩。所以我有一个外部 C++ 应用程序,它将调用“a”。对“a”的调用是在 Python 应用程序本身内部发生的,我想你可以说只是想被动地监听它。
  • 你能解释一下你真正想在这里完成什么吗?这闻起来像 XY problem,更糟糕的是,Y 是非常难的东西,需要很多你可能没有的背景知识,甚至需要学习才能开始。

标签: c# c++ python hook code-injection


【解决方案1】:

C++ 有几个不同的选项,具体取决于您想要做什么。

做你想做的最好的方法是“扩展”Python。

我最喜欢的方式是Boost.Python

您也可以使用原始的C-API,但我不建议这样做。

有关使用 Boost.Python 嵌入和扩展的一些示例,您可以查看我一直在从事的 this 项目。

【讨论】:

  • 我从没想过看boost。谢谢,我会看看。
  • 你能举个例子吗?
  • 这似乎没有回答 OP 的实际问题。构建扩展模块不会从外部挂钩现有的 Python 代码……
  • @abarnert 您构建了扩展模块以允许您与 C++ 代码进行交互。然后,您将 python 嵌入到您的应用程序中,以便能够来回传递值。看看我上面的项目,我们有一个脚本系统,它允许我们做 OP 建议他想做的事情,它与拦截调用无关。
  • 是的,这是完全合理的做法,但它与从外部挂钩 Python 解释器不同,因此每当解释器尝试执行 a() 函数时,其他代码就会运行......
【解决方案2】:

如果您只是想编写一些 C++ 代码并使其可从 Python 调用,您可以通过 Extending and Embedding the Python Interpreter 执行此操作。 (其实就是Extending,另一半忽略。)

基本上,如果你写一个新的模块foo,任何人都可以import foo和调用foo.a("a"),不管模块foo是实现为Python文件foo.py,还是编译自foo.cpp 进入动态库foo.so

为此,正如 Kyle C 所建议的那样,有许多高级方法可以做到这一点,因此您无需直接处理 C API。 (除了他对 Boost.Python 的建议之外,我还建议您查看 Cython,它可以让您使用几乎是 Python 的语言编写代码,该语言可以直接与您的 C++ 对话,同时也可以直接向 Python 公开内容。 )

但是,这不是您要求的。您想要做的是获取一些在 Python 代码中定义的函数,并将其挂钩以执行不同的操作。为此,您确实需要阅读上面链接的扩展和嵌入文档。你将不得不编写一个嵌入式解释器,重现一些行为(具体多少取决于你想钩住它的确切位置),并确保无论在哪里调用 PyObject_Call 或类似的函数,它首先检查该对象是否为a 函数,如果是,则调用您的钩子代码。

这将是相当困难的。如果您还没有编写嵌入式 Python 解释器,请在考虑如何钩住它之前完成。

值得注意的是,从 Python 内部进行挂钩可能比从解释器外部进行挂钩要容易得多。 (如果你从来没有听说过“monkeypatching”,去谷歌一下。)你总是可以从你用 C++ 构建的模块中制作你的 Python 钩子调用代码,甚至可以通过 ctypes 直接调用编译的 .so 文件。 .


最后,如果你想在运行时挂钩一些正在运行的解释器实例,那就更难了。您显然需要能够进行某种调试/跟踪/等。附加和代码插入,其细节完全取决于您的平台。然后,你会想要做你在以前的硬版本中所做的同样的事情,除了你必须通过拦截调用来做到这一点,例如,PyObject_Call(来自libpython.so/Python.dll/ Python.framework/whatever)首先通过你的钩子。如果您还不知道如何在您的平台上挂钩 SO 调用,则需要先了解这一点,然后才能考虑从外部挂钩 Python 代码。

【讨论】:

  • 我已经放弃了这个想法,我只是在胡闹,但这听起来比它的价值更麻烦。基本上我有一个我编写的小型 Python 应用程序,其想法是将 C++ DLL 注入正在运行的 Python 进程中,并让注入的 DLL 挂钩 Python 函数,并在调用该函数时在 C++ 端做一些事情。我认为这就像让 C++ 被动地监视 Python 函数调用,然后在调用函数时做一些事情。
【解决方案3】:

您有理由不更改 Python 内代码吗?如果没有,为什么不编写一个扩展模块或 python ctypes 调用 to your dll,并用调用你的钩子的装饰器包装 a

import my_cpp_module
def hook_a_to_call_my_cpp_module(orig_a):
    def replacement_a(pOne):
        try:
            my_cpp_module.my_cpp_function(pOne)
        finally:
            return orig_a(pOne)
    # 
    return replacement_a

@hook_a_to_call_my_cpp_module
def a(pOne):
    pass

您甚至可以在不接触具有a 的源文件的情况下执行此操作,如下所示:

# in this other file you import the module that contains function a
import the_py_that_has_a

# next line just to avoid copy-pasting the above code from my answer here again
from the_decorator_from_the_previous_example import hook_a_to_call_my_cpp_module

the_py_that_has_a.a = hook_a_to_call_my_cpp_module(the_py_that_has_a.a)

如果你有一些理由改变你的 py 代码,你能在评论中说明它们吗?

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2020-05-07
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多