动态库封装成python模块
之前的文章 将静态库封装成 python 模块 中讲解了如何将静态库封装成python模块,静态库封装相对来说还是有点复杂,今天来介绍下动态库封装成python模块的方法。
主要方法
将动态库封装成Python的主要方法有以下几种:
使用ctypes库:ctypes是Python标准库中用于调用动态库的工具库。它提供了一组用于描述C数据类型和调用C函数的功能。你可以使用ctypes库来加载动态库并调用其中的函数、访问结构体等。通过定义C函数的原型和C结构体的映射,可以方便地与动态库进行交互。
使用Cython:Cython是一个Python的扩展语法,允许你编写使用C语言语法的Python扩展模块。通过使用Cython,你可以将动态库中的函数包装为Python的可调用函数,并且可以直接在Python代码中使用这些函数,而无需编写额外的C代码。
使用SWIG:SWIG(Simplified Wrapper and Interface Generator)是一个自动化工具,用于生成连接C/C++和其他高级语言的接口代码。它可以根据给定的接口描述文件自动生成Python的封装代码。SWIG支持多种语言,包括Python和C/C++,使得将动态库封装成Python模块变得更加简单和高效。
哈哈,其实跟封装静态库差不多,用Cython或SWIG,既能封装静态库,也能封装动态库,但我们今天主要用ctypes
,简便、主流。
ctypes
当使用ctypes
封装动态库时,以下是一般的步骤和方法:
- 导入
ctypes
模块:首先,在Python脚本中导入ctypes
模块,以便使用其中的功能。例如:
1 | import ctypes |
- 加载动态库:使用
ctypes
提供的函数加载动态库文件。有几种方法可以加载动态库:
- 使用
cdll
加载动态库:
1 | mylib = ctypes.CDLL("mylib.so") # Linux/macOS |
- 使用
windll
加载Windows动态库:
1 | mylib = ctypes.windll.LoadLibrary("mylib.dll") |
- 使用
oledll
加载Windows OLE动态库:
1 | mylib = ctypes.oledll.LoadLibrary("mylib.dll") |
在加载动态库后,可以通过mylib
对象访问其中定义的函数和变量。
- 定义函数原型:为了能够正确地调用动态库中的函数,需要在Python中定义与动态库函数相对应的函数原型。可以使用
ctypes
提供的CFUNCTYPE
函数来定义函数原型,指定参数类型和返回类型。例如:
1 | my_function = mylib.my_function |
在上述示例中,my_function
是动态库中的函数名,argtypes
属性指定函数的参数类型列表,restype
属性指定函数的返回类型。
- 调用动态库函数:使用Python中定义的函数原型来调用动态库中的函数。可以直接像调用普通函数一样调用动态库函数,传递相应的参数即可。例如:
1 | result = my_function(10, 3.14) |
在调用函数时,确保传递正确的参数类型和顺序,以及处理返回值的类型和内容。
- 使用动态库中的结构体:如果动态库中定义了结构体,可以在Python中使用
ctypes
定义相应的结构体,以便与动态库进行交互。使用ctypes
提供的Structure
类来定义结构体,并指定其成员变量的类型和顺序。例如:
1 | class MyStruct(ctypes.Structure): |
在上述示例中,MyStruct
是自定义的结构体名称,_fields_
属性指定了结构体的成员变量和类型。
可以使用定义好的结构体来传递参数或接收动态库函数的返回值。
这是使用ctypes
封装动态库的基本方法。根据具体的动态库和需求,可能需要更多的配置和处理,例如处理指针、处理字符串、处理数组等。ctypes
提供了丰富的功能和类型,可以根据需要进行进一步的学习和使用。
实例
前面的文章 动态库封装成 python 模块 介绍了将动态库封装成python模块的方法,今天就以最近在弄的ch347
芯片的驱动为例,实践下。
沁恒在ch347的资料页并没有提供ch347驱动的动态库,而是在CH347EVT.ZIP
中提供了静态库。后来在跑demo时提示找不到动态库,搜了一下,原来这个动态库在CH341PAR.ZIP
里。沁恒提供的驱动库文件主要有以下几个:
CH347DLL.H
: 驱动库中文头文件CH347DLL_EN.H
: 驱动库英文头文件CH347DLL.LIB
: 32位静态库CH347DLLA64.LIB
: 64位静态库CH347DLL.DLL
: 32位动态库CH347DLLA64.DLL
: 64位动态库
下面以CH347OpenDevice(ULONG DevI)
, CH347CloseDevice(ULONG iIndex)
, CH347GetDeviceInfor(ULONG iIndex,mDeviceInforS *DevInformation)
, CH347GetVersion(ULONG iIndex, PUCHAR iDriverVer, PUCHAR iDLLVer, PUCHAR ibcdDevice, PUCHAR iChipType)
4个函数的封装为例。
首先新建项目文件夹ch347-py
,
1 | mkdir ch347-py && cd ch347-py |
然后将驱动库文件复制到项目文件夹中,因为今天是以动态库进行封装,所以只复制dll文件即可,头文件复制过来方便查看库函数声明和数据结构。注意动态库的位数要跟系统和python对应,不确定的话可以32位和64位都放进来,后面试一下再确定用哪个版本。
接着用vs code打开项目文件夹,并新建一个ch347.py
文件,写入以下内容:
1 | import ctypes |
这样就将动态库中的4个函数封装好了。接下来新建一个测试文件test.py
,写入以下内容:
1 | import ch347 |
连接ch347板子,运行一下:
1 | ❯ python test.py |