Python使用ctypes调用C共享库,涉及自定义结构体。

6

我正在Linux系统上从Python调用一个C共享库。

我遇到的问题是C库中的函数需要一个结构体指针作为参数。然后它使用malloc为一组结构体分配内存,填充数组数据并返回。因此,我定义了以下函数:

from ctypes import *
class myStruct(Structure):
    _fields_ = [("id", c_uint), "name", c_char*256)]

library.func.argtypes =  [POINTER(myStruct)]

然后我这样调用它:

Myfoo = myStruct
Foo = pointer(Myfoo)
Bar = library.func(Foo)
for i in range(Bar):
    print("id = %u, name = %s" % (Foo[i].id, Foo[i].name))

Bar包含了由func分配的结构体数量。

无论我怎么做,都无法从Foo中获取任何数据。我已经尝试了多种不同的方法数月,但是无法从Python中提取数据,我可以查看C库的日志,知道它正在获得并返回数据,但似乎找不到提取数据的方法。

有什么想法吗?


你能澄清一下 func() 的函数签名吗?它以指向 myStruct 的指针作为参数,我明白了,但是它分配了一个什么样的数组的内存呢?更多的 myStructs 吗?新内存的指针应该放在哪里?或者 func() 应该接受一个 myStruct** 参数,并在那里填充新指针吗? - the paul
是的,func函数接受一个未初始化的myStruct类型的**指针,读取动态数据并确定需要多少个myStructs,使用函数提供的指针分配一个myStruct类型的数组,填充数组并返回在Bar中分配的数组元素数量。 - Thomas Knox
2个回答

5
如果我在你的问题评论中正确理解了“func”的原型,那么这应该可以实现:

C 代码

#include <stdlib.h>
#include <stdio.h>

struct Foo
{
    unsigned int id;
    char name[256];
};

__declspec(dllexport) int func(struct Foo **ppFoo)
{
    int i;
    struct Foo *foo = malloc(5 * sizeof(struct Foo));
    for(i=0;i<5;i++)
    {
        foo[i].id = i;
        sprintf_s(foo[i].name,_countof(foo[i].name),"Name#%d",i);
    }
    *ppFoo = foo;
    return 5;
}

Python代码

from ctypes import *
dll = CDLL('x')
class Foo(Structure):
    _fields_ = [
        ('id',c_uint),
        ('name',c_char*256)]

pfoo = POINTER(Foo)()
count = dll.func(byref(pfoo))
for i in xrange(count):
    print pfoo[i].id,pfoo[i].name

输出

0 Name#0
1 Name#1
2 Name#2
3 Name#3
4 Name#4

请注意,您仍然需要以某种方式“释放”已分配的内存…

糟糕,晚了一分钟?哈。 - the paul
是的,这个也可以。谢谢,这一直让我很烦恼。现在我可以回去工作并完成这个愚蠢的项目了。 - Thomas Knox

4
好的,这只需要进行一些小的更新。
首先,你的参数声明有误,但这可能并不重要。就Python-C值翻译机制而言,指针就是指针。最准确的方式是POINTER(POINTER(myStruct)),但为了简化事情,让我们使用以下方式:
library.func.argtypes = [c_void_p]

接下来,你不需要费心创建一个myStruct实例让你的参数指向它;你只需要一个有效的指针,以及指向该指针的指针。func将分配实际的myStruct实例。因此,让我们从以下代码开始:

Foo = ctypes.POINTER(myStruct)()

现在我们可以调用它了。我们有一个myStruct*,但是我们将传递一个指向它的指针,以便func可以更改它。我们不需要构造一个完整的其他指针对象,因此我们将使用更轻的byref

Bar = library.func(byref(Foo))

现在你可以遍历Foo[i]
for i in range(Bar):
    print("id = %u, name = %s" % (Foo[i].id, Foo[i].name))

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接