1 加载库
ctypes是python的内置组件了,不需要额外安装下载,我们整理好自己的动态库资源文件,直接加载就可以了:
import ctypes
instance = ctypes.cdll.LoadLibrary(so_or_dll_path)
说明:以下代码中使用的instance都为上面代码块中的对象。
2 使用
ctypes.POINTOR用于包裹类型,ctypes.pointor用于包裹对象实例。
举个例子:
定义了一个python类,class Person(ctypes.Structure)
person = Person()
ctypes.pointor(person)是对的;
ctypes.POINTOR(Person)是对的。
ctypes.pointor(Person)是错的;
ctypes.POINTOR(person)是错的。
2.1 定义
2.1.1 定义枚举
import enum
class Sex(enum.Enum):
UNKNOW = -1
FEMALE = 0
MALE = 1
枚举实际上是为了方便在python中使用,如果需要传入到c接口中,需要拿到其值,例:
instance.set_sex(Sex.FEMALE.value)
2.1.2 定义结构体
c结构体原型:
struct Person
{
int id;
char name[64];
int sex;
bool is_married;
int friend_ids[4];
};
python结构体
import ctypes
class Person(ctypes.Structure):
_fields_ = [("id", ctypes.c_int),
("name", ctypes.c_char*64),
("sex", ctypes.c_int),
("is_married", ctypes.c_bool),
("friend_ids", ctypes.c_int*4),]
2.1.3 定义回调
c回调原型
// 查找某个用户的返回
typedef void (*on_search_person_by_id)(int errorcode,
stuct Person person,
const char *message,
unsigned char* ext_info,
unsigned int info_length,
void *user_context);
// 查找某个用户的好友列表的返回
typedef void (*on_search_person_friends)(int errorcode,
stuct Person* firends,
unsigned int friend_count,
const char *message,
void *user_context);
python回调
import ctypes
# from xxx import Person
# 查找某个用户的返回
CBOnSearchPersonByID = ctypes.CFUNCTYPE(None,
Person,
ctyps.c_char_p,
ctypes.POINTER(ctypes.c_ubyte),
ctyps.c_uint,
ctypes.c_void_p)
def on_search_person_by_id(errorcode,person,message,ext_info,info_length,user_context):
bytes_ext_info = bytes(ext_info[:info_length])
print(f"person name : {person.name.decode('utf-8')}")
# 查找某个用户的好友列表的返回
CBOnSearchPersonFriends = ctypes.CFUNCTYPE(None,
ctypes.POINTOR(Person),
ctyps.c_uint,
ctyps.c_char_p,
ctypes.c_void_p)
def on_search_person_by_id(errorcode,friends,count,message,user_context):
struct_arr = ctypes.cast(friends,ctypes.POINTER(Person*count)).contents
print("show all friends:")
for item in struct_arr:
print(f"name : {item.name.encode('utf-8')}")
2.2 调用函数
2.2.1 传入二级指针
c函数原型:void get_version(const char **version)
version = ctypes.c_char_p()
instance.get_version(ctypes.byref(version))
print(f"version : {version.value.decode('utf-8')}")"
2.2.2 传入结构体
c函数原型:void add_friend(int person_id,struct Person* person)
friend = Person(1233,b"张三",Sex.MALE.value,ctypes.c_bool(True))
instance.add_friend(person_id,ctypes.pointor(friend))
2.2.3 传入结构体数组
c函数原型:void add_friends(int person_id,struct Person* person,unsigned int count)
friends = (Person*3)()
friends[0] = Person(1233,b"张三",Sex.MALE.value,ctypes.c_bool(True))
friend[1] = Person(1234,b"李四",Sex.MALE.value,ctypes.c_bool(True))
friend[2] = Person(1235,b"王五",Sex.MALE.value,ctypes.c_bool(True))
instance.add_friends(person_id,ctypes.pointor(ctypes.byref(friends)),3)
2.2.4 大量数据交互
2.2.4.1 python层传入动态库
c函数原型:void get_photo(int person_id, unsigned char* photo_data, unsigned int length)
photo_data:为实际的图像数据,需要python层分配空间,动态库写入数据
# 这里假定图片大小为2M
size = 2*1024*1024
photo_data = bytes(size)
photo_data_pointor = ctypes.cast(photo_data,ctypes.POINTER(ctypes.c_ubyte))
instance.get_photo(person_id,photo_data_pointor ,size)
如果需要使用动态库发送数据,也是同样的,只不过这次的数据由外部写入。
c函数原型:void set_photo(int person_id, unsigned char* photo_data, unsigned int length)
photo_data:为实际的图像数据,python层分配空间,动态库消费
# 这里假定图片大小为2M
with oepn("photo.jpg","rb") as f:
photo_data = f.read()
size = len(photo_data)
photo_data_pointor = ctypes.cast(photo_data,ctypes.POINTER(ctypes.c_ubyte))
instance.set_photo(person_id,photo_data_pointor ,size)
2.2.4.2 动态库吐出到python层
假设我们的获取人员图片的方法修改为了异步获取方式:
// 查找某个用户的返回
typedef void (*on_person_photo_data)(int errorcode,
int person_id,
unsigned char* photo_data,
unsigned int photo_data_length,
void *user_context);
异步回调:
CBOnPersonPhotoData = ctypes.CFUNCTYPE(None,
ctypes.c_int,
ctypes.POINTER(ctypes.c_ubyte),
ctyps.c_uint,
ctypes.c_void_p)
def on_person_photo_data(errorcode,person_id,photo_data,photo_data_length,user_context):
bytes_photo_data = bytes(photo_data[:photo_data_length])
with open("photo.jpg","wb") as f:
f.write(bytes_photo_data)
c函数原型:
void get_photo_async(int person_id, on_person_photo_data callback)
photo_data:为实际的图像数据,需要python层分配空间,动态库写入数据
实际调用:
# from xxx import CBOnPersonPhotoData,on_person_photo_data
on_person_photo_data_for_c = CBOnPersonPhotoData(on_person_photo_data)
instance.get_photo_async(person_id,on_person_photo_data_for_c)
注意:这里on_person_photo_data_for_c 要注意生命周期,需要保持在返回前不会被回收释放,否则会发生crash