Python Ctypes注册回调函数

Python Ctypes register callback functions

我 运行 使用 Python 和 ctypes 进入一些非常 st运行ge 的东西。我正在使用 Python 3.4.3。首先,项目的一些背景:

我已经从 C 代码编译了一个自定义 dll。我正在使用 ctypes 与 dll 交互。 C 库与一些自定义硬件接口。有时硬件会生成一个中断并将其传递给计算机上的 C 库。在CAPI中,有一个原型为void register_callback(int addr, void (*callback)(void))的函数。我有一个回调函数指针数组,它们被初始化为 NULL。调用该函数时,index addr处的回调函数指针设置为callback,如:callbacks[addr] = callback;.

当用户在 Python 中编程时,他们会实例化 类 中的对象,这些对象模拟不同的硬件部件(例如按钮或 RGB LED)。然后他们可以编写自定义回调函数并调用 button.register_callback(func)(当然,假设他们有一个名为 button 的 Button 对象),这将调用 C 库中的 register_callback 函数。现在,当按下按钮并产生中断时,C 库将调用适当的回调函数(即 callbacks[addr]();)。

现在,奇怪的是:

在 Python 中,我第一次尝试 Python 中的 register_callback 方法是这样的:

class Obj:
    def __init__(self, name):
        # Initialize stuff

    def register_callback(self, func):
        CB_T = ctypes.CFUNCTYPE(None)
        cb_ptr = CB_T(func)
        host_api.register_callback(self.addr, cb_ptr) # host_api is the loaded dll

主要是:

def cb1():
    print("cb1")

def cb2():
    print("cb2")

def main(argv):
    # Initialization stuff
    # Now create the objects and register the callbacks:
    obj = Obj_module.Obj()
    obj2 = Obj_module.Obj()
    obj.register_callback(cb1)
    obj2.register_callback(cb2)

    while True:
        pass

当我 运行 这个时,只有 "cb2" 被打印,不管我按的是哪个按钮。真正奇怪的是,当我切换注册回调的顺序时:

    obj2.register_callback(cb2)
    obj.register_callback(cb1)

只打印了 "cb1",不管我按的是什么按钮!在 C 库中,我验证(通过 printf)根据按钮设置和调用了不同的回调函数指针,但相同的函数指针被传递给 C register_callback 函数。

我可以通过在 register_callback 方法中添加一行来解决问题:

def register_callback(self, func):
    CB_T = ctypes.CFUNCTYPE(None)
    cb_ptr = CB_T(func)
    (ctypes.cast(cb_ptr, ctypes.POINTER(ctypes.c_int)))
    host_api.register_callback(self.addr, cb_ptr)

显然,将 cb_ptr 转换为 ctypes POINTER 解决了问题 - 传入了不同的函数指针,我成功地看到 "cb1" 或 "cb2" 打印出来,具体取决于按钮我按下了。

我的问题是,为什么?为什么在原始代码中传递了相同的函数指针,为什么它根据我注册回调的顺序而改变,为什么将 cb_ptr 转换为 ctypes POINTER 确保函数指针不同?

我是 Python 的初学者,但我在 C 方面更有经验。提前感谢您的回复。

您的 cb_ptr 正在被垃圾回收。来自 documentation:

Make sure you keep references to CFUNCTYPE() objects as long as they are used from C code. ctypes doesn’t, and if you don’t, they may be garbage collected, crashing your program when a callback is made.

在此代码示例中,如果行 ptrs.append(cb_ptr) 被注释掉,cb_ptr 的位置对于两个 Obj 实例(在我的计算机上)是相同的。取消注释该行会产生两个内存位置。

import ctypes

ptrs = []

class Obj:
    def __init__(self):
        pass

    def register_callback(self, func):
        CB_T = ctypes.CFUNCTYPE(None)
        cb_ptr = CB_T(func)
        ptrs.append(cb_ptr)
        print(cb_ptr)

def cb1(): print("cb1")

def cb2(): print("cb2")

def main(argv):
    obj = Obj()
    obj2 = Obj()
    obj.register_callback(cb1)
    obj2.register_callback(cb2)

main(None)