提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档
python--ctypes库调用初体验
前言
主要是记录python调用C接口一些注意事项。
ctypes传送门:ctypes库
环境是linux。
一、库的环境变量设置
一般在登陆用的home目录下更改
vim .profile
export LF_LIBRARY_PATH="so文件目录"
souce .profile
二、初识ctypes
加载库:
path = os.path.abspath(so文件的绝对路径)#
my_cdll = cdll.LoadLibrary(path)
my_cdll.接口名()
2.1 数据对应关系
2.2 指针
2.2.1 传递指针
若一个接口API如下:
如果接口中存在VOID则不需要传参,TEST(VOID)
TEST_API int TEST(IN int resolution_type,
OUT int * width,
OUT int * height);
输出参数需要指针接收,则可使用pointer(),查看pointer函数:
# The real ctypes.pointer is a function, not a class. The stub version of pointer behaves like
# ctypes._Pointer in that it is the base class for all pointer types. Unlike the real _Pointer,
# it can be instantiated directly (to mimic the behavior of the real pointer function).
class pointer(Generic[_CT], _PointerLike, _CData):
_type_: ClassVar[Type[_CT]] = ...
contents: _CT = ...
def __init__(self, arg: _CT = ...) -> None: ...
@overload
def __getitem__(self, i: int) -> _CT: ...
@overload
def __getitem__(self, s: slice) -> List[_CT]: ...
@overload
def __setitem__(self, i: int, o: _CT) -> None: ...
@overload
def __setitem__(self, s: slice, o: Iterable[_CT]) -> None: ...
ctypes中使用POINTER和pointer表示指针,在使用POINTER时需要设置指向的数据类型,而pointer则直接从变量中得到一个特定类型的指针。
POINTER相当于只是申明数据类型,真正传递是需要pointer()
示例:
class XXXXX(SdkBase):
def run(self):
self.cdll.TEST.argtype = (c_int,POINTER(c_int),POINTER(c_int))#定义输入数据类型
self.cdll.TEST.restype = c_int#定义返回数据类型
self.width = c_int()
self.height = c_int()
code = self.cdll.TEST(self.resolution_type,pointer(self.width),pointer(self.height))
2.3 结构体
结构体和联合必须继承自 ctypes 模块中的 Structure 和 Union 。子类必须定义 fields 属性。 fields 是一个二元组列表,二元组中包含 field name 和 field type 。
type 字段必须是一个 ctypes 类型,比如 c_int,或者其他 ctypes 类型: 结构体、联合、数组、指针。
from ctypes import *
class struct_Config(Structure):
_fields_ = [
('addr', (c_char * 32)),
('port', c_uint16),
('username', (c_char * 32)),
('password', (c_char * 32)),
('chan_no', c_uint16),
('chan_profile', c_uint16),
('model', (c_char * 32)),
('rtsp_url', (c_char * 512))
]
class union_IotDeviceConfig(Union):
_fields_ = [#联合模式
('url', struct_Config)
]
class Test:
#struct_IotIpcConfig结构体
_arg = struct_IotIpcConfig()
_arg.addr = (args.get('addr', '')).encode('utf-8')
_arg.port = int(args.get('port', 0))
_arg.username = (args.get('username', '')).encode('utf-8')#如果是字符串则需要编码
2.4 数组
char addr[IOT_LEN_ADDR] ---> ('addr', (c_char * 32))
2.5 回调函数
CFUNCTYPE() 工厂函数使用 cdecl 调用约定创建回调函数类型。在 Windows 上, WINFUNCTYPE() 工厂函数使用 stdcall 调用约定为回调函数创建类型。
IOT_API int STDCALL TEST(IN CALLBACK cb,
IN void * user_data);
class Test_s(SdkBase):
def __init__(self, args=None):
CALLBACK = CFUNCTYPE(c_int, POINTER(struct_Info),c_int,c_char_p)#定义回调函数中需要的参数
self.system_define_cb = CALLBACK(self.system_define_cb_func)#定义函数名
def run(self):
self.cdll.IOT_GetDeviceList.argtype = (c_int,c_char_p)
self.cdll.IOT_GetDeviceList.restype = c_int
code = self.cdll.TEST(self.system_define_cb,self.user_data)#调用API
return self.result(code, {'result': self.res})
def result(self, code, res=None):
return super().result(code, res)
def system_define_wait_func(self):
time.sleep(2)
def system_define_cb_func(self, dev_list, dev_num, user_data):
return self.real_cb_func(dev_list,dev_num,user_data)
def real_cb_func(self, dev_list,dev_num, user_data):
return 0