在 python 中的 byref 向量参数上使用 ctypes 进行 DLL 转换

DLL conversion with ctypes on byref vector argument in python

我的机器是Win7,带anaconda。 我最近将 C++ dll 函数转换为 python 项目。 我克服了很多困难,但我不知道如何处理以下转换:

typedef int (__stdcall *p_API_GetOrder)(vector<ApiOrder>& apiOrderList);

其中,

class ApiOrder(Structure):
  _fields_ = [
  ('Timestamp',      c_long),
  ('Item',           c_char * 16),
  ('Qty',            c_long),
  ]

在python,我试过了,

mydll.API_GetOrder(POINTER(ApiOrder()))

错误是:

TypeError: must be a ctypes type

我不是 C++ 或编程的输出者。所以不太确定 byref 是什么。如果有人能澄清我的概念就太好了。

POINTER(…) 构造一个新的 指针类型 ,而不是该类型的值。所以,当你这样做时:

 mydll.API_GetOrder(POINTER(ApiOrder()))

…您传递的是 Python 类型对象,而不是 C 指针对象周围的 ctypes 包装器。


要获取指向 ctypes 包装器对象的指针,您需要调用 pointer or byref。前者构造一个 POINTER(…) 实例,将其设置为指向您的对象,并传递包装指针;后者只是直接将指针传递给您的对象,而无需构造指针包装对象,通常这就是您所需要的。有关详细信息,请参阅文档中的传递指针。


但是,出于两个原因,我认为这不会有太大好处。


首先,大多数采用指向某个结构的指针和 return int 的函数都在这样做,以便它们可以用有用的值填充该结构。构建一个新的空结构并将指针传递给它而不保留对它的引用意味着您无法查看填充的任何值。

此外,您可能想要检查 return 值。

一般来说,你需要做这样的事情:

order = ApiOrder()
ret = mydll.API_GetOrder(byref(order))
if ret:
    do some error handling with either ret or errno 
else:
    so something with order

虽然我们正在做这件事,但您几乎肯定想要 set the argtypes and restype of the function,所以 ctypes 知道如何正确地转换事物,并且如果您做了一些毫无意义的事情,可以给您一个例外,而不是让它猜测如何转换和传递东西,如果它猜错了就会出现段错误。

此外,对于 return 成功或错误 int 的函数,通常最好将函数分配给 restype,它会查找错误raises 是一个适当的例外。 (如果您需要比仅检查 int return 是否为非零或指针 return 是否为零更灵活的东西,请使用 errcheck。)


但即使这样在这里也无济于事,因为您尝试调用的函数首先不接受指向 ApiOrder 的指针,它需要引用 std::vector 其中。所以你需要调用C++ stdlib构造一个that类型的对象,然后你可以byrefthat作为参数。

但通常,编写一些向库提供 C API 的 C++ 代码,然后使用 ctypes 调用该 C API,而不是尝试构建更容易并使用 Python.

中的 C++ 对象

您的 C++ 代码将如下所示:

int call_getorder(p_API_GetOrder func, ApiOrder *apiOrderArray, size_t apiOrderCount) {
    std::vector<ApiOrder> vec(apiOrderArray, apiOrderCount);
    ret = func(vec);
    if (ret) return ret;
    std::copy(std::begin(vec), std::end(vec), apiOrderArray);
    return 0;
}

现在,您可以通过创建 an array of 1 ApiOrder(或创建 POINTERApiOrder 并将其传递给 Python 来调用它直接,如果你愿意的话):

orders = (ApiOrder*1)()
ret = mywrapperdll.call_order(mydll.API_GetOrder, byref(order), 1)
if ret:
    do some error handling with either ret or errno 
else:
    do something with order[0]

当然你仍然想要 argtypesrestype