ctypes
— 用于 Python 的外来函数库
¶
ctypes
是用于 Python 的外部函数库。它提供 C 兼容数据类型,且允许调用 DLL 或共享库中的函数。可以使用它以将这些库包裹在纯 Python 中。
注意:本教程中的代码样本使用
doctest
以确保它们能实际工作。由于某些代码范例在 Linux、Windows 或 Mac OS X 下的行为会有所不同,因此,它们在注释中包含 doctest 指令。
注意:某些代码样本引用 ctypes
c_int
type. This type is an alias for the
c_long
type on 32-bit systems. So, you should not be confused if
c_long
被打印若期望
c_int
— 它们实际上是同一类型。
ctypes
导出
cdll
,Windows
windll
and
oledll
对象,为加载 DLL (动态链接库)。
通过把库作为这些对象的属性进行访问,加载库。
cdll
加载库,其导出函数使用标准
cdecl
调用约定,而
windll
库调用函数使用
stdcall
调用约定。
oledll
也使用
stdcall
调用约定,并假定函数返回 Windows
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
文件后缀,自动地。
在 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 对象的属性被访问:
>>> 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 ? File "ctypes.py", line 239, in __getattr__ func = _StdcallFuncPtr(name, self) AttributeError: function 'MyOwnFunction' not found >>>
注意:win32 系统 DLL 像
kernel32
and
user32
经常导出函数的 ANSI 和 UNICODE 版本。导出的 UNICODE 版本将
W
追加到名称,而导出的 ANSI 版本将
A
追加到名称。win32
GetModuleHandle
函数,其返回
module handle
为给定模块名,拥有以下 C 原型,且宏用于曝光它们之一因为
GetModuleHandle
取决于 UNICODE 是否被定义:
/* ANSI version */ HMODULE GetModuleHandleA(LPCSTR lpModuleName); /* UNICODE version */ HMODULE GetModuleHandleW(LPCWSTR lpModuleName);
windll
不会试着通过 Magic (魔法) 选择它们之一,访问所需版本必须通过指定
GetModuleHandleA
or
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 ? File "ctypes.py", line 310, in __getitem__ func = _StdcallFuncPtr(name, self) AttributeError: function ordinal 0 not found >>>
可以像调用任何其它 Python 回调一样,调用这些函数。此范例使用
time()
函数,返回从 Unix 纪元起的系统时间 (以秒为单位),而
GetModuleHandleA()
函数,其返回 win32 模块句柄。
此范例调用 2 函数采用 NULL 指针 (
None
应该被用作 NULL 指针):
>>> print(libc.time(None)) 1150640792 >>> print(hex(windll.kernel32.GetModuleHandleA(None))) 0x1d000000 >>>
ctypes
tries to protect you from calling functions with the wrong number of arguments or the wrong calling convention. Unfortunately this only works on Windows. It does this by examining the stack after the function returns, so although an error is raised the function
has
been called:
>>> windll.kernel32.GetModuleHandleA() Traceback (most recent call last): File "<stdin>", line 1, in ? ValueError: Procedure probably called with not enough arguments (4 bytes missing) >>> windll.kernel32.GetModuleHandleA(0, 0) Traceback (most recent call last): File "<stdin>", line 1, in ? ValueError: Procedure probably called with too many arguments (4 bytes in excess) >>>
The same exception is raised when you call an
stdcall
函数采用
cdecl
调用约定,反之亦然:
>>> cdll.kernel32.GetModuleHandleA(None) Traceback (most recent call last): File "<stdin>", line 1, in ? 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 ? 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 ? OSError: exception: access violation reading 0x00000020 >>>
不管怎样,有足够的办法能崩溃 Python 采用
ctypes
,所以无论如何都要小心。
faulthandler
模块有助于调试崩溃 (如:由不正确 C 库调用产生的分段故障)。
None
、整数、字节对象及 (unicode) 字符串是唯一本机 Python 对象,可以直接用作调用这些函数的参数。
None
被传递作为 C
NULL
指针,字节对象和字符串被传递作为指向包含其数据的内存块的指针 (
char *
or
wchar_t *
)。Python 整数被传递作为平台默认 C
int
类型。它们的值被屏蔽以拟合成 C 类型。
在继续采用其它参数类型调用函数之前,必须了解更多有关
ctypes
数据类型。
ctypes
定义了许多首要 C 兼容数据类型:
| ctypes 类型 | C 类型 | Python 类型 |
|---|---|---|
c_bool
|
_Bool
|
bool (1) |
c_char
|
char
|
1 字符 bytes 对象 |
c_wchar
|
wchar_t
|
1 字符字符串 |
c_byte
|
char
|
int |
c_ubyte
|
unsigned char
|
int |
c_short
|
short
|
int |
c_ushort
|
unsigned short
|
int |
c_int
|
int
|
int |
c_uint
|
unsigned int
|
int |
c_long
|
long
|
int |
c_ulong
|
unsigned long
|
int |
c_longlong
|
__int64
or
long long
|
int |
c_ulonglong
|
unsigned __int64
or
unsigned long long
|
int |
c_size_t
|
size_t
|
int |
c_ssize_t
|
ssize_t
or
Py_ssize_t
|
int |
c_float
|
float
|
float |
c_double
|
double
|
float |
c_longdouble
|
long double
|
float |
c_char_p
|
char *
(Null 结尾)
|
bytes 对象或
None
|
c_wchar_p
|
wchar_t *
(Null 结尾)
|
字符串或
None
|
c_void_p
|
void *
|
int 或
None
|
可以创建这些所有类型,通过采用正确类型的可选初始化程序和值调用它们:
>>> c_int() c_long(0) >>> c_wchar_p("Hello, World") c_wchar_p('Hello, World') >>> 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 字节对象是不可变的):
>>> s = "Hello, World" >>> c_s = c_wchar_p(s) >>> print(c_s) c_wchar_p('Hello, World') >>> c_s.value = "Hi, there" >>> print(c_s) c_wchar_p('Hi, there') >>> print(s) # first object is unchanged Hello, World >>>
不管怎样,应小心,不要将它们传递给期待可变内存指针的函数。若需要可变内存块,ctypes 拥有
create_string_buffer()
函数,以各种方式创建这些。当前内存块内容可以访问 (或更改) 采用
raw
特性;若希望以 NULL 终止字符串方式访问它,使用
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' >>>
The
create_string_buffer()
函数替换
c_buffer()
函数 (仍然可用作别名),及
c_string()
函数来自早期发行的 ctypes。要创建可变内存块包含 Unicode 字符 C 类型
wchar_t
使用
create_unicode_buffer()
函数。
注意:printf 打印到真正的标准输出通道,
not
to
sys.stdout
,因此,这些范例将仅工作于控制台提示,而不是
IDLE
or
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 ? ArgumentError: argument 2: exceptions.TypeError: Don't know how to convert parameter 2 >>>
如前所述,除整数、字符串及 bytes 对象外,所有 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
使属性在请求时可用。
指定从 DLL 导出函数所需的自变量类型是可能的,通过设置
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 ? 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
函数,其期望字符串指针和 char,并返回指向字符串的指针:
>>> 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
属性,并将第 2 自变量从单个字符 Python 字节对象转换成 C 字符:
>>> 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 ? ArgumentError: argument 2: exceptions.TypeError: one character string expected >>> print(strchr(b"abcdef", b"x")) None >>> strchr(b"abcdef", b"d") 'def' >>>
还可以使用可调用 Python 对象 (例如:函数或类) 作为
restype
属性,若外来函数返回整数。可调用将被调用采用
integer
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 ? 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 函数期望 pointer 指向作为参数的数据类型,可能要写入相应位置,或者若数据太大而无法通过值被传递。这也被称为 通过引用传递参数 .
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 必须派生自
Structure
and
Union
基类的定义在
ctypes
模块。每个子类必须定义
_fields_
属性。
_fields_
必须是列表
2-tuples
,包含
field name
和
field type
.
字段类型必须是
ctypes
类型像
c_int
,或任何其它派生
ctypes
类:Structure、Union、Array、Pointer。
这里是简单 POINT 结构范例,其包含 2 个整数,名为 x and 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 ? ValueError: too many initializers >>>
不管怎样,可以构建更加复杂的结构。结构本身可以包含其它结构,通过使用结构作为字段类型。
这里是 RECT 结构,包含 2 POINT 名为 upperleft and lowerright :
>>> 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 可以被检索从 class ,它们对调试很有用,因为它们可以提供有用信息:
>>> print(POINT.x) <Field type=c_long, ofs=0, size=4> >>> print(POINT.y) <Field type=c_long, ofs=4, size=4> >>>
警告
ctypes
不支持通过值将将具有位字段的 Union 或 Structure 传递给函数。虽然这可以工作于 32 位 x86,但库不保证它能工作于一般情况。应始终通过指针,将具有位字段的 Union 和 Structure 传递给函数。
By default, Structure and Union fields are aligned in the same way the C compiler does it. It is possible to override this behavior be specifying a
_pack_
类属性在子类定义中。必须将这设为正整数,并指定字段的最大对齐方式。这是为什么
#pragma pack(n)
在 MSVC 中也如此。
ctypes
为 Structure 和 Union 使用本机字节序。构建具有非本机字节序的 Structure,可以使用某一
BigEndianStructure
,
LittleEndianStructure
,
BigEndianUnion
,和
LittleEndianUnion
基类。这些类无法包含指针字段。
创建包含位字段的 Structure 和 Union 是可能的。位字段只能是整数字段,位宽度的指定如第 3 项在
_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
Here is an example of an somewhat artificial data type, a structure containing 4 POINTs among other stuff:
>>> 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
行,因为数组内容被初始化为 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()
函数在
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
实例给指针的 contents 属性,将导致指针指向存储这的内存位置:
>>> 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 ? 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
在这种情况下) 可以传递给函数。ctypes 将应用要求的
byref()
自动转换在这种情况下。
要将 POINTER 类型字段设为
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 ? TypeError: incompatible types, c_byte_Array_4 instance instead of LP_c_long instance >>>
对于这些情况,
cast()
函数很得心应手。
The
cast()
函数可以用于将 ctypes 实例铸造成指向不同 ctypes 数据类型的指针。
cast()
接受 2 参数,是 (或) 可以被转换成某种类型指针的 ctypes 对象,和 ctypes 指针类型。它返回第 2 自变量实例,引用如第 1 自变量的相同内存块:
>>> 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 >>>
不完整类型 是 Structure、Union 或数组,尚未指定其成员。在 C 中,它们由稍后定义的前向声明指定:
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 ? File "<stdin>", line 2, in cell NameError: name 'cell' is not defined >>>
因为新
class cell
不可用于类语句本身。在
ctypes
,可以定义
cell
类并设置
_fields_
属性稍后,在类语句之后:
>>> from ctypes import * >>> class cell(Structure): ... pass ... >>> cell._fields_ = [("name", c_char_p), ... ("next", POINTER(cell))] >>>
Lets try it. We create two instances of
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
allows to create C callable function pointers from Python callables. These are sometimes called
回调函数
.
首先,必须为回调函数创建类。类知道调用约定、返回类型及此函数将接收的自变量数和自变量类型。
The
CFUNCTYPE()
工厂函数为回调函数创建类型,使用
cdecl
调用约定。在 Windows,
WINFUNCTYPE()
工厂函数为回调函数创建类型,使用
stdcall
调用约定。
采用结果类型作第 1 自变量调用这 2 工厂函数,而回调函数期望自变量类型如剩余自变量。
这里将呈现的范例使用标准 C 库
qsort()
函数,用于在回调函数的帮助下排序项。
qsort()
将用于对整数数组排序:
>>> IntArray5 = c_int * 5 >>> ia = IntArray5(5, 1, 7, 33, 99) >>> qsort = libc.qsort >>> qsort.restype = None >>>
qsort()
必须被调用采用指向要排序的数据指针、数据数组中的项数、每项的大小及比较函数指针 (回调)。然后采用 2 个指向项的指针调用回调函数,且它必须返回负整数若第 1 项小于第 2 项,0 若它们相等,否则为正整数。
因此,回调函数接收指向整数的指针,且必须返回整数。首先,我们创建
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 >>>
现在,我们可以实际比较 2 项并返回有用结果:
>>> 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 代码中使用它们。
ctypes
不会,若不这样做,它们可能被垃圾回收,从而使您的程序在回调时崩溃。
另请注意,若回调函数是在 Python 控制之外创建的线程中调用的 (如,通过调用回调的外部代码),ctypes 会创建新的虚设 Python 线程当每次援引时。此行为对于大多数用途是正确的,但意味着值存储采用
threading.local
will
not
跨不同回调幸存,甚至在这些调用由同一 C 线程做出时。
某些共享库不仅导出函数,它们还导出变量。Python 库本身的范例是
Py_OptimizeFlag
,整数设为 0、1、或 2,从属
-O
or
-OO
标志在启动时给定。
ctypes
可以 访问像这样的值采用
in_dll()
类方法为类型。
pythonapi
是给予访问 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)
if
-OO
有指定。
扩展范例还演示使用指针访问
PyImport_FrozenModules
指针导出通过 Python。
该值的引用文档:
struct _frozen
记录,被终止当其每一成员都是
NULL
or zero. When a frozen
module is imported, it is searched in this table. Third-party code could play
tricks with this to provide a dynamically created collection of frozen modules.
因此,操纵此指针甚至可以证明是有用的。为限制范例大小,只展示如何读取此表采用
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
records, we can iterate over it, but we just have to make sure that our loop terminates, because pointers have no size. Sooner or later it would probably crash with an access violation or whatever, so it’s better to break out of the loop when we hit the NULL entry:
>>> for item in table: ... print(item.name, item.size) ... if item.name is None: ... break ... __hello__ 104 __phello__ -104 __phello__.spam 104 None 0 >>>
The fact that standard Python has a frozen module and a frozen package (indicated by the negative size member) is not well known, it is only used for testing. Try it out with
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
and
temp1
对象仍使用内部缓冲
rc
对象见上文。因此执行
rc.a = temp0
拷贝缓冲内容
temp0
into
rc
的缓冲。这依次改变内容为
temp1
。因此,最后赋值
rc.b = temp1
,没有期望效果。
请记住,从 Structure、Union 及数组检索子对象不 copy 子对象,相反,它检索访问根对象底层缓冲的包装器对象。
Another example that may behave different from what one would expect is this:
>>> s = c_char_p() >>> s.value = "abc def ghi" >>> s.value 'abc def ghi' >>> s.value is s.value False >>>
为什么打印
False
?ctypes 实例对象包含内存块和一些
descriptor
用于访问内存中的内容。在内存块中存储 Python 对象并不存储对象本身,而是
contents
在对象被存储。每次再次访问内容,都会构造新的 Python 对象!
ctypes
为可变大小的数组和结构提供了一些支持。
The
resize()
函数可以用于重置现有 ctypes 对象的内存缓冲大小。函数接受对象作为第 1 自变量,和请求以字节为单位的大小作为第 2 自变量。内存块不可以比由对象类型指定的自然内存块更小,
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 的动态性质,并在已知要求大小后 (重新) 定义数据类型,根据具体情况而定。
以编译型语言编程时,当编译/链接程序及程序在运行时会访问共享库。
目地对于
find_library()
function is to locate a library in a way similar to what the compiler does (on platforms with several versions of a shared library the most recent should be loaded), while the ctypes library loaders act like when a program is run, and call the runtime loader directly.
The
ctypes.util
模块提供可以帮助确定要加载库的函数。
ctypes.util.
find_library
(
name
)
试着查找库并返回路径名。
name
是库名称没有任何前缀像
lib
,后缀像
.so
,
.dylib
或版本号 (使用这种形式为 POSIX 链接器选项
-l
)。若找不到库,返回
None
.
确切功能从属系统。
在 Linux,
find_library()
试着运行外部程序 (
/sbin/ldconfig
,
gcc
,和
objdump
) to find the library file. It returns the filename of the library file. Here are some examples:
>>> from ctypes.util import find_library >>> find_library("m") 'libm.so.6' >>> find_library("c") 'libc.so.6' >>> find_library("bz2") 'libbz2.so.1.0' >>>
在 OS X,
find_library()
会尝试几种预定义命名方案和按路径定位库,并返回完整路径名若成功:
>>> from ctypes.util import find_library >>> find_library("c") '/usr/lib/libc.dylib' >>> find_library("m") '/usr/lib/libm.dylib' >>> find_library("bz2") '/usr/lib/libbz2.dylib' >>> find_library("AGL") '/System/Library/Frameworks/AGL.framework/AGL' >>>
在 Windows,
find_library()
沿系统搜索路径搜索,并返回完整路径名,但由于没有预定义命名方案,调用像
find_library("c")
会失败并返回
None
.
若包裹共享库采用
ctypes
,它
may
更好在开发时确定共享库名称,并将这硬编码到包裹器模块,而不是使用
find_library()
以在运行时定位库。
There are several ways to loaded shared libraries into the Python process. One way is to instantiate one of the following classes:
ctypes.
CDLL
(
name
,
mode=DEFAULT_MODE
,
handle=None
,
use_errno=False
,
use_last_error=False
)
¶
此类的实例表示加载的共享库。这些库中的函数使用标准 C 调用约定,且假定返回
int
.
ctypes.
OleDLL
(
name
,
mode=DEFAULT_MODE
,
handle=None
,
use_errno=False
,
use_last_error=False
)
¶
仅 Windows:此类的实例表示加载的共享库,这些库中的函数使用
stdcall
调用约定,并假定返回 Windows 特定
HRESULT
代码。
HRESULT
值包含函数调用失败或成功的指定信息,连同额外错误代码一起。若返回值信号故障,
OSError
被自动引发。
3.3 版改变:
WindowsError
用于被引发。
ctypes.
WinDLL
(
name
,
mode=DEFAULT_MODE
,
handle=None
,
use_errno=False
,
use_last_error=False
)
¶
仅 Windows:此类的实例表示加载的共享库,这些库中的函数使用
stdcall
调用约定,并假定返回
int
在默认情况下。
Python 全局解释器锁 会被释放在调用由这些库导出的任何函数之前,和之后重新获取。
ctypes.
PyDLL
(
name
,
mode=DEFAULT_MODE
,
handle=None
)
¶
此类的实例行为像
CDLL
实例,除了 Python GIL
not
被释放在函数调用期间,并在函数执行后校验 Python 错误标志。若有设置错误标志,引发 Python 异常。
因此,这只对直接调用 Python C API 函数有用。
可以通过采用至少一自变量 (共享库的路径名) 调用它们以实例化所有这些类。若拥有已加载共享库的现有句柄,可以将它它传递作为
handle
命名参数,否则底层平台
dlopen
or
LoadLibrary
函数被用以将库加载到进程,并获取其句柄。
The mode 参数可以用于指定如何加载库。有关细节,翻阅 dlopen(3) manpage, on Windows, mode 被忽略。
The
use_errno
parameter, when set to True, enables a ctypes mechanism that allows to access the system
errno
错误编号以安全方式。
ctypes
维护线程本地副本对于系统
errno
变量;若调用外部创建函数采用
use_errno=True
那么
errno
值在函数调用之前会与 ctypes 私有副本交换,同样立即发生在函数调用之后。
函数
ctypes.get_errno()
返回 ctypes 私有副本的值,和函数
ctypes.set_errno()
将 ctypes 私有副本改为新值并返回前值。
The
use_last_error
parameter, when set to True, enables the same mechanism for the Windows error code which is managed by the
GetLastError()
and
SetLastError()
Windows API 函数;
ctypes.get_last_error()
and
ctypes.set_last_error()
用于请求和更改 Windows 错误代码的 ctypes 私有副本。
ctypes.
RTLD_GLOBAL
标志能用作 mode 参数。在此标志不可用的平台,它被定义为整数 0。
ctypes.
RTLD_LOCAL
标志能用作 mode 参数。在此不可用的平台,它如同 RTLD_GLOBAL .
ctypes.
DEFAULT_MODE
用于加载共享库的默认模式。在 OSX 10.3,这是 RTLD_GLOBAL ,否则它如同 RTLD_LOCAL .
这些类的实例没有公共方法。通过共享库导出的函数可以作为属性 (或通过索引) 访问。请注意,透过属性访问函数会缓存结果,因此重复访问它每次会返回相同对象。另一方面,透过索引访问它每次返回新对象:
>>> libc.time == libc.time True >>> libc['time'] == libc['time'] False
下列公共属性是可用的,它们的名称以不与导出函数名称抵触的下划线开头:
PyDLL.
_handle
¶
用于访问库的系统句柄。
PyDLL.
_name
¶
在构造函数中传递的库名称。
共享库也可以被加载通过使用某一预制对象,实例化的
LibraryLoader
类,通过调用
LoadLibrary()
方法,或通过作为加载器实例的属性检索库。
ctypes.
LibraryLoader
(
dlltype
)
¶
加载共享库的类。
dlltype
应为某一
CDLL
,
PyDLL
,
WinDLL
,或
OleDLL
类型。
__getattr__()
has special behavior: It allows to load a shared library by accessing it as attribute of a library loader instance. The result is cached, so repeated attribute accesses return the same library each time.
LoadLibrary
(
name
)
¶
将共享库加载到进程中并返回它。此方法始终返回库的新实例。
这些预制库加载程序是可用的:
ctypes.
cdll
创建
CDLL
实例。
ctypes.
windll
仅 Windows:创建
WinDLL
实例。
ctypes.
oledll
仅 Windows:创建
OleDLL
实例。
ctypes.
pydll
创建
PyDLL
实例。
为直接访问 C Python API,随时可以使用 Python 共享库对象:
ctypes.
pythonapi
实例化的
PyDLL
以属性形式暴露 Python C API 函数。注意,假定所有这些函数返回 C
int
,当然始终不是事实,因此必须赋值正确
restype
属性以使用这些函数。
如之前章节解释,外部函数可以作为加载共享库的属性访问。默认情况下,以这种方式创建的函数对象接受任意数量的自变量,接受任何 ctypes 数据实例作为自变量,并返回由库加载器指定的默认结果类型。它们是私有类的实例:
ctypes.
_FuncPtr
¶
用于 C 可调用外来函数的基类。
外来函数的实例也是 C 兼容数据类型;它们表示 C 函数指针。
通过赋值外部函数对象的特殊属性,可以定制此行为。
restype
¶
将 ctypes 类型赋值给外来函数的指定结果类型。使用
None
for
void
,什么都不返回的函数。
赋值非 ctypes 类型的可调用 Python 对象是可能的,在这种情况下,假定函数返回 C
int
, and the callable will be called with this integer, allowing to do further processing or error checking. Using this is deprecated, for more flexible post processing or error checking use a ctypes data type as
restype
并把可调用赋值给
errcheck
属性。
argtypes
¶
将 ctypes 类型的元组赋值给函数接受的指定自变量类型。函数使用
stdcall
调用约定才可被调用采用如此元组长度相同自变量数;使用 C 调用约定的函数还接受额外、未指定自变量。
当调用外来函数时,每个实际自变量被传递给
from_param()
类方法对于项在
argtypes
tuple, this method allows to adapt the actual argument to an object that the foreign function accepts. For example, a
c_char_p
项在
argtypes
元组将使用 ctypes 转换规则,将传递作为自变量的字符串转换成 bytes 对象。
新增:现在将项放入非 ctypes 类型 argtypes 是可能的,但每项必须拥有
from_param()
method which returns a value usable as argument (integer, string, ctypes instance). This allows to define adapters that can adapt custom objects as function parameters.
errcheck
¶
将 Python 函数或另一可调用赋值给此属性。可调用会被调用采用 3 个或更多自变量:
callable
(
result
,
func
,
arguments
)
result
是外函数所返回的,如指定通过
restype
属性。
func is the foreign function object itself, this allows to reuse the same callable object to check or post process the results of several functions.
arguments is a tuple containing the parameters originally passed to the function call, this allows to specialize the behavior on the arguments used.
此函数返回的对象是外来函数调用所返回的,但它还可以校验结果值,并引发异常若外来函数调用失败。
ctypes.
ArgumentError
¶
此异常被引发当外来函数调用无法转换传递的自变量之一时。
Foreign functions can also be created by instantiating function prototypes. Function prototypes are similar to function prototypes in C; they describe a function (return type, argument types, calling convention) without defining an implementation. The factory functions must be called with the desired result type and the argument types of the function.
ctypes.
CFUNCTYPE
(
restype
,
*argtypes
,
use_errno=False
,
use_last_error=False
)
¶
返回的函数原型创建使用标准 C 调用约定的函数。函数会释放 GIL (全局解释器锁) 在调用期间。若
use_errno
is set to True, the ctypes private copy of the system
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
and
use_last_error
的含义同上。
ctypes.
PYFUNCTYPE
(
restype
,
*argtypes
)
¶
返回的函数原型,创建使用Python 调用约定的函数。函数会 not 释放 GIL 在调用期间。
可以按不同方式实例化由这些工厂函数创建的函数原型,取决于调用中的参数类型及参数数量:
prototype
(
address
)
返回在指定地址的外来函数,地址必须为整数。
prototype
(
callable
)
创建 C 可调用函数 (回调函数) 从 Python callable .
prototype
(
func_spec
[
,
paramflags
]
)
返回由共享库导出的外来函数。
func_spec
必须是 2 元素元组
(name_or_ordinal, library)
。第 1 项是以字符串形式导出的函数名称,或是以小整数形式导出的函数序数。第 2 项是共享库实例。
prototype
(
vtbl_index
,
name
[
,
paramflags
[
,
iid
]
]
)
返回将调用 COM 方法的外来函数。 vtbl_index 是虚函数表中的索引,非负小整数。 name 是 COM (组件对象模型) 方法的名称。 iid 是指向扩展错误报告中所用接口标识符的可选指针。
COM 方法使用特殊的调用约定:它们要求指向 COM (组件对象模型) 接口的指针作为第 1 自变量,除了那些指定参数在
argtypes
元组。
可选 paramflags 参数创建的外来函数包装器,具有比上文描述特征更多的功能。
paramflags
必须是元组,长度如同
argtypes
.
此元组中的各项包含关于参数的进一步信息,它必须是包含 1 项、2 项或 3 项的元组。
第 1 项是包含参数方向标志组合的整数:
可选第 2 项是字符串形式的参数名称。若这有指定,可以采用命名参数调用外来函数。
可选第 3 项是此参数的默认值。
此范例演示如何包裹 Windows
MessageBoxA
函数以便支持默认参数和命名自变量。来自 Windows 头文件的 C 声明是这样的:
WINUSERAPI int WINAPI MessageBoxA( HWND hWnd, LPCSTR lpText, LPCSTR lpCaption, UINT uType);
在此包裹采用
ctypes
:
>>> from ctypes import c_int, WINFUNCTYPE, windll >>> from ctypes.wintypes import HWND, LPCSTR, UINT >>> prototype = WINFUNCTYPE(c_int, HWND, LPCSTR, LPCSTR, UINT) >>> paramflags = (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", None), (1, "flags", 0) >>> MessageBox = prototype(("MessageBoxA", windll.user32), paramflags) >>>
The MessageBox foreign function can now be called in these ways:
>>> MessageBox() >>> MessageBox(text="Spam, spam, spam") >>> MessageBox(flags=2, text="foo bar") >>>
第 2 个范例演示输出参数。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 类型的实例。
ctypes.
alignment
(
obj_or_type
)
¶
返回 ctypes 类型的对齐要求。 obj_or_type 必须是 ctypes 类型或实例。
ctypes.
byref
(
obj
[
,
offset
]
)
¶
返回的轻量指针指向 obj ,必须是 ctypes 类型的实例。 offset 默认为 0,且必须是将被添加到内部指针值的整数。
byref(obj, offset)
相当于此 C 代码:
(((char *)&obj) + offset)
返回对象只可用作外来函数的调用参数。其行为类似于
pointer(obj)
,但构造要快得多。
ctypes.
cast
(
obj
,
type
)
¶
此函数类似于 C cast 运算符。它返回新实例化的 type ,指向同一内存块如 obj . type 必须是指针类型,且 obj 必须是可以被解释为指针的对象。
ctypes.
create_string_buffer
(
init_or_size
,
size=None
)
¶
此函数创建可变字符缓冲。返回的对象是 ctypes 数组的
c_char
.
init_or_size 必须是指定数组大小的整数,或是用于初始化数组项的字节对象。
If a bytes object is specified as first argument, the buffer is made one item larger than its length so that the last element in the array is a NUL termination character. An integer can be passed as second argument which allows to specify the size of the array if the length of the bytes should not be used.
ctypes.
create_unicode_buffer
(
init_or_size
,
size=None
)
¶
此函数创建可变 Unicode 字符缓冲。返回对象是 ctypes 数组的
c_wchar
.
init_or_size 必须是指定数组大小的整数,或用于初始化数组项的字符串。
If a string is specified as first argument, the buffer is made one item larger than the length of the string so that the last element in the array is a NUL termination character. An integer can be passed as second argument which allows to specify the size of the array if the length of the string should not be used.
ctypes.
DllCanUnloadNow
(
)
¶
Windows only: This function is a hook which allows to implement in-process COM servers with ctypes. It is called from the DllCanUnloadNow function that the _ctypes extension dll exports.
ctypes.
DllGetClassObject
(
)
¶
Windows only: This function is a hook which allows to implement in-process COM servers with ctypes. It is called from the DllGetClassObject function that the
_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_last_error
(
)
¶
仅 Windows:返回 ctypes 私有副本的当前值为系统
LastError
变量在调用线程中。
ctypes.
memmove
(
dst
,
src
,
count
)
¶
如同标准 C memmove 库函数:拷贝 count 字节来自 src to dst . dst and src 必须是整数或 ctypes 实例,可以被转换成指针。
ctypes.
memset
(
dst
,
c
,
count
)
¶
如同标准 C memset 库函数:填充内存块在地址 dst with count 字节的值 c . dst 必须是指定地址的整数,或 ctypes 实例。
ctypes.
POINTER
(
type
)
¶
This factory function creates and returns a new ctypes pointer type. Pointer types are cached an reused internally, so calling this function repeatedly is cheap. type 必须是 ctypes 类型。
ctypes.
pointer
(
obj
)
¶
此函数创建新指针实例,指向
obj
。返回对象的类型为
POINTER(type(obj))
.
注意:若仅仅想要将指向对象的指针传递给外来函数调用,应该使用
byref(obj)
,这快得多。
ctypes.
resize
(
obj
,
size
)
¶
此函数重置内部内存缓冲大小为
obj
,必须是 ctypes 类型的实例。使缓冲比对象类型的本机大小更小是不可能的,如给出通过
sizeof(type(obj))
,但扩大缓冲是可能的。
ctypes.
set_last_error
(
value
)
¶
仅 Windows:设置 ctypes 私有副本的当前值为系统
LastError
变量在调用线程中到
value
并返回先前值。
ctypes.
sizeof
(
obj_or_type
)
¶
以字节为单位返回 ctypes 类型 (或实例内存缓冲) 的大小。所做的如同 C
sizeof
运算符。
ctypes.
string_at
(
address
,
size=-1
)
¶
此函数返回的 C 字符串始于内存地址 address 如 bytes 对象。若 size 有指定,用作大小,否则假定字符串以 0 结尾。
ctypes.
WinError
(
code=None
,
descr=None
)
¶
仅 Windows:此函数可能是 ctypes 中的最差命名。它创建 OSError 实例。若
code
未指定,
GetLastError
被调用以确定错误代码。若
descr
未指定,
FormatError()
被调用以获取错误的正文描述。
3.3 版改变:
实例化的
WindowsError
用于创建。
ctypes.
wstring_at
(
address
,
size=-1
)
¶
此函数返回的宽字符字符串始于内存地址 address 如字符串。若 size 有指定,用作字符串的字符数,否则假定字符串以 0 结尾。
ctypes.
_CData
¶
此非公共类是所有 ctypes 数据类型的公用基类。除其它事情外,所有 ctypes 类型实例均包含保持 C 兼容数据的内存块;内存块地址的返回是通过
addressof()
帮手函数。另一实例变量被暴露成
_objects
;这包含需要在内存块包含指针的情况下,保持存活的其它 Python 对象。
常见 ctypes 数据类型方法都是类方法 (确切地说,这些方法源于 metaclass ):
from_buffer
(
source
[
,
offset
]
)
¶
此方法返回共享缓冲的 ctypes 实例为
source
对象。
source
对象必须支持可写缓冲接口。可选
offset
参数指定到源缓冲的偏移 (以字节为单位);默认为 0。若源缓冲不够大
ValueError
被引发。
from_buffer_copy
(
source
[
,
offset
]
)
¶
此方法创建 ctypes 实例,拷贝缓冲从
source
对象缓冲 (必须可读)。可选
offset
参数指定到源缓冲的偏移 (以字节为单位);默认为 0。若源缓冲不够大
ValueError
被引发。
from_address
(
address
)
¶
此方法返回使用内存的 ctypes 类型实例指定通过 address 其必须为整数。
from_param
(
obj
)
¶
此方法适配
obj
到 ctypes 类型。它与外来函数调用中所用的实际对象一起被调用,当类型呈现在外来函数的
argtypes
元组;它必须返回可以用作函数调用参数的对象。
所有 ctypes 数据类型拥有此类方法的默认实现,通常返回 obj 若那是类型的实例。某些类型还接受其它对象。
in_dll
(
library
,
name
)
¶
此方法返回由共享库导出的 ctypes 类型实例。 name 是导出数据的符号名称, library 是被加载的共享库。
ctypes 数据类型的常见实例变量:
_b_needsfree_
¶
此只读变量为 True 当 ctypes 数据实例本身拥有分配内存块时,否则 False。
_objects
¶
此成员为
None
或字典 (包含需要保持存活的 Python 对象,以便内存块内容保持有效)。此对象只暴露于调试;从不修改此字典的内容。
ctypes.
_SimpleCData
¶
此非公共类是所有基础 ctypes 数据类型的基类。这里提及是因为它包含基础 ctypes 数据类型的公用属性。
_SimpleCData
是子类化的
_CData
,所以它继承了它们的方法和属性。现在可以腌制不包含指针 (或做不包含指针) 的 Ctypes 数据类型。
实例拥有单一属性:
当作为外来函数调用结果返回 (或者,例如:通过检索 Structure 字段成员或数组项) 时,基础数据类型会被透明地转换成本机 Python 类型。换句话说,若外来函数拥有
restype
of
c_char_p
,将始终收到 Python 字节对象,
not
a
c_char_p
实例。
子类化的基础数据类型做
not
继承此行为。因此,若外来函数
restype
是子类化的
c_void_p
,将从函数调用收到此子类的实例。当然,可以获得指针的值通过访问
value
属性。
这些是基础 ctypes 数据类型:
ctypes.
c_byte
¶
表示 C
signed char
数据类型,并将值解释成小整数。构造函数接受可选整数初始化程序;不做溢出校验。
ctypes.
c_char
¶
表示 C
char
数据类型,并将值解释成单个字符。构造函数接受可选字符串初始化程序,字符串的长度必须准确是一字符。
ctypes.
c_char_p
¶
表示 C
char *
数据类型当它指向以 0 结尾的字符串时。对于还可以指向二进制数据的一般字符指针,
POINTER(c_char)
必须使用。构造函数接受整数地址,或字节对象。
ctypes.
c_double
¶
表示 C
double
数据类型。构造函数接受可选浮点初始化器。
ctypes.
c_longdouble
¶
表示 C
long double
数据类型。构造函数接受可选浮点初始化程序。当平台
sizeof(long double) ==
sizeof(double)
,它是别名化的
c_double
.
ctypes.
c_float
¶
表示 C
float
数据类型。构造函数接受可选浮点初始化器。
ctypes.
c_int
¶
表示 C
signed int
数据类型。构造函数接受可选整数初始化程序;不做溢出校验。当平台
sizeof(int) == sizeof(long)
,它是别名化的
c_long
.
ctypes.
c_int64
¶
表示 C 64 位
signed int
数据类型。通常别名为
c_longlong
.
ctypes.
c_long
¶
表示 C
signed long
数据类型。构造函数接受可选整数初始化程序;不做溢出校验。
ctypes.
c_longlong
¶
表示 C
signed long long
数据类型。构造函数接受可选整数初始化程序;不做溢出校验。
ctypes.
c_short
¶
表示 C
signed short
数据类型。构造函数接受可选整数初始化程序;不做溢出校验。
ctypes.
c_size_t
¶
表示 C
size_t
数据类型。
ctypes.
c_ssize_t
¶
表示 C
ssize_t
数据类型。
3.2 版新增。
ctypes.
c_ubyte
¶
表示 C
unsigned char
数据类型,它将值解释成小整数。构造函数接受可选整数初始化程序;不做溢出校验。
ctypes.
c_uint
¶
表示 C
unsigned int
数据类型。构造函数接受可选整数初始化程序;不做溢出校验。当平台
sizeof(int) == sizeof(long)
它是别名化的
c_ulong
.
ctypes.
c_uint64
¶
表示 C 64 位
unsigned int
数据类型。通常别名为
c_ulonglong
.
ctypes.
c_ulong
¶
表示 C
unsigned long
数据类型。构造函数接受可选整数初始化程序;不做溢出校验。
ctypes.
c_ulonglong
¶
表示 C
unsigned long long
数据类型。构造函数接受可选整数初始化程序;不做溢出校验。
ctypes.
c_ushort
¶
表示 C
unsigned short
数据类型。构造函数接受可选整数初始化程序;不做溢出校验。
ctypes.
c_void_p
¶
表示 C
void *
类型。值被表示成整数。构造函数接受可选整数初始化程序。
ctypes.
c_wchar
¶
表示 C
wchar_t
数据类型,并将值解释成单个字符的 Unicode 字符串。构造函数接受可选字符串初始化程序,字符串的长度必须准确是一字符。
ctypes.
c_wchar_p
¶
表示 C
wchar_t *
数据类型,必须是指向以 0 结尾的宽字符串的指针。构造函数接受整数地址,或字符串。
ctypes.
c_bool
¶
表示 C
bool
数据类型 (更精确,
_Bool
来自 C99)。其值可以为
True
or
False
,且构造函数接受拥有真值的任何对象。
ctypes.
HRESULT
¶
仅 Windows:表示
HRESULT
值,包含函数 (或方法调用) 的成功或错误信息。
ctypes.
py_object
¶
表示 C
PyObject *
数据类型。调用这不采用自变量创建
NULL
PyObject *
指针。
The
ctypes.wintypes
模块提供了相当多的其它 Windows 特定数据类型,例如
HWND
,
WPARAM
,或
DWORD
。某些有用结构像
MSG
or
RECT
也有定义。
ctypes.
Union
(
*args
,
**kw
)
¶
用于 Union 的抽象基类,按本机字节序。
ctypes.
BigEndianStructure
(
*args
,
**kw
)
¶
用于 Structure 的抽象基类,按 big endian 字节序。
ctypes.
LittleEndianStructure
(
*args
,
**kw
)
¶
用于 Structure 的抽象基类,按 little endian 字节序。
具有非本机字节序的 Structure,不可以包含指针类型字段 (或包含指针类型字段的任何其它数据类型)。
ctypes.
Structure
(
*args
,
**kw
)
¶
用于 Structure 的抽象基类,按 native 字节序。
具体 Structure 和 Union 类型必须通过子类化这些类型之一来创建,且至少有定义
_fields_
类变量。
ctypes
将创建
descriptor
,允许通过直接访问属性读取和写入字段。这些是
_fields_
¶
定义 Structure 字段的序列。项必须是 2 元素元组 (或 3 元素元组)。第 1 项是字段名,第 2 项指定字段类型;它可以是任何 ctypes 数据类型。
对于整数类型字段像
c_int
,可以给出第 3 可选项。它必须是定义字段位宽的小的正整数。
字段名在一个 Structure 或 Union 中必须唯一。这不校验,只可以访问一字段当名称重复时。
它是可能的定义
_fields_
类变量
after
the class statement that defines the Structure subclass, this allows to create data types that directly or indirectly reference themselves:
class List(Structure): pass List._fields_ = [("pnext", POINTER(List)), ... ]
The
_fields_
类变量必须定义,不管怎样,在第一次使用类型之前 (创建实例,
sizeof()
会调用它,等等)。稍后赋值
_fields_
类变量将引发 AttributeError。
It is possible to defined sub-subclasses of structure types, they inherit the fields of the base class plus the
_fields_
定义在子子类中,若有的话。
_pack_
¶
An optional small integer that allows to override the alignment of structure fields in the instance.
_pack_
必须已定义当
_fields_
被赋值,否则不起作用。
_anonymous_
¶
列出未命名 (匿名) 字段名称的可选序列。
_anonymous_
必须已定义当
_fields_
被赋值,否则不起作用。
此变量中列出的字段,必须是 Structure 或 Union 类型字段。
ctypes
will create descriptors in the structure type that allows to access the nested fields directly, without the need to create the structure or union field.
这里是范例类型 (Windows):
class _U(Union): _fields_ = [("lptdesc", POINTER(TYPEDESC)), ("lpadesc", POINTER(ARRAYDESC)), ("hreftype", HREFTYPE)] class TYPEDESC(Structure): _anonymous_ = ("u",) _fields_ = [("u", _U), ("vt", VARTYPE)]
The
TYPEDESC
结构描述 COM (组件对象模型) 数据类型,
vt
字段指定哪个 Union 字段有效。由于
u
字段被定义成匿名字段,关闭 TYPEDESC 实例直接访问成员现在是可能的。
td.lptdesc
and
td.u.lptdesc
等价,但前者更快,由于它不需要创建临时 Union 实例:
td = TYPEDESC() td.vt = VT_PTR td.lptdesc = POINTER(some_type) td.u.lptdesc = POINTER(some_type)
It is possible to defined sub-subclasses of structures, they inherit the fields of the base class. If the subclass definition has a separate
_fields_
变量,其中的指定字段会被追加到基类字段。
Structure 和 Union 构造函数接受位置和关键词自变量两者。位置自变量用于按相同次序初始化成员字段,如它们出现在
_fields_
。构造函数中的关键词自变量被解释成属性赋值,因此它们将初始化
_fields_
采用相同名称,或创建新属性若名称未呈现在
_fields_
.
Not yet written - please see the sections 指针 and section 数组 in the tutorial.
ctypes
— 用于 Python 的外来函数库