Python使用Ctypes访问动态库

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

  • 4
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值