Python的学习(三十二)---- ctypes库的使用整理

Python中ctypes的使用整理

ctypes是Python的一个外部库,提供和C语言兼容的数据类型,可以很方便地调用C DLL中的函数。ctypes的官方文档在这里。

1. ctypes基本数据类型映射表

参数类型预先设定好,或者在调用函数时再把参数转成相应的c_***类型。ctypes的类型对应如下:

ctypes type    C type    Python Type
c_char    char    1-character string
c_wchar    wchar_t    1-character unicode string
c_byte    char    int/long
c_ubyte    unsigned char    int/long
c_bool    bool    bool
c_short    short    int/long
c_ushort    unsigned short    int/long
c_int    int    int/long
c_uint    unsigned int    int/long
c_long    long    int/long
c_ulong    unsigned long    int/long
c_longlong    __int64 or longlong    int/long
c_ulonglong    unsigned __int64 or unsigned long long    int/long
c_float    float    float
c_double    double    float
c_longdouble    long double float    float
c_char_p    char *    string or None
c_wchar_p    wchar_t *    unicode or None
c_void_p    void *    int/long or None
           
对应的指针类型是在后面加上"_p",如int*是c_int_p等等。在python中要实现c语言中的结构,需要用到类。
2. 加载DLL

访问dll,首先需引入ctypes库
from ctypes import *   

假设你已经有了一个的DLL(名字是add.dll),且该DLL有一个符合cdecl(这里强调调用约定是因为,stdcall调用约定和cdecl调用约定声明的导出函数,在使用python加载时使用的加载函数是不同的,后面会有说明)调用约定的导出函数Add。
stdcall调用约定:两种加载方式

Objdll = ctypes.windll.LoadLibrary("dllpath")  
Objdll = ctypes.WinDLL("dllpath")   
cdecl调用约定:也有两种加载方式

Objdll = ctypes.cdll.LoadLibrary("dllpath")  
Objdll = ctypes.CDLL("dllpath")  
其实windll和cdll分别是WinDLL类和CDll类的对象。 

3. 调用DLL方法

加载dll后会返回一个DLL对象,使用其中的函数方法则相当于操作该对象的对应属性。

注意,经过stdcall声明的方法,如果不是用def文件声明的导出函数或者extern “C” 声明的话,编译器会对函数名进行修改

函数参数申明,通过设置函数的argtypes属性

函数返回类型,函数默认返回c_int类型,如果需要返回其他类型,需要设置函数的restype属性

4. 指针与引用

常用的通过调用ctypes类型的指针函数来创建指针实例:

from ctype import *
i = c_int(1)
pi = POINTER(i)
对指针实例赋值只会改变其指向的内存地址,而不是改变内存的内容,与其他语言类似,如需要可改变内容的字符串,可须使用create_string_buffer()

>>> p = create_string_buffer("Hello", 10)  # create a 10 byte buffer
>>> print sizeof(p), repr(p.raw)
10 'Hello/x00/x00/x00/x00/x00'
不带参数的调用指针类型创建一个NULL指针, NULL指针有一个False布尔值

>>> null_ptr = POINTER(c_int)() 
>>> print bool(null_ptr) 
False 
指针实例有一个contents属性,返回这个指针所指向的对象。

另外,byref()是用来传递引用参数,pointer()作为传参通常会创建一个实际的指针对象,当不需要实际指针对象时,则可使用byref()

5. 结构体类型处理

Structures和Unions必须继承Structure和Union基础类,它们都在ctypes模块中定义,每一个子类必须定义个_fields_属性,_fields_是一个二维的tuples列表,包含着每个field的name及type,这field类型必须是一个ctypes类型,如c_int,或者任何其他的继承ctypes的类型,如Structure, Union, Array, 指针等。

例如有一个简单结构,包含两个整型x和y,可如下初始化一个结构:

from ctypes import *
import types
class Test(Structure):
    _fields_ = [('x', c_int),
                ('y', c_char)]
test1 = Test(1, 2)
'
运行运行
另外,如结构体用于链表操作,即包含指向结构体指针时,若直接定义:

from ctypes import *
import types
class Test(Structure):
    _fields_ = [('x', c_int),
                ('y', c_char),
                ('next', Test)]
则python会报错type未定义,:

from ctypes import *
import types
class Test(Structure):
    pass
Test._fields_ = [('x', c_int),
                ('y', c_char),
                ('next', POINTER(Test))]
'
运行运行
 

6. 数组定义

数组包含一个固定数目的相同类型的实例,常用创建数组类型是对一个数据类型与一个正数相乘,例如:

ArrayType = Test * 10
初始化和使用数组:
 
