【问题标题】:How to allocate memory to a C callback function with ctypes and threading如何使用 ctypes 和线程将内存分配给 C 回调函数
【发布时间】:2016-12-08 09:02:55
【问题描述】:

我目前正在为相机软件编写 python 绑定。商业 SDK 仅包含文档和 DLL。我没有 C 代码,但我会尽量讲清楚,所以请多多包涵。

我的所有库函数都在工作,但只有一个:获取图片的回调函数。文档只提供了函数原型:

foo:在指定端口开始图像采集。返回错误代码。

int foo(HANDLE dev, char port, int queueMode, callback_type fnCallback, void * userInfo );

callback_type:应用程序定义的回调函数,接收 获取帧信息。

void callback_type(void * userInfo, photoStruct* photo);

这是我加载库并包装函数和结构后的python代码。

callback_type = WINFUNCTYPE(None,c_void_p,POINTER(photoStruct)) 
def my_callback(userInfo, photoInfo):
    if(photoInfo.status == 0):
        #Process incoming photo
        print "I'm here" #for debugging purposes
        q.put(photoInfo.rawBuffer[:photoInfo.bufferSize])
        q.task_done()
_my_callback = callback_type(my_callback)

每当我第一次尝试调用 foo 时:

 >>>lib.foo(dev, port, c_int(0), _my_callback, None)

我得到错误代码 4:没有足够的内存来执行操作(根据文档)。 当我再次(又一次)调用它时,它总是返回 0 表示成功,但队列保持为空并且它从不打印“我在这里”。

然后我尝试添加一个线程:

ty = threading.Thread(target=lib.foo, args=(dev,port, c_int(0), _my_callback, None))
>>> ty.start()   
>>> ty.run() 
line 758, in run
    del self.__target, self.__args, self.__kwargs  
AttributeError: _Thread__target

我还尝试不使用 run() 并在启动线程后直接调用该函数。它仍然不起作用。 我真的不知道这里发生了什么。我对线程和 ctypes 很陌生,但我的猜测是该库使用自己的线程来返回照片,我应该以某种方式为这个回调函数分配内存。但是怎么做? 任何解决方案/cmets 将不胜感激。谢谢!

_ 编辑:文档中的示例代码 _

* \sample
* \code
* void __stdcall callback_type(void* userInfo, photoStruct* photo)
* {
*   // Process incoming photo
* }
* ...
* foo(dev, port, 0, &callback_type, NULL);
* ...
* \endcode
*/

我尝试在 Python 中调用 foo

>>>lib.foo(dev, port, c_int(0), byref(_my_callback), None)

它停止了程序。

【问题讨论】:

  • 我不知道我是否完全理解您的问题,但 userInfo 是可选的,所以我将其保留为 NonephotoInfo 是我需要的结构 photoStruct
  • 能否在回调函数中打印 userInfo 和 photoInfo ?在此处发布结果。
  • @Munchhausen OP 正确,None 返回类型。 void == None, void* == c_void_p
  • @Munchhausen 我不能我尝试从 _my_callback 打印它们,但没有任何反应
  • 如果什么都没有打印,我们可以断定你的回调没有被调用吗?

标签: python c multithreading ctypes


【解决方案1】:

如果没有完整的示例,我不确定您哪里出错了。你声明foo.argtypes了吗?您确定函数是__stdcallWinDLLWINFUNCTYPE 一起使用,还是它们是__cdecl 并且需要CDLLCFUNCTYPE

这是一个使用以下 DLL 测试代码的工作示例:

typedef void* HANDLE;

typedef struct {
    int value;
} photoStruct;

typedef void (__stdcall * callback_type)(void * userInfo, photoStruct* photo);

__declspec(dllexport) int __stdcall foo(HANDLE dev, char port, int queueMode, callback_type fnCallback, void * userInfo)
{
    photoStruct p;
    p.value = 123;
    fnCallback(userInfo, &p);
    return 0;
}

Python 代码:

#!python2
from ctypes import *
HANDLE = c_void_p

class photoStruct(Structure):
    _fields_ = [('value',c_int)]
    def __repr__(self):
        return 'photoStruct(value={})'.format(self.value)

callback_type = WINFUNCTYPE(None,c_void_p,POINTER(photoStruct))

@callback_type
def my_callback(userInfo, photo):
    print userInfo,photo.contents

foo = WinDLL('test').foo
foo.argtypes = (HANDLE,c_char,c_int,callback_type,c_void_p)
foo.restype = c_int

foo(1,'2',3,my_callback,None)

输出:

None photoStruct(value=123)

【讨论】:

  • 感谢您的回复。我仍然得到同样的错误:没有足够的内存。该函数是标准调用。来自 lib.h typedef void (__stdcall *callback_type)(void* useInfo, photoStruct* photoInfo);
  • @Unicorn 这适用于我在 Windows 10 上使用 Python 2(32 位)。您是否编译了这个确切的 DLL 示例并使用了这个确切的 Python 代码?如果您使用自己的 DLL,则函数声明必须有所不同。
  • @Unicorn 另外,如果使用您的实际 DLL,您可以在 C 中使用类似的回调调用相同的函数吗?可能 API 使用不正确。 photoInfo.rawBuffer 有多大?这可能会通过 ctypes 复制并超出 32 位应用程序中的虚拟内存空间(如果它是 32 位)。
  • 我使用的是 64 位 Windows 7。我现在要检查并发布结果。 Ps:我将文档中的示例代码添加到我的问题中。
  • @Unicorn 我构建了上述代码的 Python 3 64 位版本,它也可以正常工作(更改为打印 function 后)。
猜你喜欢
  • 1970-01-01
  • 2016-02-02
  • 1970-01-01
  • 2013-03-03
  • 2010-11-26
  • 2015-05-10
  • 1970-01-01
  • 2019-05-11
  • 1970-01-01
相关资源
最近更新 更多