【问题标题】:Have C library call python function with ctypes让 C 库使用 ctypes 调用 python 函数
【发布时间】:2017-09-29 19:42:59
【问题描述】:

我有一个 c 中的 DLL,适用于 Windows 平台,其结构类似于以下内容:

C 结构

typedef struct some_struct {
    int (_stdcall *function)(int arg1, int arg2);
    ...
}SOME_STRUCT;

我已经定义了一个 python ctypes 结构来模仿这个结构

Python 结构

class SOME_STRUCT(Structure):
    _fields_ = [('function', POINTER(CFUNCTYPE(c_int, c_int, c_int))), ...]

这个结构在 C 代码中的要点是注册一个回调函数,该函数在其自己的线程中的某些触发器上执行。如果可能的话,我想要做的是将回调设置为 Python 函数,这样当 C 结构中的函数从 C 代码中调用时,执行的是 Python 函数。

我在 python 中尝试完成这个(不起作用)如下:

def func(arg1,arg2):
    print('I was called!')
    return 0

struct = SOME_STRUCT()
prototype = CFUNCTYPE(c_int, c_int, c_int)
struct.function = byref(prototype(func))

我得到的具体错误(这可能不是我唯一的问题)是它抱怨 struct.function 期待 LP_CFunctionType 实例但得到了 CArgObject 实例。我该怎么做?

【问题讨论】:

  • ctypes 文档docs.python.org/3/library/ctypes.html#callback-functions 中的示例没有像您那样使用对byref 的调用。如果您删除 byref 调用并只执行struct.function = prototype(func),它是否有效?
  • @PaulCornelius 我试过这样做,但该函数似乎永远不会被调用。我从来没有看到它的输出。我试图按照您指出的示例进行操作,但我相信我需要 byref 或类似的东西,因为我试图将指针分配给函数而不是函数本身(示例正在执行此操作)。
  • 那样的话,我还有一个想法。如果 CFUNCTYPE 返回一个表示函数指针的对象,那么您可能不希望在结构定义中调用 POINTER() 函数。

标签: python c ctypes


【解决方案1】:

这是一个工作示例和测试 DLL 源。奇怪的是,当回调是结构的唯一成员(崩溃)时,我无法让它工作。这似乎是一个错误,因为没有结构包装器的回调或向结构添加第二个成员使其工作。

注意事项:

  • WINFUNCTYPE__stdcall 一起使用。 CFUNCTYPE 代表__cdecl
  • 您不需要 POINTERbyref 即可使其工作。
  • @CALLBACK 装饰器等价于func = CALLBACK(func)

test.c

#include <stdio.h>

typedef int (__stdcall *CALLBACK)(int arg1, int arg2);

typedef struct some_struct {
    CALLBACK function;
    int other;
} SOME_STRUCT;

__declspec(dllexport) int func(SOME_STRUCT* pss)
{
    printf("%d\n",pss->other);
    return pss->function(1,2);
}

test.py

from ctypes import *

CALLBACK = WINFUNCTYPE(c_int,c_int,c_int)

class SOME_STRUCT(Structure):
    _fields_ = [('function', CALLBACK),
                ('other', c_int)]

@CALLBACK
def callback(arg1,arg2):
    return arg1 + arg2

dll = CDLL('test')
dll.argtypes = POINTER(SOME_STRUCT),
dll.restype = c_int

struct = SOME_STRUCT(callback,7)
print(dll.func(byref(struct)))

输出

7
3

【讨论】:

  • 非常感谢。这非常有用。不过,有一件小事,我必须做dll.func(byref(struct))(test.py 的最后一行)才能使通话正常进行。
  • @zephyr 没有它对我有用,但这在技术上更正确。 argtypes 中有最后一个逗号吗?它必须是一个序列,这使它成为一个元组。
  • 是的,我做到了。事实上,我复制并粘贴了你的两个程序,除了需要 byref 之外,它们工作得很好。没有它,我会在写入某个随机内存地址时收到一个 OSError 说访问冲突。
  • @zephyr 32 位还是 64 位 Python?我只是想知道为什么它对我有用并寻找不同之处。我使用的是 64 位,并且猜测结构的编组总是通过该 ABI 中的指针进行。这也可以解释为什么当只有一个结构成员时我会崩溃。我必须回去尝试修复。
  • 我使用的是 32 位。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2016-02-02
  • 1970-01-01
  • 1970-01-01
  • 2023-03-27
  • 1970-01-01
  • 2014-10-19
  • 1970-01-01
相关资源
最近更新 更多