当前位置:  开发笔记 > 编程语言 > 正文

使用Python的ctypes传递/读取声明为"struct_name***param_name"的参数?

如何解决《使用Python的ctypes传递/读取声明为"struct_name***param_name"的参数?》经验,为你挑选了1个好方法。

我正在尝试使用Python的ctypes库来访问扫描库SANE中的一些方法.这是我第一次使用ctypes,第一次在一年多的时间内不得不处理C数据类型,所以这里有一个公平的学习曲线,但我认为即使没有这个,这个特殊的声明也会很麻烦:

extern SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only);

首先,我已成功处理SANE_Status(一个枚举)和SANE_Bool(一个typedef c_int).那些都很简单.另一方面,第一个参数让我感到各种各样的悲伤.我不熟悉***开头的符号,到目前为止,我的示踪子弹只能产生垃圾数据.如何格式化此函数的输入,以便我可以读回我的Python结构对象列表?作为参考,被引用的C结构是:

typedef struct
{
    SANE_String_Const name; /* unique device name */
    SANE_String_Const vendor;   /* device vendor string */
    SANE_String_Const model;    /* device model name */
    SANE_String_Const type; /* device type (e.g., "flatbed scanner") */
 }
SANE_Device;

在哪里SANE_String_Const定义为c_char_p.

我的Python/ctypes版本对象是:

class SANE_Device(Structure):
    _fields_ = [
        ("name", c_char_p),
        ("vendor", c_char_p),
        ("model", c_char_p),
        ("type", c_char_p)]

关于我应该传递什么的建议,以便我可以从中得到预期的行为(结构对象列表)?所有答复都赞赏

更新1:

使用以下内容,我能够检索正确的SANE_Device Python结构:

devices = pointer(pointer(pointer(SANE_Device())))
status = libsane.sane_get_devices(devices, c_int(0))
print status, devices, devices.contents.contents.contents.name

然而,1)yuck和2)似乎只有在有一个结果时它才会起作用.我不能len()on devices.contents.contentsdevices.contents.contents.contents.我如何确定结果的数量?SANE文档指定"如果函数成功执行,它将指向指向*device_list中的SANE_Device结构的NULL终止指针数组的指针".建议?

更新2:

我能够传递一个十项数组,然后使用以下方法访问第一个元素:

devices = pointer(pointer(pointer((SANE_Device * 10)())))
status = libsane.sane_get_devices(devices, c_int(0))
print status, devices, devices.contents.contents.contents[0].name

但是,10显然是一个任意数字,我无法确定实际的结果数.devices.contents.contents.contents[1].name当仅连接一个设备时尝试访问会导致分段错误.在ctypes中必须有一种正确的方法来处理像这样的可变长度结构.



1> Adam Rosenfi..:

A const SANE_Device ***是一个三级指针:它是指向指向常量SANE_Device的指针的指针.您可以使用程序cdecl来破译复杂的C/C++类型定义.

根据SANE文档,SANE_get_devices()如果成功,将存储指向以NULL结尾的指向SANE设备指针列表的指针.因此,调用它的正确方法是声明一个类型的变量const SANE_Device **(即指向常量`SANE_Device的指针),并传入该指针的地址:

const SANE_Device **device_list;
SANE_get_devices(&device_list, local_only);  // check return value
// Now, device_list[0] points to the first device,
// device_list[1] points to the second device, etc.
// Once you hit a NULL pointer, that's the end of the list:
int num_devices = 0;
while(device_list[num_devices] != NULL)
    num_devices++;
// num_devices now stores the total number of devices

现在,这就是你如何从C代码中调用它.我已经浏览了关于ctypes的文档,看起来你想使用该byref函数通过引用传递参数,并且你传递的值应该是指向SANE_Device的POINTER的指针.注意区分pointerPOINTER:前者创建一个指向实例,而后者创建了一个指针类型.因此,我猜测以下代码将起作用:

// SANE_Device declared as you had it
devices = POINTER(POINTER(SANE_Device))()  // devices is a NULL pointer to a pointer to a SANE_Device
status = libsane.sane_get_devices(byref(devices), c_int(0))
if status != successful:   // replace this by whatever success is
    print error
else:
    num_devices = 0
    // Convert NULL-terminated C list into Python list
    device_list = []
    while devices[num_devices]:
        device_list.append(devices[num_devices].contents)  // use .contents here since each entry in the C list is itself a pointer
        num_devices += 1
    print device_list

[编辑]我使用一个非常简单的占位符测试了上面的代码SANE_get_devices,它的工作原理.

推荐阅读
跟我搞对象吧
这个屌丝很懒,什么也没留下!
DevBox开发工具箱 | 专业的在线开发工具网站    京公网安备 11010802040832号  |  京ICP备19059560号-6
Copyright © 1998 - 2020 DevBox.CN. All Rights Reserved devBox.cn 开发工具箱 版权所有