ctypes
——针对python的外部函数库¶
ctypes
是针对Python的外部函数库。它提供C兼容的数据类型,并允许在DLL或共享库中调用函数。它可以用纯Python封装这些库。
ctypes教程¶
注意:本教程中的代码示例使用 doctest
以确保它们确实有效。由于某些代码示例在Linux、Windows或Mac OS X下的行为不同,因此它们在注释中包含doctest指令。
注意:一些代码示例引用了CTypes c_int
类型。在平台上 sizeof(long) == sizeof(int)
它是 c_long
. 所以,如果 c_long
如果你想的话,就打印出来 c_int
---它们实际上是同一类型的。
加载动态链接库¶
ctypes
出口 cdll ,在Windows上 windll 和 奥莱德尔 对象,用于加载动态链接库。
通过将库作为这些对象的属性访问来加载库。 cdll 加载使用标准导出函数的库 cdecl
调用约定,而 windll 库调用函数时使用 stdcall
调用约定。 奥莱德尔 也使用 stdcall
调用约定,并假定函数返回一个窗口 HRESULT
错误代码。错误代码用于自动引发 OSError
函数调用失败时出现异常。
在 3.3 版更改: 用于引发的Windows错误 WindowsError
,现在是的别名 OSError
.
下面是一些Windows示例。注意 msvcrt
是包含大多数标准C函数的MS标准C库,并使用cdecl调用约定:
>>> from ctypes import *
>>> print(windll.kernel32)
<WinDLL 'kernel32', handle ... at ...>
>>> print(cdll.msvcrt)
<CDLL 'msvcrt', handle ... at ...>
>>> libc = cdll.msvcrt
>>>
Windows附加常规 .dll
文件后缀自动。
注解
通过访问标准C库 cdll.msvcrt
将使用与Python使用的库不兼容的过时版本。如果可能,使用本机python功能,或者导入并使用 msvcrt
模块。
在Linux上,需要指定文件名 包括 加载库的扩展,因此不能使用属性访问来加载库。要么 LoadLibrary()
应使用dll加载程序的方法,或者应通过调用构造函数创建cdll实例来加载库::
>>> cdll.LoadLibrary("libc.so.6")
<CDLL 'libc.so.6', handle ... at ...>
>>> libc = CDLL("libc.so.6")
>>> libc
<CDLL 'libc.so.6', handle ... at ...>
>>>
从加载的DLL访问函数¶
函数作为dll对象的属性访问:
>>> from ctypes import *
>>> libc.printf
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.GetModuleHandleA)
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.MyOwnFunction)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "ctypes.py", line 239, in __getattr__
func = _StdcallFuncPtr(name, self)
AttributeError: function 'MyOwnFunction' not found
>>>
请注意,win32系统dll类似 kernel32
和 user32
通常导出函数的ANSI和Unicode版本。Unicode版本是用 W
附加到名称后,当使用 A
附加到名称后。Win32 GetModuleHandle
函数,它返回 module handle 对于给定的模块名,具有以下C原型,并使用宏将其中一个作为 GetModuleHandle
取决于是否定义了Unicode::
/* ANSI version */
HMODULE GetModuleHandleA(LPCSTR lpModuleName);
/* UNICODE version */
HMODULE GetModuleHandleW(LPCWSTR lpModuleName);
windll 不尝试通过magic选择其中一个,您必须通过指定 GetModuleHandleA
或 GetModuleHandleW
显式调用,然后分别用字节或字符串对象调用它。
有时,DLL会导出名称不是有效的python标识符的函数,例如 "??2@YAPAXI@Z"
.在这种情况下,你必须使用 getattr()
要检索函数:
>>> getattr(cdll.msvcrt, "??2@YAPAXI@Z")
<_FuncPtr object at 0x...>
>>>
在Windows上,一些DLL不按名称而是按顺序导出函数。可以通过用序号对dll对象进行索引来访问这些函数:
>>> cdll.kernel32[1]
<_FuncPtr object at 0x...>
>>> cdll.kernel32[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "ctypes.py", line 310, in __getitem__
func = _StdcallFuncPtr(name, self)
AttributeError: function ordinal 0 not found
>>>
调用函数¶
您可以像调用其他任何Python一样调用这些函数。此示例使用 time()
函数,以秒为单位返回自Unix epoch以来的系统时间,以及 GetModuleHandleA()
函数,返回win32模块句柄。
此示例使用 NULL
指针 (None
应该用作 NULL
指针)::
>>> print(libc.time(None))
1150640792
>>> print(hex(windll.kernel32.GetModuleHandleA(None)))
0x1d000000
>>>
ValueError
当您调用 stdcall
函数 cdecl
调用约定,或相反:
>>> cdll.kernel32.GetModuleHandleA(None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with not enough arguments (4 bytes missing)
>>>
>>> windll.msvcrt.printf(b"spam")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with too many arguments (4 bytes in excess)
>>>
要找到正确的调用约定,您必须查看C头文件或要调用函数的文档。
在Windows上, ctypes
当使用无效参数值调用函数时,使用Win32结构化异常处理来防止常规保护错误导致崩溃::
>>> windll.kernel32.GetModuleHandleA(32)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: exception: access violation reading 0x00000020
>>>
但是,有足够的方法可以将python与 ctypes
所以你还是要小心。这个 faulthandler
模块有助于调试崩溃(例如,由于错误的C库调用导致的分段错误)。
None
、整数、字节对象和(unicode)字符串是唯一可以在这些函数调用中直接用作参数的本机python对象。 None
作为C传递 NULL
指针、字节对象和字符串作为指针传递到包含其数据的内存块 (char* 或 wchar_t* )python整数作为平台默认值c传递 int 类型,它们的值被屏蔽以适合C类型。
在继续使用其他参数类型调用函数之前,我们必须进一步了解 ctypes
数据类型。
基本数据类型¶
ctypes
定义许多与C原语兼容的数据类型:
ctypes类型 |
C类型 |
Python类型 |
---|---|---|
_Bool |
布尔(1) |
|
char |
1字符字节对象 |
|
|
1个字符的字符串 |
|
char |
int |
|
unsigned char |
int |
|
short |
int |
|
unsigned short |
int |
|
int |
int |
|
unsigned int |
int |
|
long |
int |
|
unsigned long |
int |
|
|
int |
|
unsigned __int64 or unsigned long long |
int |
|
|
int |
|
|
int |
|
float |
浮动 |
|
double |
浮动 |
|
long double |
浮动 |
|
char* (NUL终止) |
字节对象或 |
|
wchar_t* (NUL终止) |
字符串或 |
|
void* |
int或 |
构造函数接受具有真值的任何对象。
所有这些类型都可以通过使用正确类型和值的可选初始值设定项调用它们来创建::
>>> c_int()
c_long(0)
>>> c_wchar_p("Hello, World")
c_wchar_p(140018365411392)
>>> c_ushort(-3)
c_ushort(65533)
>>>
由于这些类型是可变的,因此之后也可以更改它们的值::
>>> i = c_int(42)
>>> print(i)
c_long(42)
>>> print(i.value)
42
>>> i.value = -99
>>> print(i.value)
-99
>>>
为指针类型的实例分配新值 c_char_p
, c_wchar_p
和 c_void_p
改变了 内存位置 他们指出, 不是内容 内存块(当然不是,因为python bytes对象是不可变的)::
>>> s = "Hello, World"
>>> c_s = c_wchar_p(s)
>>> print(c_s)
c_wchar_p(139966785747344)
>>> print(c_s.value)
Hello World
>>> c_s.value = "Hi, there"
>>> print(c_s) # the memory location has changed
c_wchar_p(139966783348904)
>>> print(c_s.value)
Hi, there
>>> print(s) # first object is unchanged
Hello, World
>>>
但是,您应该小心,不要将它们传递给期望指向可变内存的指针的函数。如果您需要可变内存块,ctypes有一个 create_string_buffer()
以各种方式创建这些内容的函数。可以使用访问(或更改)当前内存块内容 raw
属性;如果要将其作为以nul结尾的字符串访问,请使用 value
属性:
>>> from ctypes import *
>>> p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes
>>> print(sizeof(p), repr(p.raw))
3 b'\x00\x00\x00'
>>> p = create_string_buffer(b"Hello") # create a buffer containing a NUL terminated string
>>> print(sizeof(p), repr(p.raw))
6 b'Hello\x00'
>>> print(repr(p.value))
b'Hello'
>>> p = create_string_buffer(b"Hello", 10) # create a 10 byte buffer
>>> print(sizeof(p), repr(p.raw))
10 b'Hello\x00\x00\x00\x00\x00'
>>> p.value = b"Hi"
>>> print(sizeof(p), repr(p.raw))
10 b'Hi\x00lo\x00\x00\x00\x00\x00'
>>>
这个 create_string_buffer()
函数替换 c_buffer()
函数(仍可用作别名)以及 c_string()
来自早期CTypes版本的函数。创建包含C类型的Unicode字符的可变内存块 wchar_t
使用 create_unicode_buffer()
功能。
调用函数,续¶
注意,printf打印到真正的标准输出通道, not 到 sys.stdout
,因此这些示例只在控制台提示下工作,而不是从内部 IDLE 或 PythonWin ::
>>> printf = libc.printf
>>> printf(b"Hello, %s\n", b"World!")
Hello, World!
14
>>> printf(b"Hello, %S\n", "World!")
Hello, World!
14
>>> printf(b"%d bottles of beer\n", 42)
42 bottles of beer
19
>>> printf(b"%f bottles of beer\n", 42.5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ArgumentError: argument 2: exceptions.TypeError: Don't know how to convert parameter 2
>>>
如前所述,除整数、字符串和字节对象外,所有的python类型都必须封装在相应的 ctypes
类型,以便将它们转换为所需的C数据类型::
>>> printf(b"An int %d, a double %f\n", 1234, c_double(3.14))
An int 1234, a double 3.140000
31
>>>
使用自己的自定义数据类型调用函数¶
您还可以自定义 ctypes
允许将自己类的实例用作函数参数的参数转换。 ctypes
寻找一个 _as_parameter_
属性,并将其用作函数参数。当然,它必须是整数、字符串或字节中的一个:
>>> class Bottles:
... def __init__(self, number):
... self._as_parameter_ = number
...
>>> bottles = Bottles(42)
>>> printf(b"%d bottles of beer\n", bottles)
42 bottles of beer
19
>>>
如果不想将实例的数据存储在 _as_parameter_
实例变量,可以定义 property
这使属性在请求时可用。
指定所需的参数类型(函数原型)¶
通过设置 argtypes
属性。
argtypes
必须是C数据类型的序列 printf
函数在这里可能不是一个很好的例子,因为它需要一个可变的数字和不同类型的参数(取决于格式字符串),另一方面,这对于试验这个特性非常方便)::
>>> printf.argtypes = [c_char_p, c_char_p, c_int, c_double]
>>> printf(b"String '%s', Int %d, Double %f\n", b"Hi", 10, 2.2)
String 'Hi', Int 10, Double 2.200000
37
>>>
指定格式可防止不兼容的参数类型(就像C函数的原型一样),并尝试将参数转换为有效类型::
>>> printf(b"%d %d %d", 1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ArgumentError: argument 2: exceptions.TypeError: wrong type
>>> printf(b"%s %d %f\n", b"X", 2, 3)
X 2 3.000000
13
>>>
如果已经定义了自己的类,并将其传递给函数调用,则必须实现 from_param()
类方法,以便它们能够在 argtypes
序列。这个 from_param()
类方法接收传递给函数调用的python对象,它应该执行类型检查或需要执行的任何操作,以确保该对象是可接受的,然后返回对象本身、其 _as_parameter_
属性,或者在本例中作为C函数参数传递的任何内容。同样,结果应该是整数、字符串、字节和 ctypes
实例或具有 _as_parameter_
属性。
返回类型¶
默认情况下,假定函数返回c int 类型。其他返回类型可以通过设置 restype
函数对象的属性。
下面是一个更高级的示例,它使用 strchr
函数,它需要一个字符串指针和一个字符,并返回一个指向字符串的指针::
>>> strchr = libc.strchr
>>> strchr(b"abcdef", ord("d"))
8059983
>>> strchr.restype = c_char_p # c_char_p is a pointer to a string
>>> strchr(b"abcdef", ord("d"))
b'def'
>>> print(strchr(b"abcdef", ord("x")))
None
>>>
如果你想避免 ord("x")
上面的调用,您可以设置 argtypes
属性,第二个参数将从单个字符python bytes对象转换为c char::
>>> strchr.restype = c_char_p
>>> strchr.argtypes = [c_char_p, c_char]
>>> strchr(b"abcdef", b"d")
'def'
>>> strchr(b"abcdef", b"def")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ArgumentError: argument 2: exceptions.TypeError: one character string expected
>>> print(strchr(b"abcdef", b"x"))
None
>>> strchr(b"abcdef", b"d")
'def'
>>>
还可以使用可调用的python对象(例如函数或类)作为 restype
属性,如果外部函数返回整数。将使用 整数 C函数返回,此调用的结果将用作函数调用的结果。这对于检查错误返回值并自动引发异常非常有用:
>>> GetModuleHandle = windll.kernel32.GetModuleHandleA
>>> def ValidHandle(value):
... if value == 0:
... raise WinError()
... return value
...
>>>
>>> GetModuleHandle.restype = ValidHandle
>>> GetModuleHandle(None)
486539264
>>> GetModuleHandle("something silly")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in ValidHandle
OSError: [Errno 126] The specified module could not be found.
>>>
WinError
是一个调用Windows的函数 FormatMessage()
获取错误代码的字符串表示形式的API,以及 返回 一个例外。 WinError
接受可选的错误代码参数,如果不使用任何参数,则调用 GetLastError()
检索它。
请注意,通过 errcheck
属性;有关详细信息,请参阅参考手册。
传递指针(或:通过引用传递参数)¶
有时C API函数需要 指针 以数据类型作为参数,可能写入相应的位置,或者如果数据太大而无法通过值传递。这也被称为 通过引用传递参数 .
ctypes
出口 byref()
用于通过引用传递参数的函数。同样的效果也可以用 pointer()
功能,尽管 pointer()
因为它构造了一个真正的指针对象,所以使用起来更快 byref()
如果不需要python本身中的指针对象:
>>> i = c_int()
>>> f = c_float()
>>> s = create_string_buffer(b'\000' * 32)
>>> print(i.value, f.value, repr(s.value))
0 0.0 b''
>>> libc.sscanf(b"1 3.14 Hello", b"%d %f %s",
... byref(i), byref(f), s)
3
>>> print(i.value, f.value, repr(s.value))
1 3.1400001049 b'Hello'
>>>
结构和工会¶
结构和联合必须从 Structure
和 Union
在中定义的基类 ctypes
模块。每个子类必须定义 _fields_
属性。 _fields_
必须是 2-tuples 含A 字段名 和A 字段类型 .
字段类型必须是 ctypes
类 c_int
或任何其他派生的 ctypes
类型:结构、联合、数组、指针。
下面是一个简单的点结构示例,其中包含两个名为 x 和 y ,还演示如何在构造函数中初始化结构::
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = [("x", c_int),
... ("y", c_int)]
...
>>> point = POINT(10, 20)
>>> print(point.x, point.y)
10 20
>>> point = POINT(y=5)
>>> print(point.x, point.y)
0 5
>>> POINT(1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: too many initializers
>>>
但是,您可以构建更复杂的结构。通过将结构用作字段类型,结构本身可以包含其他结构。
这是一个rect结构,包含两个名为 左上角的 和 低亮度 ::
>>> class RECT(Structure):
... _fields_ = [("upperleft", POINT),
... ("lowerright", POINT)]
...
>>> rc = RECT(point)
>>> print(rc.upperleft.x, rc.upperleft.y)
0 5
>>> print(rc.lowerright.x, rc.lowerright.y)
0 0
>>>
嵌套结构也可以通过以下几种方式在构造函数中初始化:
>>> r = RECT(POINT(1, 2), POINT(3, 4))
>>> r = RECT((1, 2), (3, 4))
场 descriptor S可以从 classes ,它们对于调试很有用,因为它们可以提供有用的信息::
>>> print(POINT.x)
<Field type=c_long, ofs=0, size=4>
>>> print(POINT.y)
<Field type=c_long, ofs=4, size=4>
>>>
警告
ctypes
不支持按值将具有位字段的联合或结构传递给函数。虽然这可以在32位x86上工作,但库不能保证在一般情况下工作。带位字段的联合和结构应始终通过指针传递给函数。
结构/联合对齐和字节顺序¶
默认情况下,Structure和Union字段的对齐方式与C编译器的对齐方式相同。可以通过指定 _pack_
子类定义中的类属性。必须将其设置为正整数,并指定字段的最大对齐方式。这是什么 #pragma pack(n)
在MSVC中也是如此。
ctypes
对结构和联合使用本机字节顺序。要使用非本机字节顺序构建结构,可以使用 BigEndianStructure
, LittleEndianStructure
, BigEndianUnion
和 LittleEndianUnion
基类。这些类不能包含指针字段。
结构和联合中的位字段¶
可以创建包含位字段的结构和联合。位字段只能用于整数字段,位宽度被指定为 _fields_
元组::
>>> class Int(Structure):
... _fields_ = [("first_16", c_int, 16),
... ("second_16", c_int, 16)]
...
>>> print(Int.first_16)
<Field type=c_long, ofs=0:0, bits=16>
>>> print(Int.second_16)
<Field type=c_long, ofs=0:16, bits=16>
>>>
数组¶
数组是序列,包含固定数量的相同类型的实例。
创建数组类型的建议方法是将数据类型与正整数相乘:
TenPointsArrayType = POINT * 10
下面是一个人工数据类型的例子,一个包含4个点的结构,其中包括:
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = ("x", c_int), ("y", c_int)
...
>>> class MyStruct(Structure):
... _fields_ = [("a", c_int),
... ("b", c_float),
... ("point_array", POINT * 4)]
>>>
>>> print(len(MyStruct().point_array))
4
>>>
通过调用类,以通常的方式创建实例:
arr = TenPointsArrayType()
for pt in arr:
print(pt.x, pt.y)
上面的代码打印了一系列 0 0
行,因为数组内容初始化为零。
也可以指定正确类型的初始值设定项::
>>> 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, end=" ")
...
1 2 3 4 5 6 7 8 9 10
>>>
指针¶
指针实例是通过调用 pointer()
A函数 ctypes
类型:
>>> from ctypes import *
>>> i = c_int(42)
>>> pi = pointer(i)
>>>
指针实例具有 contents
返回指针指向的对象的属性, i
对象:
>>> pi.contents
c_long(42)
>>>
注意 ctypes
没有OOR(原始对象返回),它在每次检索属性时构造一个新的等效对象::
>>> pi.contents is i
False
>>> pi.contents is pi.contents
False
>>>
分配另一个 c_int
指向指针内容属性的实例将导致指针指向存储此内容的内存位置::
>>> i = c_int(99)
>>> pi.contents = i
>>> pi.contents
c_long(99)
>>>
指针实例也可以用整数进行索引::
>>> pi[0]
99
>>>
分配给整数索引将更改指向值:
>>> print(i)
c_long(99)
>>> pi[0] = 22
>>> print(i)
c_long(22)
>>>
也可以使用不同于0的索引,但您必须知道自己在做什么,就像在C中一样:您可以访问或更改任意的内存位置。通常,只有当您从C函数收到指针时,才使用此功能,并且 know 指针实际指向数组而不是单个项。
在幕后, pointer()
函数不仅仅创建指针实例,还必须创建指针 类型 第一。这是用 POINTER()
函数,它接受任何 ctypes
键入并返回新类型::
>>> PI = POINTER(c_int)
>>> PI
<class 'ctypes.LP_c_long'>
>>> PI(42)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: expected c_long instead of int
>>> PI(c_int(42))
<ctypes.LP_c_long object at 0x...>
>>>
在没有参数的情况下调用指针类型将创建 NULL
指针。 NULL
指针有一个 False
布尔值:
>>> null_ptr = POINTER(c_int)()
>>> print(bool(null_ptr))
False
>>>
ctypes
检查 NULL
当取消对指针的引用时(但取消对无效非-NULL
指针将使python崩溃)::
>>> null_ptr[0]
Traceback (most recent call last):
....
ValueError: NULL pointer access
>>>
>>> null_ptr[0] = 1234
Traceback (most recent call last):
....
ValueError: NULL pointer access
>>>
类型转换¶
通常,CTypes执行严格的类型检查。这意味着,如果你有 POINTER(c_int)
在 argtypes
函数列表或结构定义中的成员字段类型,只接受完全相同类型的实例。此规则有一些例外,其中CTypes接受其他对象。例如,可以传递兼容的数组实例,而不是指针类型。所以,为了 POINTER(c_int)
,ctypes接受一个c_int:数组:
>>> class Bar(Structure):
... _fields_ = [("count", c_int), ("values", POINTER(c_int))]
...
>>> bar = Bar()
>>> bar.values = (c_int * 3)(1, 2, 3)
>>> bar.count = 3
>>> for i in range(bar.count):
... print(bar.values[i])
...
1
2
3
>>>
此外,如果函数参数显式声明为指针类型(例如 POINTER(c_int)
在 argtypes
,指向类型的对象 (c_int
在这种情况下)可以传递给函数。C类型将应用所需的 byref()
在这种情况下自动转换。
将指针类型字段设置为 NULL
,您可以分配 None
::
>>> bar.values = None
>>>
有时您有不兼容类型的实例。在C语言中,可以将一种类型转换成另一种类型。 ctypes
提供了一个 cast()
可以以相同方式使用的函数。这个 Bar
上面定义的结构接受 POINTER(c_int)
指针或 c_int
数组 values
字段,但不是其他类型的实例:
>>> bar.values = (c_byte * 4)()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: incompatible types, c_byte_Array_4 instance instead of LP_c_long instance
>>>
对于这些情况, cast()
功能很方便。
这个 cast()
函数可用于将CTypes实例强制转换为指向不同CTypes数据类型的指针。 cast()
接受两个参数,一个转换为或可以转换为某种指针的CTypes对象和一个CTypes指针类型。它返回第二个参数的实例,该实例引用与第一个参数相同的内存块:
>>> a = (c_byte * 4)()
>>> cast(a, POINTER(c_int))
<ctypes.LP_c_long object at ...>
>>>
所以, cast()
可用于分配给 values
领域 Bar
结构:
>>> bar = Bar()
>>> bar.values = cast((c_byte * 4)(), POINTER(c_int))
>>> print(bar.values[0])
0
>>>
类型不完整¶
类型不完整 是尚未指定其成员的结构、联合或数组。在C语言中,它们是由forward声明指定的,后者在后面定义:
struct cell; /* forward declaration */
struct cell {
char *name;
struct cell *next;
};
直接翻译成ctypes代码就是这样,但它不起作用:
>>> class cell(Structure):
... _fields_ = [("name", c_char_p),
... ("next", POINTER(cell))]
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in cell
NameError: name 'cell' is not defined
>>>
因为新 class cell
在类语句本身中不可用。在 ctypes
我们可以定义 cell
类并设置 _fields_
属性,在class语句之后::
>>> from ctypes import *
>>> class cell(Structure):
... pass
...
>>> cell._fields_ = [("name", c_char_p),
... ("next", POINTER(cell))]
>>>
我们试试吧。我们创建了两个 cell
,让它们互相指向,最后跟随指针链几次:
>>> c1 = cell()
>>> c1.name = "foo"
>>> c2 = cell()
>>> c2.name = "bar"
>>> c1.next = pointer(c2)
>>> c2.next = pointer(c1)
>>> p = c1
>>> for i in range(8):
... print(p.name, end=" ")
... p = p.next[0]
...
foo bar foo bar foo bar foo bar
>>>
回调函数¶
ctypes
允许从Python调用创建C可调用函数指针。这些有时被称为 回调函数 .
首先,必须为回调函数创建一个类。类知道调用约定、返回类型以及此函数将接收的参数的数量和类型。
这个 CFUNCTYPE()
factory函数使用 cdecl
调用约定。在Windows上 WINFUNCTYPE()
factory函数使用 stdcall
调用约定。
调用这两个factory函数时,结果类型都是第一个参数,回调函数期望参数类型是剩余参数。
我将在这里给出一个使用标准C库的示例 qsort()
函数,用于在回调函数的帮助下对项进行排序。 qsort()
将用于对整数数组进行排序::
>>> IntArray5 = c_int * 5
>>> ia = IntArray5(5, 1, 7, 33, 99)
>>> qsort = libc.qsort
>>> qsort.restype = None
>>>
qsort()
必须使用指向要排序的数据的指针、数据数组中的项数、一个项的大小以及指向比较函数、回调的指针调用。然后使用指向项的两个指针调用回调,如果第一个项小于第二个项,则回调必须返回负整数;如果相等,则回调必须返回零;否则,回调必须返回正整数。
所以回调函数接收指向整数的指针,并且必须返回一个整数。首先我们创建 type
对于回调函数:
>>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
>>>
为了开始,这里有一个简单的回调,它显示了它传递的值:
>>> def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return 0
...
>>> cmp_func = CMPFUNC(py_cmp_func)
>>>
结果:
>>> qsort(ia, len(ia), sizeof(c_int), cmp_func)
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 5 7
py_cmp_func 1 7
>>>
现在我们可以实际比较这两个项目并返回一个有用的结果:
>>> def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return a[0] - b[0]
...
>>>
>>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func))
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>
由于我们可以很容易地检查,我们的数组现在被排序:
>>> for i in ia: print(i, end=" ")
...
1 5 7 33 99
>>>
功能工厂可以用作装饰工厂,所以我们也可以写:
>>> @CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
... def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return a[0] - b[0]
...
>>> qsort(ia, len(ia), sizeof(c_int), py_cmp_func)
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>
注解
请确保保留对的引用 CFUNCTYPE()
对象,只要它们是从C代码中使用的。 ctypes
没有,如果没有,它们可能会被垃圾收集,在回调时会使程序崩溃。
另外,请注意,如果回调函数是在python控制之外创建的线程中调用的(例如,由调用回调的外部代码调用),则ctypes在每次调用时都会创建一个新的伪python线程。这种行为在大多数情况下都是正确的,但这意味着存储的值 threading.local
将 not 即使这些调用是从同一个C线程发出的,也能在不同的回调中存活。
访问从DLL导出的值¶
有些共享库不仅导出函数,还导出变量。python库本身的一个例子是 Py_OptimizeFlag
,一个整数设置为0、1或2,具体取决于 -O
或 -OO
启动时给出的标志。
ctypes
可以使用 in_dll()
类型的类方法。 巨嘴鸟 是允许访问python c api的预定义符号::
>>> opt_flag = c_int.in_dll(pythonapi, "Py_OptimizeFlag")
>>> print(opt_flag)
c_long(0)
>>>
如果口译员是用 -O
,样本会打印出来 c_long(1)
或 c_long(2)
如果 -OO
会被指定。
一个扩展示例,它还演示了指针访问 PyImport_FrozenModules
由python导出的指针。
为该值引用文档:
此指针初始化为指向 struct _frozen 记录,由成员均为
NULL
或者是零。导入冻结模块时,将在此表中进行搜索。第三方代码可以利用这一点来提供动态创建的冻结模块集合。
所以操纵这个指针甚至可以证明是有用的。为了限制示例大小,我们只显示如何使用 ctypes
::
>>> from ctypes import *
>>>
>>> class struct_frozen(Structure):
... _fields_ = [("name", c_char_p),
... ("code", POINTER(c_ubyte)),
... ("size", c_int)]
...
>>>
我们已经定义了 struct _frozen 数据类型,因此我们可以获取指向表的指针:
>>> FrozenTable = POINTER(struct_frozen)
>>> table = FrozenTable.in_dll(pythonapi, "PyImport_FrozenModules")
>>>
自从 table
是一个 pointer
到数组中 struct_frozen
记录,我们可以遍历它,但我们只需要确保循环终止,因为指针没有大小。迟早它可能会因访问冲突或其他原因而崩溃,所以当我们点击 NULL
条目::
>>> for item in table:
... if item.name is None:
... break
... print(item.name.decode("ascii"), item.size)
...
_frozen_importlib 31764
_frozen_importlib_external 41499
__hello__ 161
__phello__ -161
__phello__.spam 161
>>>
标准Python有一个冻结的模块和一个冻结的包(由 size
成员)不是很有名,它只用于测试。试试看 import __hello__
例如。
惊奇¶
有一些边缘 ctypes
在那里你可能会期待一些事情,而不是实际发生的事情。
请考虑以下示例:
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = ("x", c_int), ("y", c_int)
...
>>> class RECT(Structure):
... _fields_ = ("a", POINT), ("b", POINT)
...
>>> p1 = POINT(1, 2)
>>> p2 = POINT(3, 4)
>>> rc = RECT(p1, p2)
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
1 2 3 4
>>> # now swap the two points
>>> rc.a, rc.b = rc.b, rc.a
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
3 4 3 4
>>>
嗯,我们当然希望最后一份声明能打印出来 3 4 1 2
. 发生了什么事?下面是 rc.a, rc.b = rc.b, rc.a
线以上:
>>> temp0, temp1 = rc.b, rc.a
>>> rc.a = temp0
>>> rc.b = temp1
>>>
注意 temp0
和 temp1
对象是否仍在使用 rc
上面的对象。所以执行 rc.a = temp0
复制的缓冲区内容 temp0
进入之内 rc
缓冲器。这反过来又改变了 temp1
.所以,最后一个任务 rc.b = temp1
,没有预期的效果。
请记住,从结构、联合和数组中检索子对象不会 copy 子对象,而不是检索访问根对象的底层缓冲区的封装对象。
另一个可能表现出与预期不同的例子是:
>>> s = c_char_p()
>>> s.value = b"abc def ghi"
>>> s.value
b'abc def ghi'
>>> s.value is s.value
False
>>>
注解
从中实例化的对象 c_char_p
只能将其值设置为字节或整数。
为什么要打印 False
?CTypes实例是包含一个内存块和一些 descriptor s访问内存的内容。在内存块中存储python对象并不存储对象本身,而是 contents
存储对象的。每次再次访问内容都会构造一个新的python对象!
可变大小的数据类型¶
ctypes
为可变大小的数组和结构提供一些支持。
这个 resize()
函数可用于调整现有CTypes对象的内存缓冲区大小。函数将对象作为第一个参数,请求的字节大小作为第二个参数。内存块不能小于对象类型a指定的自然内存块。 ValueError
如果尝试此操作,则引发:
>>> short_array = (c_short * 4)()
>>> print(sizeof(short_array))
8
>>> resize(short_array, 4)
Traceback (most recent call last):
...
ValueError: minimum size is 8
>>> resize(short_array, 32)
>>> sizeof(short_array)
32
>>> sizeof(type(short_array))
8
>>>
这很好也很好,但是如何访问这个数组中包含的附加元素呢?由于类型仍然只知道4个元素,因此访问其他元素时出错:
>>> short_array[:]
[0, 0, 0, 0]
>>> short_array[7]
Traceback (most recent call last):
...
IndexError: invalid index
>>>
另一种使用可变大小数据类型的方法 ctypes
是使用python的动态特性,并且(重新)在所需的大小已知之后,根据具体情况定义数据类型。
ctypes引用¶
外部功能¶
如前一节所述,可以将外部函数作为加载的共享库的属性来访问。以这种方式创建的函数对象在默认情况下接受任意数量的参数,接受任意CTypes数据实例作为参数,并返回库加载程序指定的默认结果类型。它们是私有类的实例:
- class ctypes._FuncPtr¶
C可调用外部函数的基类。
外部函数的实例也是C兼容的数据类型;它们表示C函数指针。
可以通过指定外部函数对象的特殊属性来自定义此行为。
- restype¶
指定CTypes类型以指定外部函数的结果类型。使用
None
对于 void ,一个不返回任何内容的函数。可以分配一个不是ctypes类型的可调用python对象,在这种情况下,假定函数返回一个c int ,并且将使用此整数调用可调用文件,从而允许进一步处理或检查错误。不推荐使用此方法,为了更灵活的后期处理或错误检查,请将CTypes数据类型用作
restype
并将可调用分配给errcheck
属性。
- argtypes¶
指定CTypes类型的元组以指定函数接受的参数类型。函数使用
stdcall
调用约定只能使用与此元组长度相同的参数数目来调用;使用C调用约定的函数也接受其他未指定的参数。调用外部函数时,每个实际参数都传递给
from_param()
中的项的类方法argtypes
元组,此方法允许将实际参数调整为外部函数接受的对象。例如,Ac_char_p
项目中argtypes
tuple将使用ctypes转换规则将作为参数传递的字符串转换为bytes对象。新:现在可以将项目放入不是CTypes类型的argtype中,但每个项目必须具有
from_param()
方法,返回可用作参数的值(integer、string、ctypes实例)。这允许定义可以将自定义对象调整为函数参数的适配器。
- errcheck¶
为该属性分配一个python函数或另一个可调用的函数。将使用三个或更多参数调用Callable:
- callable(result, func, arguments)
结果 是外部函数返回的值,如
restype
属性。func 是外部函数对象本身,这允许重用同一个可调用对象来检查或后期处理多个函数的结果。
参数 是一个包含最初传递给函数调用的参数的元组,这允许专门化所用参数的行为。
此函数返回的对象将从外部函数调用返回,但它也可以检查结果值,并在外部函数调用失败时引发异常。
- exception ctypes.ArgumentError¶
当外部函数调用无法转换某个传递的参数时,会引发此异常。
提出一个 auditing event ctypes.seh_exception
带着论证 code
.
提出一个 auditing event ctypes.call_function
带着论据 func_pointer
, arguments
.
函数原型¶
也可以通过实例化函数原型来创建外部函数。函数原型与C中的函数原型相似;它们描述一个函数(返回类型、参数类型、调用约定),而不定义实现。factory函数必须使用所需的结果类型和函数的参数类型调用,并且可以用作修饰工厂,因此,可以通过 @wrapper
语法。参见 回调函数 举个例子。
- ctypes.CFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)¶
返回的函数原型创建使用标准C调用约定的函数。该函数将在调用期间释放gil。如果 use_errno 设置为true时,系统的CTypes私有副本
errno
变量与实数交换errno
调用前后的值; use_last_error 对Windows错误代码执行相同的操作。
- ctypes.WINFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)¶
仅限Windows:返回的函数原型创建使用
stdcall
调用约定,但在Windows CE上除外WINFUNCTYPE()
是一样的CFUNCTYPE()
. 该函数将在调用期间释放gil。 use_errno 和 use_last_error 与上述含义相同。
- ctypes.PYFUNCTYPE(restype, *argtypes)¶
返回的函数原型创建使用Python调用约定的函数。功能将 not 在通话中释放gil。
这些factory函数创建的函数原型可以用不同的方式实例化,具体取决于调用中参数的类型和数量:
- prototype(address)
返回指定地址的外部函数,该地址必须是整数。
- prototype(callable)
从python创建C可调用函数(回调函数) 可赎回的 .
- prototype(func_spec[, paramflags])
返回由共享库导出的外部函数。 func_spec 必须是2元组
(name_or_ordinal, library)
. 第一项是导出函数的名称(作为字符串),或者导出函数的序号(作为小整数)。第二项是共享库实例。
- prototype(vtbl_index, name[, paramflags[, iid]])
返回将调用COM方法的外部函数。 vtbl_index 是进入虚拟函数表的索引,一个小的非负整数。 name 是COM方法的名称。 iid 是指向在扩展错误报告中使用的接口标识符的可选指针。
COM方法使用特殊的调用约定:除了在
argtypes
元组。可选的 副旗标 参数创建的外部函数封装器的功能比上面描述的功能多得多。
副旗标 必须是与相同长度的元组
argtypes
.此元组中的每个项都包含有关参数的进一步信息,它必须是包含一个、两个或三个项的元组。
第一项是一个整数,包含参数的方向标志组合:
- 1
指定函数的输入参数。
- 2
输出参数。foreign函数填充一个值。
- 4
默认为整数零的输入参数。
可选的第二项是作为字符串的参数名。如果指定了此参数,则可以使用命名参数调用外部函数。
可选的第三项是此参数的默认值。
这个例子演示了如何封装窗口 MessageBoxW
函数,以便它支持默认参数和命名参数。Windows头文件中的C声明如下:
WINUSERAPI int WINAPI
MessageBoxW(
HWND hWnd,
LPCWSTR lpText,
LPCWSTR lpCaption,
UINT uType);
这是封装纸 ctypes
::
>>> from ctypes import c_int, WINFUNCTYPE, windll
>>> from ctypes.wintypes import HWND, LPCWSTR, UINT
>>> prototype = WINFUNCTYPE(c_int, HWND, LPCWSTR, LPCWSTR, UINT)
>>> paramflags = (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", "Hello from ctypes"), (1, "flags", 0)
>>> MessageBox = prototype(("MessageBoxW", windll.user32), paramflags)
这个 MessageBox
现在可以通过以下方式调用外部函数:
>>> MessageBox()
>>> MessageBox(text="Spam, spam, spam")
>>> MessageBox(flags=2, text="foo bar")
第二个示例演示输出参数。Win32 GetWindowRect
函数通过将指定窗口的维度复制到 RECT
调用方必须提供的结构。这是C声明:
WINUSERAPI BOOL WINAPI
GetWindowRect(
HWND hWnd,
LPRECT lpRect);
这是封装纸 ctypes
::
>>> from ctypes import POINTER, WINFUNCTYPE, windll, WinError
>>> from ctypes.wintypes import BOOL, HWND, RECT
>>> prototype = WINFUNCTYPE(BOOL, HWND, POINTER(RECT))
>>> paramflags = (1, "hwnd"), (2, "lprect")
>>> GetWindowRect = prototype(("GetWindowRect", windll.user32), paramflags)
>>>
带有输出参数的函数如果只有一个参数,将自动返回输出参数值;如果有多个参数,则返回包含输出参数值的元组,因此当调用getWindowRect函数时,它现在返回一个rect实例。
输出参数可以与 errcheck
协议做进一步的输出处理和错误检查。Win32 GetWindowRect
API函数返回 BOOL
指示成功或失败,以便此函数可以进行错误检查,并在API调用失败时引发异常::
>>> def errcheck(result, func, args):
... if not result:
... raise WinError()
... return args
...
>>> GetWindowRect.errcheck = errcheck
>>>
如果 errcheck
函数返回它接收到的参数元组不变, ctypes
继续对输出参数进行正常处理。如果要返回窗口坐标的元组而不是 RECT
例如,您可以检索函数中的字段并返回它们,而不再进行正常处理::
>>> def errcheck(result, func, args):
... if not result:
... raise WinError()
... rc = args[1]
... return rc.left, rc.top, rc.bottom, rc.right
...
>>> GetWindowRect.errcheck = errcheck
>>>
效用函数¶
- ctypes.addressof(obj)¶
以整数形式返回内存缓冲区的地址。 obj 必须是CTypes类型的实例。
提出一个 auditing event
ctypes.addressof
带着论证obj
.
- ctypes.alignment(obj_or_type)¶
返回CTypes类型的对齐要求。 obj_or_type 必须是CTypes类型或实例。
- ctypes.byref(obj[, offset])¶
返回一个轻量级指针 obj ,它必须是CTypes类型的实例。 抵消 默认为零,并且必须是将添加到内部指针值的整数。
byref(obj, offset)
对应于此C代码:(((char *)&obj) + offset)
返回的对象只能用作外部函数调用参数。它的行为类似于
pointer(obj)
但施工速度要快得多。
- ctypes.cast(obj, type)¶
此函数与C中的强制转换运算符类似。它返回 type 指向同一个内存块 obj . type 必须是指针类型,并且 obj 必须是可以解释为指针的对象。
- ctypes.create_string_buffer(init_or_size, size=None)¶
此函数创建可变字符缓冲区。返回的对象是
c_char
.init_or_size 必须是指定数组大小的整数,或者是用于初始化数组项的bytes对象。
如果一个bytes对象被指定为第一个参数,缓冲区将被设置为一个大于其长度的项目,以便数组中的最后一个元素是nul终止字符。整数可以作为第二个参数传递,如果不应使用字节长度,则该参数允许指定数组的大小。
提出一个 auditing event
ctypes.create_string_buffer
带着论据init
,size
.
- ctypes.create_unicode_buffer(init_or_size, size=None)¶
此函数创建可变的Unicode字符缓冲区。返回的对象是
c_wchar
.init_or_size 必须是指定数组大小的整数,或者是用于初始化数组项的字符串。
如果将字符串指定为第一个参数,则缓冲区将被设置为一个大于字符串长度的项,以便数组中的最后一个元素是nul终止字符。整数可以作为第二个参数传递,如果不应使用字符串的长度,则该参数允许指定数组的大小。
提出一个 auditing event
ctypes.create_unicode_buffer
带着论据init
,size
.
- ctypes.DllCanUnloadNow()¶
仅限Windows:此函数是一个钩子,允许使用CTypes实现进程内COM服务器。它是从dllcanUnloadNow函数调用的,该函数用于扩展dll导出。
- ctypes.DllGetClassObject()¶
仅限Windows:此函数是一个钩子,允许使用CTypes实现进程内COM服务器。它是从dllgetClassObject函数调用的,
_ctypes
扩展dll导出。
- ctypes.util.find_library(name)¶
尝试查找库并返回路径名。 name 没有任何前缀的库名是否类似
lib
后缀.so
,.dylib
或版本号(这是用于POSIX链接器选项的表单-l
)如果找不到库,则返回None
.确切的功能取决于系统。
- ctypes.util.find_msvcrt()¶
仅限Windows:返回python和扩展模块使用的vc运行库的文件名。如果无法确定库的名称,
None
返回。例如,如果需要释放内存,则由一个扩展模块分配,并调用
free(void *)
,在分配内存的同一个库中使用该函数很重要。
- ctypes.FormatError([code])¶
仅限Windows:返回错误代码的文本描述 code . 如果未指定错误代码,则通过调用Windows API函数getLastError来使用最后一个错误代码。
- ctypes.GetLastError()¶
仅限Windows:返回调用线程中Windows设置的最后一个错误代码。此函数调用Windows GetLastError() 函数,它不会返回错误代码的CTypes私有副本。
- ctypes.get_errno()¶
返回系统的CTypes私有副本的当前值
errno
调用线程中的变量。提出一个 auditing event
ctypes.get_errno
没有参数。
- ctypes.get_last_error()¶
仅限Windows:返回系统的CTypes私有副本的当前值
LastError
调用线程中的变量。提出一个 auditing event
ctypes.get_last_error
没有参数。
- ctypes.memmove(dst, src, count)¶
与标准的c memmove库函数相同:copies 计数 字节从 src 到 dst . dst 和 src 必须是可转换为指针的整数或CTypes实例。
- ctypes.memset(dst, c, count)¶
与标准的c memset library函数相同:填充地址处的内存块 dst 具有 计数 价值字节 c . dst 必须是指定地址或CTypes实例的整数。
- ctypes.POINTER(type)¶
此factory函数创建并返回新的CTypes指针类型。指针类型在内部进行缓存和重用,因此重复调用此函数很便宜。 type 必须是CTypes类型。
- ctypes.pointer(obj)¶
此函数创建一个新的指针实例,指向 obj . 返回的对象类型为
POINTER(type(obj))
.注意:如果只想将指向对象的指针传递给外部函数调用,则应使用
byref(obj)
速度快得多。
- ctypes.resize(obj, size)¶
此函数用于调整 obj ,它必须是CTypes类型的实例。不能使缓冲区小于对象类型的本机大小,如
sizeof(type(obj))
,但可以放大缓冲区。
- ctypes.set_errno(value)¶
设置系统的CTypes私有副本的当前值
errno
调用线程中的变量 value 并返回上一个值。提出一个 auditing event
ctypes.set_errno
带着论证errno
.
- ctypes.set_last_error(value)¶
仅限Windows:设置系统的CTypes私有副本的当前值
LastError
调用线程中的变量 value 并返回上一个值。提出一个 auditing event
ctypes.set_last_error
带着论证error
.
- ctypes.sizeof(obj_or_type)¶
返回CTypes类型或实例内存缓冲区的字节大小。和C一样吗
sizeof
操作员。
- ctypes.string_at(address, size=- 1)¶
此函数返回从内存地址开始的C字符串 地址 作为字节对象。如果指定了大小,则将其用作大小,否则假定字符串以零结尾。
提出一个 auditing event
ctypes.string_at
带着论据address
,size
.
- ctypes.WinError(code=None, descr=None)¶
仅限Windows:此函数可能是CTypes中命名最差的函数。它创建了一个OSError实例。如果 code 未指定,
GetLastError
调用以确定错误代码。如果 德克思 未指定,FormatError()
调用以获取错误的文本描述。在 3.3 版更改: 的实例
WindowsError
以前是要创建的。
- ctypes.wstring_at(address, size=- 1)¶
此函数返回从内存地址开始的宽字符串 地址 作为字符串。如果 size 如果指定,它将用作字符串的字符数,否则假定该字符串以零结尾。
提出一个 auditing event
ctypes.wstring_at
带着论据address
,size
.
数据类型¶
- class ctypes._CData¶
这个非公共类是所有CTypes数据类型的公共基类。除此之外,所有CTypes类型实例都包含一个存储C兼容数据的内存块;内存块的地址由
addressof()
帮助程序函数。另一个实例变量公开为_objects
;这包含其他的python对象,在内存块包含指针的情况下,这些对象需要保持活动状态。ctypes数据类型的常用方法,这些都是类方法(准确地说,它们是 metaclass ):
- from_buffer(source[, offset])¶
此方法返回共享 source 对象。这个 source 对象必须支持可写缓冲区接口。可选的 抵消 参数以字节为单位指定到源缓冲区的偏移量;默认值为零。如果源缓冲区不够大,
ValueError
提高了。提出一个 auditing event
ctypes.cdata/buffer
带着论据pointer
,size
,offset
.
- from_buffer_copy(source[, offset])¶
此方法创建CTypes实例,从 source 必须可读的对象缓冲区。可选的 抵消 参数以字节为单位指定到源缓冲区的偏移量;默认值为零。如果源缓冲区不够大,
ValueError
提高了。提出一个 auditing event
ctypes.cdata/buffer
带着论据pointer
,size
,offset
.
- from_address(address)¶
此方法使用由指定的内存返回CTypes类型实例 地址 必须是整数。
提出一个 auditing event
ctypes.cdata
带着论证address
.
- from_param(obj)¶
这种方法适用于 obj 到CTypes类型。当类型存在于外部函数的
argtypes
元组;它必须返回一个可以用作函数调用参数的对象。所有CTypes数据类型都具有此ClassMethod的默认实现,该方法通常返回 obj 如果这是该类型的实例。有些类型也接受其他对象。
- in_dll(library, name)¶
此方法返回由共享库导出的CTypes类型实例。 name 是导出数据的符号的名称, 类库 是加载的共享库。
ctypes数据类型的公共实例变量:
- _b_needsfree_¶
当CTypes数据实例已分配内存块本身时,此只读变量为true,否则为false。
- _objects¶
这个成员是
None
或者包含需要保持活动以便保持内存块内容有效的python对象的字典。此对象只公开用于调试;永远不要修改此字典的内容。
基本数据类型¶
- class ctypes._SimpleCData¶
这个非公共类是所有基本CTypes数据类型的基类。这里提到它是因为它包含基本CTypes数据类型的公共属性。
_SimpleCData
是的子类_CData
,所以它继承了它们的方法和属性。现在可以pickle不包含指针和不包含指针的CTypes数据类型。实例具有单个属性:
当基本数据类型作为外部函数调用结果返回时,或者,例如,通过检索结构字段成员或数组项,基本数据类型将透明地转换为本机Python类型。换句话说,如果一个外部函数 restype
属于 c_char_p
,您将始终收到一个python bytes对象, not 一 c_char_p
实例。
基本数据类型的子类有 not 继承此行为。所以,如果一个外国函数 restype
是的子类 c_void_p
,您将从函数调用中接收此子类的实例。当然,您可以通过访问 value
属性。
这些是基本CTypes数据类型:
- class ctypes.c_byte¶
表示C signed char 数据类型,并将值解释为小整数。构造函数接受可选的整数初始值设定项;未执行溢出检查。
- class ctypes.c_char¶
表示C char 数据类型,并将值解释为单个字符。构造函数接受可选的字符串初始值设定项,字符串的长度必须正好是一个字符。
- class ctypes.c_char_p¶
表示C char* 当数据类型指向以零结尾的字符串时。对于也可以指向二进制数据的一般字符指针,
POINTER(c_char)
必须使用。构造函数接受整数地址或字节对象。
- class ctypes.c_double¶
表示C double 数据类型。构造函数接受可选的浮点初始值设定项。
- class ctypes.c_longdouble¶
表示C long double 数据类型。构造函数接受可选的浮点初始值设定项。在平台上
sizeof(long double) == sizeof(double)
它是c_double
.
- class ctypes.c_float¶
表示C float 数据类型。构造函数接受可选的浮点初始值设定项。
- class ctypes.c_int¶
表示C signed int 数据类型。构造函数接受可选的整数初始值设定项;未执行溢出检查。在平台上
sizeof(int) == sizeof(long)
它是c_long
.
- class ctypes.c_int64¶
表示c 64位 signed int 数据类型。通常是的别名
c_longlong
.
- class ctypes.c_long¶
表示C signed long 数据类型。构造函数接受可选的整数初始值设定项;未执行溢出检查。
- class ctypes.c_longlong¶
表示C signed long long 数据类型。构造函数接受可选的整数初始值设定项;未执行溢出检查。
- class ctypes.c_short¶
表示C signed short 数据类型。构造函数接受可选的整数初始值设定项;未执行溢出检查。
- class ctypes.c_size_t¶
表示C
size_t
数据类型。
- class ctypes.c_ssize_t¶
表示C
ssize_t
数据类型。3.2 新版功能.
- class ctypes.c_ubyte¶
表示C unsigned char 数据类型,它将值解释为小整数。构造函数接受可选的整数初始值设定项;未执行溢出检查。
- class ctypes.c_uint¶
表示C unsigned int 数据类型。构造函数接受可选的整数初始值设定项;未执行溢出检查。在平台上
sizeof(int) == sizeof(long)
它是的别名c_ulong
.
- class ctypes.c_uint64¶
表示c 64位 unsigned int 数据类型。通常是的别名
c_ulonglong
.
- class ctypes.c_ulong¶
表示C unsigned long 数据类型。构造函数接受可选的整数初始值设定项;未执行溢出检查。
- class ctypes.c_ulonglong¶
表示C unsigned long long 数据类型。构造函数接受可选的整数初始值设定项;未执行溢出检查。
- class ctypes.c_ushort¶
表示C unsigned short 数据类型。构造函数接受可选的整数初始值设定项;未执行溢出检查。
- class ctypes.c_void_p¶
表示C void* 类型。该值表示为整数。构造函数接受可选的整数初始值设定项。
- class ctypes.c_wchar¶
表示C
wchar_t
数据类型,并将该值解释为单个字符的Unicode字符串。构造函数接受可选的字符串初始值设定项,字符串的长度必须正好是一个字符。
- class ctypes.c_wchar_p¶
表示C wchar_t* 数据类型,必须是指向以零结尾的宽字符串的指针。构造函数接受一个整数地址或字符串。
- class ctypes.c_bool¶
代表C bool 数据类型(更准确地说, _Bool 来自C99)。它的价值可以是
True
或False
,构造函数接受任何具有真值的对象。
- class ctypes.HRESULT¶
仅限Windows:表示
HRESULT
值,该值包含函数或方法调用的成功或错误信息。
这个 ctypes.wintypes
例如,模块提供了许多其他特定于Windows的数据类型 HWND
, WPARAM
或 DWORD
. 一些有用的结构,比如 MSG
或 RECT
也被定义。
结构化数据类型¶
- class ctypes.Union(*args, **kw)¶
以本机字节顺序抽象联合的基类。
- class ctypes.BigEndianStructure(*args, **kw)¶
中结构的抽象基类 大端 字节顺序。
- class ctypes.LittleEndianStructure(*args, **kw)¶
中结构的抽象基类 小字节 字节顺序。
具有非本机字节顺序的结构不能包含指针类型字段,或者包含指针类型字段的任何其他数据类型。
- class ctypes.Structure(*args, **kw)¶
中结构的抽象基类 本地的 字节顺序。
具体结构和联合类型必须通过子类化其中一个类型来创建,并且至少定义
_fields_
类变量。ctypes
将创造 descriptor 允许通过直接属性访问读取和写入字段的。这些是- _fields_¶
定义结构字段的序列。项必须是2元组或3元组。第一项是字段的名称,第二项指定字段的类型;它可以是任何CTypes数据类型。
对于整型字段,如
c_int
,可以给出第三个可选项。它必须是定义字段位宽度的小正整数。字段名在一个结构或联合中必须是唯一的。未选中此项,重复名称时只能访问一个字段。
可以定义
_fields_
类变量 之后 定义结构子类的类语句,这允许创建直接或间接引用自身的数据类型:class List(Structure): pass List._fields_ = [("pnext", POINTER(List)), ... ]
这个
_fields_
但是,在首次使用类型(创建实例,sizeof()
被调用,等等)。以后分配给_fields_
类变量将引发attributeError。可以定义结构类型的子类,它们继承基类的字段加上
_fields_
在子类中定义,如果有的话。
- _anonymous_¶
列出未命名(匿名)字段名称的可选序列。
_anonymous_
必须在以下情况下定义_fields_
已分配,否则将不起作用。此变量中列出的字段必须是结构或联合类型字段。
ctypes
将在允许直接访问嵌套字段的结构类型中创建描述符,而无需创建结构或联合字段。以下是示例类型(Windows)::
class _U(Union): _fields_ = [("lptdesc", POINTER(TYPEDESC)), ("lpadesc", POINTER(ARRAYDESC)), ("hreftype", HREFTYPE)] class TYPEDESC(Structure): _anonymous_ = ("u",) _fields_ = [("u", _U), ("vt", VARTYPE)]
这个
TYPEDESC
结构描述了COM数据类型,vt
字段指定哪个联合字段有效。自从u
字段定义为匿名字段,现在可以直接从typedsc实例访问成员。td.lptdesc
和td.u.lptdesc
是等效的,但前者更快,因为它不需要创建临时联合实例:td = TYPEDESC() td.vt = VT_PTR td.lptdesc = POINTER(some_type) td.u.lptdesc = POINTER(some_type)
可以定义结构的子类,它们继承基类的字段。如果子类定义有单独的
_fields_
变量,其中指定的字段将附加到基类的字段中。结构和联合构造函数同时接受位置参数和关键字参数。位置参数用于初始化成员字段,其顺序与它们在中的显示顺序相同。
_fields_
. 构造函数中的关键字参数被解释为属性赋值,因此它们将初始化_fields_
使用相同的名称,或为不在中的名称创建新属性_fields_
.