我正在尝试使用 Python 的 ctypes 库访问扫描库中的一些方法 SANE .这是我第一次使用 ctypes,也是我一年多来第一次不得不处理 C 数据类型,所以这里有一个公平的学习曲线,但我认为即使没有这个特定的声明也会很麻烦:
extern SANE_Status sane_get_devices (const SANE_Device *** device_list, SANE_Bool local_only);
首先,我已经成功处理了 SANE_Status(枚举)和 SANE_Bool(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) 讨厌和 2) 这似乎只有在只有一个结果时才有效。我不能在 devices.contents.contents 或 devices.contents.contents.contents 上使用 len()。我如何确定结果的数量? SANE 文档指定“如果函数成功执行,它会存储一个指针,该指针指向指向 SANE_Device 结构的 NULL 终止指针数组,位于 *device_list 中”。有什么建议吗?
更新 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
但是,十显然是一个任意数字,我无法确定实际结果的数量。在仅连接一个设备时尝试访问 devices.contents.contents.contents[1].name 会导致段错误。必须有一种适当的方法来处理 ctypes 中的可变长度结构。
请您参考如下方法:
const SANE_Device *** 是一个三级指针:它是一个指向常量 SANE_Device 的指针的指针。您可以使用程序 cdecl破译复杂的 C/C++ 类型定义。
根据SANE documentation ,如果成功,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 和 POINTER 之间的区别:前者创建一个指向 instance 的指针,而后者创建一个指向 type 的指针。因此,我猜测以下代码会起作用:
// 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 占位符测试了上面的代码,它有效。
