python ctypes,回调函数增广类型为C++参考

Posted

技术标签:

【中文标题】python ctypes,回调函数增广类型为C++参考【英文标题】:python ctypes, callback function augment type is C++ reference 【发布时间】:2017-04-17 10:41:29 【问题描述】:

我通过 ctypes 将一个 dll 集成到 python 中,该 dll 需要设置一个回调函数。但是dll回调函数声明如下

void setCallBack(void(*)(Data &a))

Data is C struct, declare as below
typedef struct 
    int index,
    ....
Data

我不清楚如何通过ctypes设置它,我尝试如下,但返回“WindowsError:[Error -1073741795] Windows Error 0xC000001D”错误,你能帮忙举个例子如何设置回调。

mydll = cdll.LoadLibrary("XXX.dll")  
callback_type = CFUNCTYPE(None, POINTER(Data))
callback = callback_type(Data)
mydll.setCallBack(callback)

def dataResponse(data):
    print data.index

class Data(ctypes.Structure):
    _fields_ = [("index", c_int)]

【问题讨论】:

【参考方案1】:

您的结构看起来是正确的,但如果您正在调用 Windows API,则您的调用约定可能不正确(您使用的是 cdecl,但如果这是 Windows API 调用,则需要 stdcall)。

尝试将mydll = cdll.LoadLibrary("XXX.dll") 替换为mydll = WinDLL("XXX.dll")

您可能还需要提供一个使用 stdcall 而不是 cdecl 的回调函数。如果是这样,请将CFUNCTYPE 切换为WINFUNCTYPE。您调用的 API 应提供有关其预期内容的详细信息。

看看是否有帮助。

【讨论】:

【参考方案2】:

一个错误是您的回调正在包装您的数据结构,而不是您的回调函数。变化:

callback_type = CFUNCTYPE(None, POINTER(Data))
callback = callback_type(Data)
mydll.setCallBack(callback)
def dataResponse(data):
    print data.index

到:

def dataResponse(data):
    print data.index
callback = callback_type(dataResponse)
mydll.setCallBack(callback)

或者你可以使用装饰器语法:

@CFUNCTYPE(None, POINTER(Data))
def dataResponse(data):
    print data.index

mydll.setCallBack(dataResponse)

这是一个完整的例子。也可以使用extern "C" 来避免 C++ 名称混淆:

test.cpp (Windows)

struct Data 
    int index;
;

typedef void (*CALLBACK)(Data&);
CALLBACK g_callback;

extern "C" 
    __declspec(dllexport)void setCallBack(CALLBACK f) 
        g_callback = f;
    

    __declspec(dllexport) void func() 
        Data d;
        d.index = 42;
        if(g_callback)
            g_callback(d);
    

test.py

from ctypes import *

class Data(Structure):
    _fields_ = [('index',c_int)]

CALLBACK = CFUNCTYPE(None,POINTER(Data))

dll = CDLL('test')
dll.setCallBack.argtypes = CALLBACK,
dll.setCallBack.restype = None
dll.func.argtypes = None
dll.restype = None

@CALLBACK
def dataResponse(data):
    print(data.contents.index)

dll.setCallBack(dataResponse)
dll.func()

输出

42

【讨论】:

【参考方案3】:

ctypes 不适用于 Cpp,它仅适用于 C,您的 Cpp 函数需要有 extern "C" 链接。 不起作用的原因与 Cpp 名称修改有关。您的函数名称 void setCallBack(void(*)(Data &a)) 在 Cpp 中被修改为 _Z11setCallBackPFvR4DataE,但是如果您将 extern "C" 从它 (extern "C" void setCallBack(void(*)(Data &a))) 中放入,它将 C 修改规则,因此它将被修改为 setCallBack。 因此,当您执行 mydll.setCallBack 时,ctypes 正在寻找一个名为 setCallBack 的函数但它没有找到它,它会找到一个名为 _Z11setCallBackPFvR4DataE 的函数,它无法识别并给您一个错误。

【讨论】:

以上是关于python ctypes,回调函数增广类型为C++参考的主要内容,如果未能解决你的问题,请参考以下文章

Python ctypes回调函数没有得到任何数据

在 Python/ctypes 中的结构内取消引用 C 函数指针

Python 外部函数调用库ctypes简介

使用ctypes实现python类型和C语言类型之间的相互转化

ctypes给扩展模块中的函数传递回调函数

使用 ctypes 传递结构指针