>>> from ctypes import *
>>> TenIntegers = c_int * 10
>>> ii = TenIntegers(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
>>> print ii
<c_long_Array_10 object at 0x...>
>>> for i in ii: print i,
 

7. 回调函数

ctypes可以从python可调用对象中创建一个c可调用的函数指针,这些通常被称为回调函数。

(这个等用到时,再仔细研究。。。)
————————————————

Python 使用ctypes包进行打包和解包(Structure <-> str)

在本文中,我们将介绍如何使用Python的ctypes包进行打包和解包操作。ctypes是Python的外部函数库,它允许Python调用C库中的函数。通过ctypes,我们可以使用Python访问底层的C函数,并进行相应的操作。在本文中,我们将重点介绍如何使用ctypes进行数据结构的打包和解包操作。

阅读更多:Python 教程

ctypes简介

ctypes是Python中用于调用C库的外部函数库,它提供了与C兼容的数据类型和相应的函数。它可以用来调用存在于共享库(.dll、.so)或者C函数库(.lib、.a)中的函数。通过ctypes,我们可以通过Python调用C函数,进行底层的操作。ctypes提供了诸多的函数和类,用于处理不同的数据类型和函数调用方式。在本文中,我们将重点介绍ctypes中的结构体打包和解包操作。

打包操作(Structure -> bytes)

打包操作是将结构体(Structure)中的数据转为字节流(bytes)的过程。在C语言中,结构体是用来组织一组不同类型的数据的,通过结构体,我们可以将相关的数据进行组织和操作。在Python中,我们也可以使用ctypes中的Structure类定义结构体,并将其转换为字节流。

以下是使用ctypes进行打包操作的示例代码:

import ctypes

# 定义结构体
class Point(ctypes.Structure):
    _fields_ = [("x", ctypes.c_int),
                ("y", ctypes.c_int)]

# 创建结构体对象
point = Point(10, 20)

# 将结构体对象转为字节流
packed_data = ctypes.string_at(ctypes.byref(point), ctypes.sizeof(point))
print(packed_data)

Python

Copy

在上述代码中,我们首先导入了ctypes模块,并定义了一个Point结构体,包含了两个整型字段x和y。然后我们创建了一个结构体对象point,并将它转换为字节流packed_data。最后,我们使用print语句输出了packed_data的内容。

运行上述代码,输出结果为:b’\n\x00\x00\x00\x14\x00\x00\x00’。可以看到,结构体对象point被成功转换为了字节流。

解包操作(bytes -> Structure)

解包操作是将字节流(bytes)转换为结构体(Structure)的过程。与打包操作相反,解包操作将字节流还原为结构体对象,方便我们对数据进行操作和访问。

以下是使用ctypes进行解包操作的示例代码:

import ctypes

# 定义结构体
class Point(ctypes.Structure):
    _fields_ = [("x", ctypes.c_int),
                ("y", ctypes.c_int)]

# 创建空的结构体对象
point = Point()

# 解包操作,将字节流转为结构体对象
data = b'\n\x00\x00\x00\x14\x00\x00\x00'
ctypes.memmove(ctypes.byref(point), data, ctypes.sizeof(point))

# 访问解包后的结构体对象
print(point.x, point.y)

Python

Copy

在上述代码中,我们还是首先导入了ctypes模块,并定义了一个Point结构体。然后我们创建了一个空的结构体对象point。接着,我们使用ctypes中的memmove函数将字节流data解包到point中。最后,我们使用print语句输出了point.x和point.y字段的值。

运行上述代码,输出结果为:10 20。可以看到,字节流data被成功解包为了结构体对象point,并且成功访问了其中的字段数据。

三、结构体对应
如果在c中定义了结构体

#define _NL_TC_MAX 20
#define _NL_TC_NAME_MAX 100
typedef struct NLDJ_TC_Out
{
    int dwNum;
    float fScales;
    char model[128];
    char dsClassName[_NL_TC_MAX][_NL_TC_NAME_MAX];
} NLDJ_TC_Out;

则python中要定义成class

class Struct_NLDJ_TC_Out(Structure):
    _fields_ = [("dwNum", c_int),("fScales", c_float), ("model", c_char*128),("dsClassName",c_char * 100 *20)]

//声明一个结构体变量   
djTCVarOut = Struct_NLDJ_TC_Out() 


特别注意:二维数组的时候要倒着写维度

四、指针
函数    说明
byref(x [, offset])    返回 x 的地址,x 必须为 ctypes 类型的一个实例。相当于 c 的 &x 。 offset 表示偏移量。
pointer(x)    创建并返回一个指向 x 的指针实例, x 是一个实例对象。
POINTER(type)    返回一个类型,这个类型是指向 type 类型的指针类型, type 是 ctypes 的一个类型。
byref 很好理解,传递参数的时候就用这个,用 pointer 创建一个指针变量也行,不过 byref 更快。
而 pointer 和 POINTER 的区别是,pointer 返回一个实例,POINTER 返回一个类型。甚至你可以用 POINTER 来做 pointer 的工作:

>>> a = c_int(66)         # 创建一个 c_int 实例
>>> b = pointer(a)        # 创建指针
>>> c = POINTER(c_int)(a) # 创建指针
>>> b
<__main__.LP_c_long object at 0x00E12AD0>
>>> c
<__main__.LP_c_long object at 0x00E12B20>
>>> b.contents            # 输出 a 的值
c_long(66)
>>> c.contents            # 输出 a 的值
c_long(66)


其实前面我们已经有用到了POINTER,看二部分的代码
————————————————

                            版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
                        
原文链接:https://blog.csdn.net/im6520/article/details/105146225

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值