@[TOC](python ctypes调用C++ dll,arry(数组)的相关操作)
前言
本人新手python一枚,最近工作中需要用到python 调用C++库,一个数组调用,花费了太多时间,遂决定记录下来,防止忘记。
调用原型:
#MAX_ID_LEN_PER_THREAD 65
DWORD QueryConnectedDeviceList(int *iChNum,char szDevList[][MAX_ID_LEN_PER_THREAD],int itimeout)
C++下使用:
char devList[32][65]={0};
int iChNum=0;
res=QueryConnectedDeviceList(&iChNum,devList,30);
使用python提供的ctypes库调用C++,如果想直接看结果,请拉到最后。
这部分讨论 ctypes.Arry的个人理解
首先是官方的资料,读完一脸懵逼。ctypes
class ctypes.Array(*args)
Abstract base class for arrays.
The recommended way to create concrete array types is by multiplying any ctypes data type with a positive integer. Alternatively, you can subclass this type and define _length_ and _type_ class variables. Array elements can be read and written using standard subscript and slice accesses; for slice reads, the resulting object is not itself an Array.
_length_
A positive integer specifying the number of elements in the array. Out-of-range subscripts result in an IndexError. Will be returned by len().
_type_
Specifies the type of each element in the array.
Array subclass constructors accept positional arguments, used to initialize the elements in order.
然后网上找了一些资料,尝试了一下,如下是可以生成一个数组
def make_array(ctype,arr)->ctypes.Array:
return (ctype * len(arr))(*arr)
第一次我想到的是用char*的数组调用dll,能成功调用并放回正确值,但当我访问数组的值却发生了异常。
代码如下:
# -*- coding: utf-8 -*-
from ctypes import *
def make_array(ctype,arr):
return (ctype * len(arr))(*arr)
lib = CDLL("./UCCommonLib.dll") # 加载动态库,LoadLibrary
# note:ctypes调用和返回值默认为int --》即没有特别需要,DWORD返回类型不需要特定指定返回类型
# 如果类型不为int,则必须指定类型
lib.QueryConnectedDeviceList.argtypes =[POINTER(c_int),c_char_p * 32,c_int]
ChNum = c_int(0)
arr = [b""]*32
szDevList = make_array(c_char_p,arr)
print(szDevList)
print(szDevList[0])
res = lib.QueryConnectedDeviceList(byref(ChNum),szDevList,30)
print("res:",res)
print("ChNum:",ChNum.value)
print(szDevList)
print(szDevList[0])
print("调用完成")
运行结果:
<__main__.c_char_p_Array_32 object at 0x00E393D0>
b''
res: 0
ChNum: 2
<__main__.c_char_p_Array_32 object at 0x00E393D0>
Process finished with exit code -1073741819 (0xC0000005)
调用C++ dll前szDevList[0]是可以访问,调用后再打印szDevList[0]就报错了,无奈pycharm在ctypes下不能用debug模式,就只能自己去猜问题出在哪里。
1.怀疑是make_array方法没能生成一个以65 char字节对齐的数组,因为我生成的是一个32数组大小的char *。
按照这个想法,我直接生成一个 65 * 32 *sizeof(char) 大小的数组去调用。
# -*- coding: utf-8 -*-
from ctypes import *
def make_array(ctype,arr):
return (ctype * len(arr))(*arr)
lib = CDLL("./UCCommonLib.dll") # 加载动态库,LoadLibrary
lib.QueryConnectedDeviceList.argtypes =[POINTER(c_int),c_char_p ,c_int]
ChNum = c_int(0)
szDevList = create_string_buffer(b"\0", 65*sizeof(c_char) * 32)
print(szDevList)
res = lib.QueryConnectedDeviceList(byref(ChNum),szDevList,30)
print(szDevList)
print(szDevList.raw)
print(szDevList.value)
print("调用完成")
结果:
<ctypes.c_char_Array_2080 object at 0x01819418>
QueryConnectedDeviceList
find device info:COM-1|3
find device info:COM-1|4
szDevList:COM-1|3
szDevList:COM-1|4
<ctypes.c_char_Array_2080 object at 0x01819418>
b'COM-1|3\x00\x00\x00\x00\x00\x00\x00\x00COM-1|4\x00\x00\x00\x00' # 此次省略掉若干输出
b'COM-1|3'
调用完成
我本身需要的是 szDevList包含 COM-1|3、COM-1|4两个信息,如果我使用
print(szDevList[65].raw)
output:
Traceback (most recent call last):
File "ucdebugger.py", line 69, in <module>
print(szDevList[65].raw)
AttributeError: 'bytes' object has no attribute 'raw'
这又会让我进入另个坑中。
再次静下心来,认真分析
def make_array(ctype,arr):
return (ctype * len(arr))(*arr)
__main__.c_char_p_Array_32 object at 0x00E393D0
ctypes.c_char_Array_2080 object at 0x01819418
我得到了重要的结论:
def make_array(ctype,arr)->ctypes.Array
ctype * len(arr) 返回一个ctypes类型的数组, (*arr)则是构造这个类型的一个实例
c_int * 5 返回一个 c_long_Array_5类型,即 int[5]
list = [1,2,3,4,5]
(c_int * 5)(*list) 即构造了一个实列对象,即 int a[5] = {1,2,3,4,5}
按照这个想法,我开始构造二维数组。
arrType = (c_char * 65) * 32
print(arrType)
output:
c_char_Array_65_Array_32
看到这个输出,就知道该怎么做了,以下是完整代码
# -*- coding: utf-8 -*-
import ctypes
from ctypes import *
# note:ctypes调用返回值默认为int --》即没有特别需要,DWORD返回类型不需要特定指定返回类型
def make_array(ctype,arr)->ctypes.Array:
return (ctype * len(arr))(*arr)
lib = CDLL("./UCCommonLib.dll") # 加载动态库,LoadLibrary
arrType = (c_char * 65) * 32
print(arrType)
lib.QueryConnectedDeviceList.argtypes =[POINTER(c_int),arrType,c_int]
ChNum = c_int(0)
str65 = create_string_buffer(b"0", 65*sizeof(c_char))
arr = [str65]*32
szDevList = make_array(c_char*65,arr)
print(szDevList)
res = lib.QueryConnectedDeviceList(byref(ChNum),szDevList,30)
print("res:",res)
print("ChNum:", ChNum.value)
# print(values.raw)
for one in szDevList:
print(one.value)
print("调用完成")
结果:
<class '__main__.c_char_Array_65_Array_32'>
<__main__.c_char_Array_65_Array_32 object at 0x01EB95C8>
QueryConnectedDeviceList
find device info:COM-1|3
find device info:COM-1|4
szDevList:COM-1|3
szDevList:COM-1|4
res: 0
ChNum: 2
b'COM-1|3'
b'COM-1|4'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
b'0'
调用完成
Process finished with exit code 0