最全ctypes用法总结

最全ctypes⽤法总结
注:原⽂链接因作者写到了富⽂本上,故此发表,版权归原作者所有 ctypes是Python的⼀个外部库,提供和C语⾔兼容的数据类型,可以很⽅便地调⽤DLL中输出的C接⼝函数
1.加载dll和取出函数
from ctypes import*
dll = cdll.LoadLibrary(dllpath)#dllpath是字符串
dll = windll.LoadLibrary(dllpath)
上⾯两⾏使⽤哪⼀⾏,取决于导出函数的调⽤规范(cdecl或stdcall).也可以使⽤下⾯两⾏代替:
dll = CDLL(dllpath)#注意和上⾯⼤⼩写的区别
dll = WinDLL(dllpath)
注意,这⾥使⽤的dll必须和python平台匹配,⽐如都是32位的或者都是64位的。因为本质上是⼀个exe加载⼀个dll,⽆法跨平台。汝州市
加载dll后,可直接得到dll中的导出函数地址.
func = dll.func_name  #func_name 是dll的导出函数
有时动态链接库导出c++函数时,并不是有效的Python标识符,例如 “??2@YAPAXI@Z” 。这种情况下,必须使⽤getattr 获取函数:
func =getattr(cdll.msvcrt,"??2@YAPAXI@Z")
在Windows上,有些动态链接库导出函数不是⽤名字,⽽是⽤序号(ordinal)。这时需通过索引获取:
func = cdll.kernel32[1]
2.函数参数和返回值
上⾯只是得到了函数地址,还⽆法进⾏函数调⽤.要进⾏正确的函数调⽤,需设置好参数和返回值类型.
ctypes⽀持的原⽣数据类型如下:
ctypes类型  C 类型Python 类型c_char char1-character string
c_wchar wchar_t1-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
ctypes类型  C 类型Python 类型
c_char_p char *string or None
c_wchar_p wchar_t *unicode or None
c_void_p void *int/long or None
设置函数的参数类型使⽤函数的argtypes属性,直接赋值为⼀个ctypes类型的列表或元组。设置函数的返回值类型使⽤函数的restype属性。下⾯是⽰例代码:
python中,默认函数返回值是c_int型,此类型可以不⽤显⽰设置函数的restype属性,如果是参数类型是c_int型则需要设置。
fun.argtypes =(c_int, c_int,c_int,c_void_p)#设置函数参数类型为 int,int,int,void *
None、整数、字节串和(unicode)字符串是可以作为本地Python对象直接传递给函数调⽤的。
None是作为C的NULL指针传递。
字节串和字符串作为内存块指针传递(char* 或 wchar_t*)。
Python整数作为平台相关的C语⾔int类型传递,其值会截断到C类型。
除了整数、字节串和字符串以外Python类型的参数传递,必须使⽤ctypes类型做包装。
在调⽤函数时,如果使⽤了错误的参数数量和调⽤规范时,ctypes尝试保护调⽤。不幸的是该功能仅在Windows上有⽤。它通过检查函数返回栈来实现,所以尽管发⽣了错误,但是函数还是调⽤了。
这很容易导致当前使⽤的整个Python环境崩溃,所以必须很⼩⼼的使⽤。
除了上述的基本类型,ctypes还⽀持⾃定义的结构体和联合体,它们可以出现在函数的参数或返回值中。
3.结构体
⾃定义的结构体和联合体必须继承⾃ctypes的Structure和Union,这两个类都在ctypes模块中定义。每
⼀个⼦类必须定义"_fields_“属性,”_fields_"是⼀个⼆维的tuples列表,
描述类的每个数据成员的字段名和字段类型,这⾥的字段类型必须是⼀个ctypes类型,如c_int,或者任何其他的继承ctypes的类型,如Structure, Union, Array, 指针等。
例如有⼀个简单结构,包含两个整型x和y,可如下初始化⼀个结构:
from ctypes import*
import types
class Point(Structure):
_fields_ =[('x', c_int),
('y', c_int)]
p1 = Point(1,2)
贵池红盾网print(point.x, point.y)#输出 1 2
#可以创建复杂的结构体,嵌套了其它结构体。如下:
class RECT(Structure):
_fields_ =[("upperleft", POINT),
("lowerright", POINT)]
rc = RECT(p1)
print(rc.upperleft.x, rc.upperleft.y)#输出 1 2
print(rc.lowerright.x, rc.lowerright.y)#输出 0 0
#嵌套结构体可以通过下⾯多种⽅法初始化:
rc2 = RECT(POINT(1,2), POINT(3,4))
rc3 = RECT((1,2),(3,4))
如结构体⽤于链表操作,即包含指向结构体指针时,若直接定义:
新旧巴塞尔协议import types
class Test(Structure):
_fields_ =[('x', c_int),
('y', c_char),
('next', Test)]#这⼀⾏报错
则python会报错type未定义,如下定义则OK:
from ctypes import*
import types
class Test(Structure):
pass
Test._fields_ =[('x', c_int),
('y', c_char),
('next', POINTER(Test))]
字节对齐和字节顺序
默认情况下结构体和联合的对齐使⽤C编译器相同的⽅式。这可以通过类属性_pack_ 来重载其⾏为。这必须设置⼀个正数指定字段的最⼤对齐。这个功能与MSVC中的 #pragma pack(n) 功能⼀样。
ctypes中的结构体和联合使⽤本地字节序。想要⽤⾮本地字节序,可以使⽤ BigEndianStructure 、LittleEndianStructure 、BigEndianUnion 、 LittleEndianUnion 基类。这些类⽆法包含指针字段。
位域
创建结构与联合体时,可以包含位域字段。只有整型域才可以使⽤位字段,位宽可以在_fields_元组的第三个选项中指定:
class Int(Structure):
_fields_ =[("first_16", c_int,16),#这个字段占16位
("second_16",c_int,16)]
数组就是序列,包含固定数量的相同类型的实例。推荐的创建数组类型的⽅式是使⽤正数和乘号应⽤到类型:
class POINT(Structure):
_fields_ =[("x", c_int),("y", c_int)]
TenPointsArrayType=POINT*10#创建⼀个数组类型,它是10个Point元素组成的数组。
class MyStruct(Structure):
_fields_ =[("a", c_int),
("b", c_float),
("pts", POINT*4)]# 相当于C语⾔的:  POINT pts[4]
print(len(MyStruct().point_array))#输出  4
arr = TenPointsArrayType()#创建⼀个数组类的对象。
for pt in arr:
print(pt.x, pt.y)
TenIntegers = c_int*10# 定义⼀个int[10]的类型(类)
ii = TenIntegers(1,2,3,4,5,6,7,8,9,10)#显式初始化数组(类)
for i in ii:
print(i)
#⾼维数组,即数组的数组。
type_int_array_10 = c_int *10#先定义⼀个数组类型
type_int_array_10_10 = type_int_array_10 *10#定义数组的数组(即⼆维数组)
my_array = type_int_array_10_10()#创建⼆维数组对象
my_array[1][2]=3#使⽤⼆维数组
5.指针和引⽤
有时C函数需要⼀个指针指向的数据作为参数,或想向指针指向的内存块写输出数据,或者数据太⼤不适合传递,这时就需要使⽤指针或者引⽤。
ctypes中使⽤POINTER和pointer表⽰指针,在使⽤POINTER时需要设置指向的数据类型,⽽pointer则直接从变量中得到⼀个特定类型的指针。
ctypes使⽤ byref() 函数表⽰指针概念,该函数也是直接从变量得到指针指向的数据类型。
对内存块的使⽤,实际上是区分输⼊/输出的。如果是该内存块是函数的输⼊(即函数内部是从内存块读数据),则使⽤指针,即POINTER() 或 pointer()。
如果是该内存块是函数的输出(即函数内部是写数据到内存块),则需使⽤create_string_buffer() 函数。
指针
例如创建⼀个类似于C语⾔的int *:
type_p_int = POINTER(c_int)#创建指针类型,它指向整数
v = c_int(4)#定义⼀个整数,值为4.
2011优酷影视盛典
p_int = type_p_int(v)#给⼀个指针变量(p_int)赋值(为变量v的地址).
print(p_int[0])
print(ts)#指针实例有⼀个contents属性,返回这个指针所指向的对象。
上⾯这段代码在C语⾔⾥相当于:
typedef int* type_p_int;
int v =4;
type_p_int p =&v;
printf("%d",p[0]);
printf("%d",*p);
也可以不经过声明指针类型这⼀步,直接从变量得到指针,如下:
v = c_int(4)#定义⼀个整数,值为4.
p_int = pointer(v)#直接得到v的指针,不需创建指针类型(省去类型声明)。
print(p_int[0])
print(ts)
注意:对指针类型 c_char_p,c_wchar_p,c_void_p 的赋值将会改变其指向的内存区域地址,⽽不是改变内存块的值(因为Python字符串是只读的)。
byref()
ctypes使⽤ byref() 函数传递参数引⽤。通常使⽤ byref()的地⽅同样也可⽤指针函数pointer(),但pointer()作为参数通常会额外创建⼀个指针对象,如果并不需要再次使⽤该指针对象的话,使⽤ byref() 会更快。
内存块
各种指针类型(c_char_p,c_wchar_p,c_void_p)指向的内存块实际上都是只读的。如果某个函数
需要⼀个输⼊内存块保存输出值,不能传递这些指针。我们需要⼀个可写的内存块,使⽤create_string_buffer() 函数创建。
from ctypes import*
p = create_string_buffer(3)#创建3字节长的buf,且初始化为0
print(sizeof(p),repr(p.raw))#输出 3 '\x00\x00\x00'
p = create_string_buffer(""Hello")#创建⼀个字符串(包括结尾的0)的buf
print(sizeof(p),repr(p.raw))#输出 6 'Hello\x00'
p = create_string_buffer("Hello",10)
print(sizeof(p),repr(p.raw))#输出 10 'Hello\x00\x00\x00\x00\x00'
p.value ="Hi"#修改buf内容(这是可变buf)
print(sizeof(p),repr(p.raw))#输出10 'Hi\x00lo\x00\x00\x00\x00\x00'
pw = create_unicode_buffer(3)#创建⼀个unicode使⽤的buf,且初始化为0
printf(sizeof(pw))#注意,这⾥将输出6。
想要创建包含unicode字符(对应C类型wchar_t)的可变内存块,使⽤create_unicode_buffer() 函数。
6.类型转换
通常情况下,ctypes会做严格的类型检查。这意味着,如果形参有⼀个POINTER(c_int)指针指向⼀个函数或者结构体的成员域类型,那么实参只能接受相同类型的实例。
但这个规则也有例外。⽐如,你可以传递兼容的数组类型来代替指针类型。例如对于POINTER(c_int)指针类型来说,可以使⽤c_int数组来代替。
class Bar(Structure):
_fields_ =[("count", c_int),("values", POINTER(c_int))]
bar = Bar()
bar.values =(c_int *3)(1,2,3)#数组和指针的转化
for i in unt):
print(bar.values[i])
金大奶奶#输出 1 2 3
bar.values =None#设置指针为NULL
如果⼀个函数参数显式声明为某种指针类型(例如POINT(c_int) 类型),则传递该指针指向的对象类型也是可以的(例如这⾥可以传递
c_int),ctypes会⾃动加上byref()函数进⾏类型转换。

本文发布于:2024-09-21 04:31:38,感谢您对本站的认可!

本文链接:https://www.17tex.com/xueshu/361357.html

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系,我们将在24小时内删除。

标签:类型   函数   指针   指向   数组   内存   创建
留言与评论(共有 0 条评论)
   
验证码:
Copyright ©2019-2024 Comsenz Inc.Powered by © 易纺专利技术学习网 豫ICP备2022007602号 豫公网安备41160202000603 站长QQ:729038198 关于我们 投诉建议