python调用C接口
这段时间需要用python调用C的接口,网上搜了很多,结合python的官方文档,整理下备用
1、加载dll
from ctypes import * dll = cdll.LoadLibrary('DLL1.dll')#func1 dll = CDLL('DLL1.dll')#func2 print(dll)
2、数据类型的对应

3、函数调用
C
DLL1_API int fnDLL1(void) { return 42; }
Python
print(dll.fnDLL1())
4、参数传递
C
DLL1_API int fnDLL2(int a, float b, double c, const char * buffer,int &d) { printf("recv : %d,%f,%f,%s,\n", a, b, c, buffer); d = 10; return 1; }
int double float 这些类型可以直接传递
char * 直接传递bytes
指针或者引用类型需要用byref或者pointer,也可以用相应类型的指针类型
例如上个接口中传递 int &d 在传递的过程中可以用 byref(temp)
Python
temp = c_int(0) print(dll.fnDLL2(1,c_float(2.0),c_double(3.0),'hell0'.encode('gbk'),byref(temp))) print('byref',temp1.value)
也可以用int的指针类型,这个类型需要自己定义,POINTER一般针对类型
而pointer针对实例化以后的对象,比如上面也可以用pointer(temp)
type_p_int = POINTER(c_int) temp = type_p_int(c_int(0)) print(dll.fnDLL2(1,c_float(2.0),c_double(3.0),'hell0'.encode('gbk'),temp)) print('int *',temp,temp[0],temp.contents)
返回值
int,float,double 这些类型直接接收就可以
其他类型需要先设置接口的返回类型
C
DLL1_API char * fnDLL3(char *buf) { return buf; }
python
dll.fnDLL3.restype = c_char_p res = dll.fnDLL3('hello'.encode('gbk')) print(res,type(res))
如果传递的是char * 需要改变其内容,需要预先定义好存储空间
C
DLL1_API int fnDLL4(char *buf, size_t buffsize) { printf("%s\n", buf); memset(buf, 0, buffsize); sprintf(buf, "world"); return 1; }
python
buf = create_string_buffer('hello'.encode('gbk'),10) dll.fnDLL4(byref(buf),10) print(buf.value)
unicode类型
C
DLL1_API WCHAR * fnDLL10(WCHAR * buf,size_t bufsize) { wprintf(L"wchar:%s\n", buf); wmemset(buf, 0, bufsize); wsprintf(buf, L"hello world\n"); return buf; }
python
wbuf = create_unicode_buffer("hello",32) dll.fnDLL10.restype = c_wchar_p res = dll.fnDLL10(byref(wbuf),32) print("wchar--",res)
5、结构体定义
我们用 fields = [(‘name1’,type1),(‘name2’,type2)]来表示结构体的成员
字节对齐 C结构体中经常会出现按照指定的字节进行对齐结构体,用pack来指定对齐的字节数,数组的定义直接用 *num 表示个数
C
我们用 fields = [(‘name1’,type1),(‘name2’,type2)]来表示结构体的成员
字节对齐 C结构体中经常会出现按照指定的字节进行对齐结构体,用pack来指定对齐的字节数,数组的定义直接用 *num 表示个数
C
#pragma pack(1) struct MyStruct { int a; double b; char c[32]; }; #pragma pack()
python
class MyStruct(Structure): _fields_ = [ ('a',c_int), ('b',c_double), ('c',c_char*32), ] _pack_ = 1
位域
C
struct MyStruct1 { int a : 16; int b : 16; };
python
class MyStruct1(Structure): _fields_ = [ ('a',c_int,16), ('b', c_int, 16), ]
结构体的嵌套
c
struct MyStruct2 { int a; MyStruct S[4]; };
python
class MyStruct2(Structure): _fields_ = [ ('a',c_int), ('struct',MyStruct*4) ]
传递结构体,与之前传递参数一样,指针类型用byref或者pointer
c
DLL1_API int fnDLL5(MyStruct & s) { printf("mystruct:\na:%d\nb:%f\nc:%s\n", s.a, s.b, s.c); return 1; }
python
mystruct = MyStruct() mystruct.a = 1 mystruct.b = 1.0 mystruct.c = 'helloworld'.encode('gbk') dll.fnDLL5(byref(mystruct)) dll.fnDLL5(pointer(mystruct))
返回结构体,与之前相同,需要指定返回的类型
c
DLL1_API MyStruct fnDLL6() { MyStruct *tem = new MyStruct; tem->a = 10; tem->b = 20; sprintf(tem->c, "hello"); return *tem; }
python
dll.fnDLL6.restype = MyStruct res = dll.fnDLL6() print(res) print('mystruct:', res.a, res.b, res.c) del res
高阶数组的定义
int my_array[10][10];
# 先定义一个数组类型 type_int_array_10 = c_int * 10 # 定义数组的数组(即二维数组) type_int_array_10_10 = type_int_array_10 * 10 # 创建二维数组对象 my_array = type_int_array_10_10() # 使用二维数组 my_array[1][2] = 3
字节流与结构体的相互转换
#pack print(string_at(addressof(mystruct),sizeof(mystruct))) #unpack buf = bytes(sizeof(MyStruct)) assert len(buf) buf = create_string_buffer(sizeof(MyStruct)) res = cast(pointer(buf),POINTER(MyStruct)).contents print(res,type(res)) print('mystruct:',res.a,res.b,res.c)
def Pack(ctype_instance): return string_at(addressof(ctype_instance),sizeof(ctype_instance)) def UnPack(ctype,buf): assert sizeof(ctype) == len(buf) cstring = create_string_buffer(buf) return cast(pointer(cstring),POINTER(ctype)).contents
回调函数
先用CFUNCTYPE 定义回调函数类型,参数的第一个参数为返回值类型
后面的参数为回调函数传递的参数类型,然后定义python中的函数,
C
typedef int (*callbakc) (int a, int b); DLL1_API void fnDLL7(int a, int b, callbakc func) { int n = func(a, b); printf("c++ callback %d\n", n); }
python
CMPFUNC = CFUNCTYPE(c_int,c_int,c_int)
cmp_func = CMPFUNC(callFunc) dll.fnDLL7(1,2,cmp_func)
这里有个地方特别注意,如果回调函数中有void* ,char等类型,在python中定义回调函数的时候如果定义为 c_void_p ,c_char_p,实际返回的数据为int,bytes
这时候其实python内部已经把参数的值拿出来了,而我们需要的是char地址的内容,常用的比如传递某一串字节流,我们需要传递出字节流的长度和首地址的指针,如果直接使用参数,c_void_p拿到的是一个int类型,而c_char_p拿到的是截止到最后一个'\0'的字节,最终我们在python中用string_at 来拿到实际的字节流
这时候其实python内部已经把参数的值拿出来了,而我们需要的是char地址的内容,常用的比如传递某一串字节流,我们需要传递出字节流的长度和首地址的指针,如果直接使用参数,c_void_p拿到的是一个int类型,而c_char_p拿到的是截止到最后一个'\0'的字节,最终我们在python中用string_at 来拿到实际的字节流
c回调
typedef void (*callbakc) (void * buf, int &buf_size);
python中的定义
string = string_at(buf,size.value)
def callback(buf,size): string = string_at(buf,size.value) CALLBACKFUNC = CFUNCTYPE(None,c_void_p,c_int) call = CALLBACKFUNC(callback)
浙公网安备 33010602011771号