让C库使用ctypes调用python函数

让C库使用ctypes调用python函数,python,c,ctypes,Python,C,Ctypes,我有一个c语言的DLL,用于windows平台,其结构类似于以下内容: 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)) C结构 typedef struct some_struct { int (_s

我有一个c语言的DLL,用于windows平台,其结构类似于以下内容:

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))
C结构

typedef struct some_struct {
    int (_stdcall *function)(int arg1, int arg2);
    ...
}SOME_STRUCT;
class SOME_STRUCT(Structure):
    _fields_ = [('function', POINTER(CFUNCTYPE(c_int, c_int, c_int))), ...]
我已经定义了一个PythoncTypes结构来模拟它,如下所示

Python结构

typedef struct some_struct {
    int (_stdcall *function)(int arg1, int arg2);
    ...
}SOME_STRUCT;
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
实例。我该如何做我想做的事情?

这里是一个工作示例和测试DLL源代码。奇怪的是,当回调是结构(崩溃)的唯一成员时,我无法让它工作。这看起来像一个bug,因为没有结构包装器的回调或向结构中添加第二个成员使其工作

注意事项:

  • WINFUNCTYPE
    \uu stdcall
    一起使用
    CFUNCTYPE
    用于
    \uu cdecl
  • 您不需要
    指针
    byref
    使其工作
  • @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);
}
输出

7
3

ctypes文档中的示例没有像您这样使用对
byref
的调用。如果删除byref调用,只执行
struct.function=prototype(func)
,是否有效?@PaulCornelius我尝试过这样做,但该函数似乎从未被调用过。我从来没有看到它的输出。我试图遵循您指出的示例,但我认为我需要
byref
或类似的东西,因为我试图将指针分配给函数,而不是函数本身(示例正在执行)。在这种情况下,我还有一个想法。如果CFUNCTYPE返回一个表示函数指针的对象,那么您可能不希望在结构定义中调用pointer()函数。非常感谢。这是非常有用的。不过有一件小事,我必须做
dll.func(byref(struct))
(test.py的最后一行)才能使调用工作。@zephyr没有它我也能工作,但这在技术上更为正确。在
argtypes
中是否有最后一个逗号?它一定是一个序列,这使它成为一个元组。是的,我做到了。事实上,我复制并粘贴了您的两个程序,除了需要byref之外,它们工作得很好。如果没有它,我在写入某个随机内存地址时会收到一个操作错误,说是访问冲突。@zephyr 32位还是64位Python?我只是想知道为什么它对我有效,并寻找不同之处。我使用的是64位,我猜结构的封送总是通过ABI中的指针进行的。这也可以解释为什么只有一个结构成员时我崩溃了。我得回去试试那个修复程序。我用的是32位。