這段時間需要用python調(diào)用C的接口催什,網(wǎng)上搜了很多涵亏,結(jié)合python的官方文檔,整理下備用
1蒲凶、加載dll
from ctypes import *
dll = cdll.LoadLibrary('DLL1.dll')#func1
dll = CDLL('DLL1.dll')#func2
print(dll)
2气筋、數(shù)據(jù)類型的對應(yīng)
3、函數(shù)調(diào)用
C
DLL1_API int fnDLL1(void)
{
return 42;
}
Python
print(dll.fnDLL1())
4旋圆、參數(shù)傳遞
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宠默,也可以用相應(yīng)類型的指針類型
例如上個接口中傳遞 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 這些類型直接接收就可以
其他類型需要先設(shè)置接口的返回類型
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 * 需要改變其內(nèi)容抹沪,需要預(yù)先定義好存儲空間
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、結(jié)構(gòu)體定義
我們用 fields = [(‘name1’,type1),(‘name2’,type2)]來表示結(jié)構(gòu)體的成員
字節(jié)對齊 C結(jié)構(gòu)體中經(jīng)常會出現(xiàn)按照指定的字節(jié)進行對齊結(jié)構(gòu)體瓤球,用pack來指定對齊的字節(jié)數(shù)融欧,數(shù)組的定義直接用 *num 表示個數(shù)
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),
]
結(jié)構(gòu)體的嵌套
c
struct MyStruct2
{
int a;
MyStruct S[4];
};
python
class MyStruct2(Structure):
_fields_ = [
('a',c_int),
('struct',MyStruct*4)
]
傳遞結(jié)構(gòu)體,與之前傳遞參數(shù)一樣卦羡,指針類型用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))
返回結(jié)構(gòu)體噪馏,與之前相同,需要指定返回的類型
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
高階數(shù)組的定義
int my_array[10][10];
# 先定義一個數(shù)組類型
type_int_array_10 = c_int * 10
# 定義數(shù)組的數(shù)組(即二維數(shù)組)
type_int_array_10_10 = type_int_array_10 * 10
# 創(chuàng)建二維數(shù)組對象
my_array = type_int_array_10_10()
# 使用二維數(shù)組
my_array[1][2] = 3
字節(jié)流與結(jié)構(gòu)體的相互轉(zhuǎn)換
#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
回調(diào)函數(shù)
先用CFUNCTYPE 定義回調(diào)函數(shù)類型绿饵,參數(shù)的第一個參數(shù)為返回值類型
后面的參數(shù)為回調(diào)函數(shù)傳遞的參數(shù)類型欠肾,然后定義python中的函數(shù),
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)
這里有個地方特別注意蝴罪,如果回調(diào)函數(shù)中有void* 董济,char等類型,在python中定義回調(diào)函數(shù)的時候如果定義為 c_void_p ,c_char_p要门,實際返回的數(shù)據(jù)為int虏肾,bytes
這時候其實python內(nèi)部已經(jīng)把參數(shù)的值拿出來了,而我們需要的是char地址的內(nèi)容欢搜,常用的比如傳遞某一串字節(jié)流封豪,我們需要傳遞出字節(jié)流的長度和首地址的指針,如果直接使用參數(shù),c_void_p拿到的是一個int類型炒瘟,而c_char_p拿到的是截止到最后一個'\0'的字節(jié)吹埠,最終我們在python中用string_at 來拿到實際的字節(jié)流
c回調(diào)
typedef void (*callbakc) (void * buf, int &buf_size);
python中的定義
def callback(buf,size):
string = string_at(buf,size.value)
CALLBACKFUNC = CFUNCTYPE(None,c_void_p,c_int)
call = CALLBACKFUNC(callback)
OK,應(yīng)該基本都講到了疮装,后續(xù)有遇到的坑再繼續(xù)填