ctypes
— A foreign function library for Python¶
Source code: Lib/ctypes
ctypes
— це бібліотека зовнішніх функцій для Python. Він надає C-сумісні типи даних і дозволяє викликати функції в DLL або спільних бібліотеках. Його можна використовувати, щоб обернути ці бібліотеки в чистий Python.
підручник ctypes¶
Примітка: зразки коду в цьому підручнику використовують doctest
, щоб переконатися, що вони справді працюють. Оскільки деякі зразки коду поводяться по-різному в Linux, Windows або macOS, вони містять директиви doctest у коментарях.
Примітка. Деякі зразки коду посилаються на тип ctypes c_int
. На платформах, де sizeof(long) == sizeof(int)
, це псевдонім c_long
. Отже, вас не повинно бентежити, якщо надруковано c_long
, якщо ви очікуєте c_int
— вони насправді одного типу.
Завантаження бібліотек динамічних посилань¶
ctypes
експортує cdll, а в Windows об’єкти windll і oledll для завантаження бібліотек динамічних посилань.
You load libraries by accessing them as attributes of these objects. cdll
loads libraries which export functions using the standard cdecl
calling
convention, while windll libraries call functions using the stdcall
calling convention. oledll also uses the stdcall
calling convention, and
assumes the functions return a Windows HRESULT
error code. The error
code is used to automatically raise an OSError
exception when the
function call fails.
Змінено в версії 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
.
On Linux, it is required to specify the filename including the extension to
load a library, so attribute access can not be used to load libraries. Either the
LoadLibrary()
method of the dll loaders should be used, or you should load
the library by creating an instance of CDLL by calling the constructor:
>>> 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:
>>> 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
>>>
Note that win32 system dlls like kernel32
and user32
often export ANSI
as well as UNICODE versions of a function. The UNICODE version is exported with
an W
appended to the name, while the ANSI version is exported with an A
appended to the name. The win32 GetModuleHandle
function, which returns a
module handle for a given module name, has the following C prototype, and a
macro is used to expose one of them as GetModuleHandle
depending on whether
UNICODE is defined or not:
/* ANSI version */
HMODULE GetModuleHandleA(LPCSTR lpModuleName);
/* UNICODE version */
HMODULE GetModuleHandleW(LPCWSTR lpModuleName);
windll не намагається вибрати одну з них магією, ви повинні отримати доступ до потрібної вам версії, явно вказавши 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
>>>
Функції виклику¶
You can call these functions like any other Python callable. This example uses
the time()
function, which returns system time in seconds since the Unix
epoch, and the GetModuleHandleA()
function, which returns a win32 module
handle.
This example calls both functions with a NULL
pointer (None
should be used
as the NULL
pointer):
>>> 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
, integers, bytes objects and (unicode) strings are the only native
Python objects that can directly be used as parameters in these function calls.
None
is passed as a C NULL
pointer, bytes objects and strings are passed
as pointer to the memory block that contains their data (char* or
wchar_t*). Python integers are passed as the platforms default C
int type, their value is masked to fit into the C type.
Перш ніж перейти до виклику функцій з іншими типами параметрів, ми повинні дізнатися більше про типи даних ctypes
.
Основні типи даних¶
ctypes
визначає ряд примітивних C-сумісних типів даних:
тип ctypes |
тип С |
Тип Python |
---|---|---|
_Bool |
bool (1) |
|
char |
1-символьний об’єкт байтів |
|
|
1-символьний рядок |
|
char |
внутр |
|
unsigned char |
внутр |
|
short |
внутр |
|
unsigned short |
внутр |
|
int |
внутр |
|
unsigned int |
внутр |
|
long |
внутр |
|
unsigned long |
внутр |
|
__int64 or long long |
внутр |
|
unsigned __int64 or unsigned long long |
внутр |
|
|
внутр |
|
|
внутр |
|
float |
плавати |
|
double |
плавати |
|
long double |
плавати |
|
char* (NUL terminated) |
bytes або |
|
wchar_t* (NUL terminated) |
рядок або |
|
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'
>>>
The create_string_buffer()
function replaces the old c_buffer()
function (which is still available as an alias). To create a mutable memory
block containing unicode characters of the C type wchar_t
, use the
create_unicode_buffer()
function.
Функції виклику, продовження¶
Зауважте, що printf друкує на справжній стандартний канал виводу, а не на 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: 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
>>>
Calling variadic functions¶
On a lot of platforms calling variadic functions through ctypes is exactly the same as calling functions with a fixed number of parameters. On some platforms, and in particular ARM64 for Apple Platforms, the calling convention for variadic functions is different than that for regular functions.
On those platforms it is required to specify the argtypes attribute for the regular, non-variadic, function arguments:
libc.printf.argtypes = [ctypes.c_char_p]
Because specifying the attribute does not inhibit portability it is advised to always
specify argtypes
for all variadic functions.
Виклик функцій із власними типами даних¶
You can also customize ctypes
argument conversion to allow instances of
your own classes be used as function arguments. ctypes
looks for an
_as_parameter_
attribute and uses this as the function argument. The
attribute must be an integer, string, bytes, a ctypes
instance, or an
object with an _as_parameter_
attribute:
>>> 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
>>>
If you don’t want to store the instance’s data in the _as_parameter_
instance variable, you could define a property
which makes the
attribute available on request.
Вказівка необхідних типів аргументів (прототипів функцій)¶
It is possible to specify the required argument types of functions exported from
DLLs by setting the argtypes
attribute.
argtypes
must be a sequence of C data types (the printf
function is
probably not a good example here, because it takes a variable number and
different types of parameters depending on the format string, on the other hand
this is quite handy to experiment with this feature):
>>> 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: TypeError: wrong type
>>> printf(b"%s %d %f\n", b"X", 2, 3)
X 2 3.000000
13
>>>
If you have defined your own classes which you pass to function calls, you have
to implement a from_param()
class method for them to be able to use them
in the argtypes
sequence. The from_param()
class method receives
the Python object passed to the function call, it should do a typecheck or
whatever is needed to make sure this object is acceptable, and then return the
object itself, its _as_parameter_
attribute, or whatever you want to
pass as the C function argument in this case. Again, the result should be an
integer, string, bytes, a ctypes
instance, or an object with an
_as_parameter_
attribute.
Типи повернення¶
By default functions are assumed to return the C int type. Other
return types can be specified by setting the restype
attribute of the
function object.
Here is a more advanced example, it uses the strchr
function, which expects
a string pointer and a char, and returns a pointer to a string:
>>> 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
>>>
If you want to avoid the ord("x")
calls above, you can set the
argtypes
attribute, and the second argument will be converted from a
single character Python bytes object into a 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: TypeError: one character string expected
>>> print(strchr(b"abcdef", b"x"))
None
>>> strchr(b"abcdef", b"d")
'def'
>>>
You can also use a callable Python object (a function or a class for example) as
the restype
attribute, if the foreign function returns an integer. The
callable will be called with the integer the C function returns, and the
result of this call will be used as the result of your function call. This is
useful to check for error return values and automatically raise an exception:
>>> 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()
, щоб отримати його.
Please note that a much more powerful error checking mechanism is available
through the errcheck
attribute; see the reference manual for details.
Передача покажчиків (або: передача параметрів за посиланням)¶
Іноді функція 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'
>>>
Структури та спілки¶
Structures and unions must derive from the Structure
and Union
base classes which are defined in the ctypes
module. Each subclass must
define a _fields_
attribute. _fields_
must be a list of
2-tuples, containing a field name and a field type.
Тип поля має бути типу ctypes
, наприклад c_int
, або будь-якого іншого похідного типу ctypes
: структура, об’єднання, масив, покажчик.
Ось простий приклад структури POINT, яка містить два цілі числа з іменами 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, яка містить дві ТОЧКИ з іменами upperleft і 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))
Поля descriptors можна отримати з класу, вони корисні для налагодження, оскільки можуть надати корисну інформацію:
>>> 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, бібліотека не гарантує роботу в загальному випадку. Об’єднання та структури з бітовими полями слід завжди передавати функціям за вказівником.
Вирівнювання структури/об’єднання та порядок байтів¶
By default, Structure and Union fields are aligned in the same way the C
compiler does it. It is possible to override this behavior by specifying a
_pack_
class attribute in the subclass definition. This must be set to a
positive integer and specifies the maximum alignment for the fields. This is
what #pragma pack(n)
also does in MSVC.
ctypes
використовує власний порядок байтів для структур і об’єднань. Щоб створити структури з невласним порядком байтів, ви можете використовувати один із базових класів BigEndianStructure
, LittleEndianStructure
, BigEndianUnion
і LittleEndianUnion
. Ці класи не можуть містити поля вказівників.
Бітові поля в структурах і об’єднаннях¶
It is possible to create structures and unions containing bit fields. Bit fields
are only possible for integer fields, the bit width is specified as the third
item in the _fields_
tuples:
>>> 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()
для типу 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 і знаєте, що вказівник насправді вказує на масив, а не на один елемент.
За лаштунками функція 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
>>>
Перетворення типів¶
Usually, ctypes does strict type checking. This means, if you have
POINTER(c_int)
in the argtypes
list of a function or as the type of
a member field in a structure definition, only instances of exactly the same
type are accepted. There are some exceptions to this rule, where ctypes accepts
other objects. For example, you can pass compatible array instances instead of
pointer types. So, for POINTER(c_int)
, ctypes accepts an array of 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
>>>
In addition, if a function argument is explicitly declared to be a pointer type
(such as POINTER(c_int)
) in argtypes
, an object of the pointed
type (c_int
in this case) can be passed to the function. ctypes will apply
the required byref()
conversion in this case automatically.
Щоб встановити для поля типу 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 <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 вони визначені прямими оголошеннями, які визначені пізніше:
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
>>>
because the new class cell
is not available in the class statement itself.
In ctypes
, we can define the cell
class and set the _fields_
attribute later, after the class statement:
>>> from ctypes import *
>>> class cell(Structure):
... pass
...
>>> cell._fields_ = [("name", c_char_p),
... ("next", POINTER(cell))]
>>>
Давайте спробуємо. Ми створюємо два екземпляри cell
і дозволяємо їм вказувати один на одного, і, нарешті, слідуємо за ланцюжком вказівників кілька разів:
>>> c1 = cell()
>>> c1.name = b"foo"
>>> c2 = cell()
>>> c2.name = b"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
дозволяє створювати вказівники на функції C із викликів Python. Іноді їх називають функціями зворотного виклику.
По-перше, ви повинні створити клас для функції зворотного виклику. Клас знає угоду про виклики, тип повернення, а також кількість і типи аргументів, які ця функція отримає.
Фабрична функція CFUNCTYPE()
створює типи для функцій зворотного виклику за допомогою угоди про виклики cdecl
. У Windows фабрична функція WINFUNCTYPE()
створює типи для функцій зворотного виклику за допомогою угоди про виклики stdcall
.
Обидві ці фабричні функції викликаються з типом результату як першим аргументом, а функції зворотного виклику – очікуваними типами аргументів як рештою аргументів.
I will present an example here which uses the standard C library’s
qsort()
function, that is used to sort items with the help of a callback
function. qsort()
will be used to sort an array of integers:
>>> IntArray5 = c_int * 5
>>> ia = IntArray5(5, 1, 7, 33, 99)
>>> qsort = libc.qsort
>>> qsort.restype = None
>>>
qsort()
must be called with a pointer to the data to sort, the number of
items in the data array, the size of one item, and a pointer to the comparison
function, the callback. The callback will then be called with two pointers to
items, and it must return a negative integer if the first item is smaller than
the second, a zero if they are equal, and a positive integer otherwise.
Таким чином, наша функція зворотного виклику отримує покажчики на цілі числа та повинна повертати ціле число. Спочатку ми створюємо 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
не збережуться в різних зворотних викликах, навіть якщо ці виклики здійснюються з того самого потоку C.
Доступ до значень, експортованих із dll¶
Some shared libraries not only export functions, they also export variables. An
example in the Python library itself is the Py_OptimizeFlag
, an integer
set to 0, 1, or 2, depending on the -O
or -OO
flag given on
startup.
ctypes
can access values like this with the in_dll()
class methods of
the type. pythonapi is a predefined symbol giving access to the Python C
api:
>>> opt_flag = c_int.in_dll(pythonapi, "Py_OptimizeFlag")
>>> print(opt_flag)
c_long(0)
>>>
If the interpreter would have been started with -O
, the sample would
have printed c_long(1)
, or c_long(2)
if -OO
would have been
specified.
Розширений приклад, який також демонструє використання покажчиків, звертається до покажчика PyImport_FrozenModules
, експортованого Python.
Цитування документів для цього значення:
This pointer is initialized to point to an array of
_frozen
records, terminated by one whose members are allNULL
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),
... ("get_code", POINTER(c_ubyte)), # Function pointer
... ]
...
>>>
We have defined the _frozen
data type, so we can get the pointer
to the table:
>>> FrozenTable = POINTER(struct_frozen)
>>> table = FrozenTable.in_dll(pythonapi, "_PyImport_FrozenBootstrap")
>>>
Оскільки table
є покажчиком
на масив 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
zipimport 12345
>>>
Той факт, що стандартний 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
не має очікуваного ефекту.
Майте на увазі, що отримання підоб’єктів зі структур, об’єднань і масивів не копіює підоб’єкт, натомість отримує обгортку об’єкта, який отримує доступ до основного буфера кореневого об’єкта.
Інший приклад, який може поводитися інакше, ніж очікувалося, це:
>>> 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, що мають доступ до вмісту пам’яті. Зберігання об’єкта Python у блоці пам’яті не зберігає сам об’єкт, натомість зберігається вміст
об’єкта. Повторний доступ до вмісту щоразу створює новий об’єкт Python!
Типи даних змінного розміру¶
ctypes
забезпечує деяку підтримку для масивів і структур змінного розміру.
Функцію resize()
можна використовувати для зміни розміру буфера пам’яті існуючого об’єкта ctypes. Функція приймає об’єкт як перший аргумент, а запитуваний розмір у байтах як другий аргумент. Блок пам’яті не може бути меншим, ніж природний блок пам’яті, визначений типом об’єкта, 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¶
Assign a ctypes type to specify the result type of the foreign function. Use
None
for void, a function not returning anything.It is possible to assign a callable Python object that is not a ctypes type, in this case the function is assumed to return a C int, and the callable will be called with this integer, allowing further processing or error checking. Using this is deprecated, for more flexible post processing or error checking use a ctypes data type as
restype
and assign a callable to theerrcheck
attribute.
- argtypes¶
Призначте кортеж типів ctypes, щоб указати типи аргументів, які приймає функція. Функції, які використовують угоду про виклики
stdcall
, можуть бути викликані лише з тією ж кількістю аргументів, що й довжина цього кортежу; функції, що використовують угоду про виклики C, також приймають додаткові, невизначені аргументи.When a foreign function is called, each actual argument is passed to the
from_param()
class method of the items in theargtypes
tuple, this method allows adapting the actual argument to an object that the foreign function accepts. For example, ac_char_p
item in theargtypes
tuple will convert a string passed as argument into a bytes object using ctypes conversion rules.New: It is now possible to put items in argtypes which are not ctypes types, but each item must have a
from_param()
method which returns a value usable as argument (integer, string, ctypes instance). This allows defining adapters that can adapt custom objects as function parameters.
- errcheck¶
Призначте цьому атрибуту функцію Python або інший виклик. Викликається з трьома або більше аргументами:
- callable(result, func, arguments)
result is what the foreign function returns, as specified by the
restype
attribute.func — це сам зовнішній об’єкт функції, це дозволяє повторно використовувати той самий об’єкт, що викликається, для перевірки або постобробки результатів кількох функцій.
Аргументи — це кортеж, що містить параметри, спочатку передані до виклику функції, що дозволяє спеціалізувати поведінку на використовуваних аргументах.
Об’єкт, який повертає ця функція, буде повернено з виклику зовнішньої функції, але він також може перевірити значення результату та викликати виняток, якщо виклик зовнішньої функції не вдався.
- exception ctypes.ArgumentError¶
Цей виняток виникає, коли зовнішній виклик функції не може перетворити один із переданих аргументів.
On Windows, when a foreign function call raises a system exception (for
example, due to an access violation), it will be captured and replaced with
a suitable Python exception. Further, an auditing event
ctypes.seh_exception
with argument code
will be raised, allowing an
audit hook to replace the exception with its own.
Деякі способи виклику зовнішніх викликів функцій можуть викликати подію аудиту ctypes.call_function
з аргументами вказівник функції
і аргументи
.
Прототипи функцій¶
Сторонні функції також можуть бути створені шляхом інстанціювання прототипів функцій. Прототипи функцій подібні до прототипів функцій у C; вони описують функцію (тип повернення, типи аргументів, угоду про виклики) без визначення реалізації. Фабричні функції мають викликатися з потрібним типом результату та типами аргументів функції, і їх можна використовувати як фабрики декораторів, і як такі застосовувати до функцій за допомогою синтаксису @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
. Функція звільнить GIL під час виклику. use_errno і use_last_error мають те саме значення, що й вище.
- ctypes.PYFUNCTYPE(restype, *argtypes)¶
Повернений прототип функції створює функції, які використовують угоду про виклики Python. Функція не звільняє GIL під час виклику.
Прототипи функцій, створені цими фабричними функціями, можуть бути створені різними способами, залежно від типу та кількості параметрів у виклику:
- prototype(address)
Повертає зовнішню функцію за вказаною адресою, яка має бути цілим числом.
- prototype(callable)
Створіть функцію виклику C (функцію зворотного виклику) з Python callable.
- prototype(func_spec[, paramflags])
Повертає зовнішню функцію, експортовану спільною бібліотекою. func_spec має бути 2-кортежем
(name_or_ordinal, library)
. Перший елемент — це ім’я експортованої функції у вигляді рядка або порядковий номер експортованої функції у вигляді малого цілого числа. Другий елемент — екземпляр спільної бібліотеки.
- prototype(vtbl_index, name[, paramflags[, iid]])
Повертає зовнішню функцію, яка викликає метод COM. vtbl_index — це індекс у таблиці віртуальних функцій, мале невід’ємне ціле число. ім’я — це ім’я методу COM. iid — це додатковий покажчик на ідентифікатор інтерфейсу, який використовується в розширеному звіті про помилки.
COM methods use a special calling convention: They require a pointer to the COM interface as first argument, in addition to those parameters that are specified in the
argtypes
tuple.
Необов’язковий параметр paramflags створює зовнішні обгортки функцій із набагато більшою функціональністю, ніж функції, описані вище.
paramflags must be a tuple of the same length as argtypes
.
Кожен елемент у цьому кортежі містить додаткову інформацію про параметр, це має бути кортеж, що містить один, два або три елементи.
Перший елемент є цілим числом, що містить комбінацію прапорів напрямку для параметра:
- 1
Визначає вхідний параметр для функції.
- 2
Вихідний параметр. Стороння функція заповнює значення.
- 4
Вхідний параметр, який за умовчанням дорівнює нулю.
Другим необов’язковим елементом є назва параметра у вигляді рядка. Якщо це вказано, зовнішня функція може бути викликана з іменованими параметрами.
Додатковий третій елемент є значенням за замовчуванням для цього параметра.
The following example demonstrates how to wrap the Windows MessageBoxW
function so
that it supports default parameters and named arguments. The C declaration from
the windows header file is this:
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")
Другий приклад демонструє вихідні параметри. Функція GetWindowRect
win32 отримує розміри вказаного вікна шляхом копіювання їх у структуру 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 під час виклику.
Output parameters can be combined with the errcheck
protocol to do
further output processing and error checking. The win32 GetWindowRect
api
function returns a BOOL
to signal success or failure, so this function could
do the error checking, and raises an exception when the api call failed:
>>> def errcheck(result, func, args):
... if not result:
... raise WinError()
... return args
...
>>> GetWindowRect.errcheck = errcheck
>>>
If the errcheck
function returns the argument tuple it receives
unchanged, ctypes
continues the normal processing it does on the output
parameters. If you want to return a tuple of window coordinates instead of a
RECT
instance, you can retrieve the fields in the function and return them
instead, the normal processing will no longer take place:
>>> 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.addressof
з аргументомobj
.
- ctypes.alignment(obj_or_type)¶
Повертає вимоги до вирівнювання типу ctypes. obj_or_type має бути типом або екземпляром ctypes.
- ctypes.byref(obj[, offset])¶
Повертає легкий вказівник на obj, який має бути екземпляром типу ctypes. offset за замовчуванням дорівнює нулю і має бути цілим числом, яке буде додано до значення внутрішнього покажчика.
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)¶
Ця функція створює змінний символьний буфер. Повернений об’єкт є масивом ctypes
c_char
.init_or_size має бути цілим числом, яке визначає розмір масиву, або об’єктом bytes, який використовуватиметься для ініціалізації елементів масиву.
Якщо в якості першого аргументу вказано об’єкт bytes, буфер стає на один елемент більшим, ніж його довжина, так що останній елемент у масиві є завершальним символом NUL. Ціле число може бути передане як другий аргумент, що дозволяє вказати розмір масиву, якщо довжина байтів не повинна використовуватися.
Викликає подію аудиту
ctypes.create_string_buffer
з аргументамиinit
,size
.
- ctypes.create_unicode_buffer(init_or_size, size=None)¶
Ця функція створює змінний буфер символів Unicode. Повернений об’єкт є масивом ctypes
c_wchar
.init_or_size має бути цілим числом, що вказує розмір масиву, або рядком, який використовуватиметься для ініціалізації елементів масиву.
Якщо рядок вказано як перший аргумент, буфер робиться на один елемент більшим, ніж довжина рядка, так що останній елемент у масиві є завершальним символом NUL. Ціле число може бути передане як другий аргумент, що дозволяє вказати розмір масиву, якщо довжина рядка не повинна використовуватися.
Викликає подію аудиту
ctypes.create_unicode_buffer
з аргументамиinit
,size
.
- ctypes.DllCanUnloadNow()¶
Лише для Windows: ця функція є хуком, який дозволяє реалізувати COM-сервери в процесі за допомогою ctypes. Він викликається з функції DllCanUnloadNow, яку експортує dll розширення _ctypes.
- ctypes.DllGetClassObject()¶
Лише для Windows: ця функція є хуком, який дозволяє реалізувати COM-сервери в процесі за допомогою ctypes. Він викликається з функції DllGetClassObject, яку експортує dll розширення
_ctypes
.
- ctypes.util.find_library(name)¶
Спробуйте знайти бібліотеку та повернути шлях. ім’я — це ім’я бібліотеки без будь-яких префіксів, як-от
lib
, суфіксів, як-от.so
,.dylib
або номера версії (це форма, яка використовується для параметра компонувальника posix-l
). Якщо бібліотеки не знайдено, повертаєNone
.Точна функція залежить від системи.
- ctypes.util.find_msvcrt()¶
Лише для Windows: повертає назву файлу бібліотеки середовища виконання VC, яка використовується Python, і модулями розширення. Якщо назва бібліотеки не може бути визначена, повертається
None
.Якщо вам потрібно звільнити пам’ять, наприклад, виділену модулем розширення за допомогою виклику
free(void *)
, важливо, щоб ви використовували функцію в тій самій бібліотеці, яка виділила пам’ять.
- ctypes.FormatError([code])¶
Лише для Windows: повертає текстовий опис коду помилки code. Якщо код помилки не вказано, останній код помилки використовується під час виклику функції Windows API GetLastError.
- ctypes.GetLastError()¶
Windows only: Returns the last error code set by Windows in the calling thread. This function calls the Windows
GetLastError()
function directly, it does not return the ctypes-private copy of the error code.
- ctypes.get_errno()¶
Повертає поточне значення ctypes-private копії системної змінної
errno
у потоці виклику.Викликає подію аудиту
ctypes.get_errno
без аргументів.
- ctypes.get_last_error()¶
Windows only: returns the current value of the ctypes-private copy of the system
LastError
variable in the calling thread.Викликає подію аудиту
ctypes.get_last_error
без аргументів.
- ctypes.memmove(dst, src, count)¶
Те саме, що стандартна функція бібліотеки C memmove: копіює count байти з src до dst. dst і src мають бути цілими числами або екземплярами ctypes, які можна перетворити на покажчики.
- ctypes.memset(dst, c, count)¶
Те саме, що стандартна функція бібліотеки memset C: заповнює блок пам’яті за адресою dst count байтами зі значенням c. dst має бути цілим числом, що визначає адресу, або екземпляр ctypes.
- ctypes.POINTER(type, /)¶
Create and return a new ctypes pointer type. Pointer types are cached and reused internally, so calling this function repeatedly is cheap. type must be a ctypes type.
- ctypes.pointer(obj, /)¶
Create a new pointer instance, pointing to obj. The returned object is of the type
POINTER(type(obj))
.Примітка. Якщо ви просто хочете передати вказівник на об’єкт у зовнішній виклик функції, вам слід використовувати
byref(obj)
, що набагато швидше.
- ctypes.resize(obj, size)¶
Ця функція змінює розмір буфера внутрішньої пам’яті obj, який має бути екземпляром типу ctypes. Неможливо зробити буфер меншим, ніж власний розмір типу об’єктів, як задано
sizeof(type(obj))
, але можна збільшити буфер.
- ctypes.set_errno(value)¶
Установіть поточне значення ctypes-private копії системної змінної
errno
у викликаючому потоці на value і поверніть попереднє значення.Викликає подію аудиту
ctypes.set_errno
з аргументомerrno
.
- ctypes.set_last_error(value)¶
Windows only: set the current value of the ctypes-private copy of the system
LastError
variable in the calling thread to value and return the previous value.Викликає подію аудиту
ctypes.set_last_error
з аргументомпомилка
.
- ctypes.sizeof(obj_or_type)¶
Повертає розмір у байтах типу ctypes або буфера пам’яті примірника. Діє так само, як і оператор C
sizeof
.
- ctypes.string_at(address, size=-1)¶
This function returns the C string starting at memory address address as a bytes object. If size is specified, it is used as size, otherwise the string is assumed to be zero-terminated.
Raises an auditing event
ctypes.string_at
with argumentsaddress
,size
.
- ctypes.WinError(code=None, descr=None)¶
Windows only: this function is probably the worst-named thing in ctypes. It creates an instance of
OSError
. If code is not specified,GetLastError
is called to determine the error code. If descr is not specified,FormatError()
is called to get a textual description of the error.Змінено в версії 3.3: An instance of
WindowsError
used to be created, which is now an alias ofOSError
.
- ctypes.wstring_at(address, size=-1)¶
This function returns the wide character string starting at memory address address as a string. If size is specified, it is used as the number of characters of the string, otherwise the string is assumed to be zero-terminated.
Raises an auditing event
ctypes.wstring_at
with argumentsaddress
,size
.
Типи даних¶
- class ctypes._CData¶
Цей непублічний клас є загальним базовим класом для всіх типів даних ctypes. Серед іншого, усі екземпляри типу ctypes містять блок пам’яті, який містить C-сумісні дані; адреса блоку пам’яті повертається допоміжною функцією
addressof()
. Інша змінна екземпляра представлена як_objects
; це містить інші об’єкти Python, які потрібно підтримувати в активному стані, якщо блок пам’яті містить покажчики.Загальні методи типів даних ctypes, це всі методи класу (точніше, це методи metaclass):
- from_buffer(source[, offset])¶
Цей метод повертає екземпляр ctypes, який спільно використовує буфер об’єкта source. Об’єкт джерело має підтримувати інтерфейс буфера з можливістю запису. Необов’язковий параметр offset визначає зсув у вихідному буфері в байтах; за замовчуванням дорівнює нулю. Якщо вихідний буфер недостатньо великий, виникає
ValueError
.Викликає подію аудиту
ctypes.cdata/buffer
з аргументамиpointer
,size
,offset
.
- from_buffer_copy(source[, offset])¶
Цей метод створює екземпляр ctypes, копіюючи буфер із буфера джерельного об’єкта, який має бути читабельним. Необов’язковий параметр offset визначає зсув у вихідному буфері в байтах; за замовчуванням дорівнює нулю. Якщо вихідний буфер недостатньо великий, виникає
ValueError
.Викликає подію аудиту
ctypes.cdata/buffer
з аргументамиpointer
,size
,offset
.
- from_address(address)¶
Цей метод повертає екземпляр типу ctypes, використовуючи пам’ять, визначену адресою, яка має бути цілим числом.
Цей метод та інші, які опосередковано викликають цей метод, викликають подію аудиту
ctypes.cdata
з аргументомадреса
.
- from_param(obj)¶
This method adapts obj to a ctypes type. It is called with the actual object used in a foreign function call when the type is present in the foreign function’s
argtypes
tuple; it must return an object that can be used as a function call parameter.Усі типи даних ctypes мають стандартну реалізацію цього методу класу, який зазвичай повертає obj, якщо це екземпляр типу. Деякі типи також приймають інші об’єкти.
- in_dll(library, name)¶
Цей метод повертає екземпляр типу ctypes, експортований спільною бібліотекою. name — це ім’я символу, який експортує дані, library — це завантажена спільна бібліотека.
Загальні змінні екземплярів типів даних ctypes:
- _b_base_¶
Іноді екземпляри даних ctypes не володіють блоком пам’яті, який вони містять, натомість вони спільно використовують частину блоку пам’яті базового об’єкта. Член
_b_base_
лише для читання є кореневим об’єктом ctypes, якому належить блок пам’яті.
- _b_needsfree_¶
Ця змінна лише для читання є істиною, коли екземпляр даних ctypes сам виділив блок пам’яті, інакше – false.
- _objects¶
Цей член або
None
, або словник, що містить об’єкти Python, які потрібно підтримувати в активному стані, щоб вміст блоку пам’яті залишався дійсним. Цей об’єкт доступний лише для налагодження; ніколи не змінюйте вміст цього словника.
Основні типи даних¶
- class ctypes._SimpleCData¶
Цей закритий клас є базовим класом усіх основних типів даних ctypes. Він згадується тут, оскільки він містить загальні атрибути основних типів даних ctypes.
_SimpleCData
є підкласом_CData
, тому він успадковує їхні методи та атрибути. Типи даних ctypes, які не є та не містять покажчиків, тепер можна маринувати.Екземпляри мають один атрибут:
- value¶
Цей атрибут містить фактичне значення екземпляра. Для цілочисельних типів і типів покажчиків це ціле число, для типів символів – об’єкт або рядок із одним символом у байтах, для типів покажчиків на символи – це об’єкт або рядок Python bytes.
Коли атрибут
value
отримується з екземпляра ctypes, зазвичай щоразу повертається новий об’єкт.ctypes
не реалізує повернення оригінального об’єкта, завжди створюється новий об’єкт. Те саме стосується всіх інших екземплярів об’єктів ctypes.
Fundamental data types, when returned as foreign function call results, or, for
example, by retrieving structure field members or array items, are transparently
converted to native Python types. In other words, if a foreign function has a
restype
of c_char_p
, you will always receive a Python bytes
object, not a c_char_p
instance.
Subclasses of fundamental data types do not inherit this behavior. So, if a
foreign functions restype
is a subclass of c_void_p
, you will
receive an instance of this subclass from the function call. Of course, you can
get the value of the pointer by accessing the value
attribute.
Ось основні типи даних ctypes:
- class ctypes.c_byte¶
Represents the C signed char datatype, and interprets the value as small integer. The constructor accepts an optional integer initializer; no overflow checking is done.
- class ctypes.c_char¶
Represents the C char datatype, and interprets the value as a single character. The constructor accepts an optional string initializer, the length of the string must be exactly one character.
- class ctypes.c_char_p¶
Represents the C char* datatype when it points to a zero-terminated string. For a general character pointer that may also point to binary data,
POINTER(c_char)
must be used. The constructor accepts an integer address, or a bytes object.
- class ctypes.c_double¶
Represents the C double datatype. The constructor accepts an optional float initializer.
- class ctypes.c_longdouble¶
Represents the C long double datatype. The constructor accepts an optional float initializer. On platforms where
sizeof(long double) == sizeof(double)
it is an alias toc_double
.
- class ctypes.c_float¶
Represents the C float datatype. The constructor accepts an optional float initializer.
- class ctypes.c_int¶
Represents the C signed int datatype. The constructor accepts an optional integer initializer; no overflow checking is done. On platforms where
sizeof(int) == sizeof(long)
it is an alias toc_long
.
- class ctypes.c_int64¶
Represents the C 64-bit signed int datatype. Usually an alias for
c_longlong
.
- class ctypes.c_long¶
Represents the C signed long datatype. The constructor accepts an optional integer initializer; no overflow checking is done.
- class ctypes.c_longlong¶
Represents the C signed long long datatype. The constructor accepts an optional integer initializer; no overflow checking is done.
- class ctypes.c_short¶
Represents the C signed short datatype. The constructor accepts an optional integer initializer; no overflow checking is done.
- class ctypes.c_size_t¶
Представляє тип даних C
size_t
.
- class ctypes.c_ssize_t¶
Представляє тип даних C
ssize_t
.Нове в версії 3.2.
- class ctypes.c_ubyte¶
Represents the C unsigned char datatype, it interprets the value as small integer. The constructor accepts an optional integer initializer; no overflow checking is done.
- class ctypes.c_uint¶
Represents the C unsigned int datatype. The constructor accepts an optional integer initializer; no overflow checking is done. On platforms where
sizeof(int) == sizeof(long)
it is an alias forc_ulong
.
- class ctypes.c_uint16¶
Represents the C 16-bit unsigned int datatype. Usually an alias for
c_ushort
.
- class ctypes.c_uint64¶
Represents the C 64-bit unsigned int datatype. Usually an alias for
c_ulonglong
.
- class ctypes.c_ulong¶
Represents the C unsigned long datatype. The constructor accepts an optional integer initializer; no overflow checking is done.
- class ctypes.c_ulonglong¶
Represents the C unsigned long long datatype. The constructor accepts an optional integer initializer; no overflow checking is done.
- class ctypes.c_ushort¶
Represents the C unsigned short datatype. The constructor accepts an optional integer initializer; no overflow checking is done.
- class ctypes.c_void_p¶
Represents the C void* type. The value is represented as integer. The constructor accepts an optional integer initializer.
- class ctypes.c_wchar¶
Представляє тип даних C
wchar_t
і інтерпретує значення як односимвольний рядок Unicode. Конструктор приймає необов’язковий ініціалізатор рядка, довжина рядка має бути рівно одному символу.
- class ctypes.c_wchar_p¶
Represents the C wchar_t* datatype, which must be a pointer to a zero-terminated wide character string. The constructor accepts an integer address, or a string.
- class ctypes.c_bool¶
Represent the C bool datatype (more accurately, _Bool from C99). Its value can be
True
orFalse
, and the constructor accepts any object that has a truth value.
- class ctypes.HRESULT¶
Windows only: Represents a
HRESULT
value, which contains success or error information for a function or method call.
- class ctypes.py_object¶
Represents the C PyObject* datatype. Calling this without an argument creates a
NULL
PyObject* pointer.
The ctypes.wintypes
module provides quite some other Windows specific
data types, for example HWND
, WPARAM
, or DWORD
. Some
useful structures like MSG
or RECT
are also defined.
Структуровані типи даних¶
- class ctypes.Union(*args, **kw)¶
Абстрактний базовий клас для об’єднань у рідному порядку байтів.
- class ctypes.BigEndianUnion(*args, **kw)¶
Abstract base class for unions in big endian byte order.
Нове в версії 3.11.
- class ctypes.LittleEndianUnion(*args, **kw)¶
Abstract base class for unions in little endian byte order.
Нове в версії 3.11.
- class ctypes.BigEndianStructure(*args, **kw)¶
Абстрактний базовий клас для структур у великому порядку байтів.
- class ctypes.LittleEndianStructure(*args, **kw)¶
Абстрактний базовий клас для структур у порядку байтів little endian.
Structures and unions with non-native byte order cannot contain pointer type fields, or any other data types containing pointer type fields.
- class ctypes.Structure(*args, **kw)¶
Абстрактний базовий клас для структур у власному порядку байтів.
Конкретні типи структури та об’єднання мають бути створені шляхом створення підкласу одного з цих типів і принаймні визначення змінної класу
_fields_
.ctypes
створить descriptors, які дозволяють читати та записувати поля за допомогою прямого доступу до атрибутів. Це- _fields_¶
Послідовність, що визначає поля структури. Елементи мають бути 2-кортежними або 3-кортежними. Перший пункт – ім’я поля, другий – тип поля; це може бути будь-який тип даних ctypes.
Для полів цілого типу, таких як
c_int
, можна вказати третій необов’язковий елемент. Це має бути маленьке позитивне ціле число, що визначає розрядність поля.Імена полів мають бути унікальними в межах однієї структури чи об’єднання. Це не позначено, лише одне поле доступне, якщо імена повторюються.
Можна визначити змінну класу
_fields_
після оператора класу, який визначає підклас Structure, це дозволяє створювати типи даних, які прямо чи опосередковано посилаються на себе:class List(Structure): pass List._fields_ = [("pnext", POINTER(List)), ... ]
Однак змінну класу
_fields_
потрібно визначити перед першим використанням типу (створюється екземпляр, для нього викликаєтьсяsizeof()
і так далі). Пізніше призначення змінній класу_fields_
призведе до помилки AttributeError.Можна визначити під-підкласи структурних типів, вони успадковують поля базового класу плюс
_fields_
, визначені в під-підкласі, якщо такі є.
- _pack_¶
An optional small integer that allows overriding the alignment of structure fields in the instance.
_pack_
must already be defined when_fields_
is assigned, otherwise it will have no effect.
- _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
визначено як анонімне поле, тепер можна отримати доступ до членів безпосередньо з примірника TYPEDESC.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_
.
Масиви та покажчики¶
- class ctypes.Array(*args)¶
Абстрактний базовий клас для масивів.
Рекомендований спосіб створення конкретних типів масивів — це множення будь-якого типу даних
ctypes
на невід’ємне ціле число. Крім того, ви можете створити підклас цього типу та визначити змінні класу_length_
і_type_
. Елементи масиву можна читати та записувати за допомогою стандартного підрядкового і зрізного доступу; для зчитування фрагментів результуючий об’єкт не сам єArray
.- _length_¶
Додатне ціле число, що визначає кількість елементів у масиві. Індекси поза діапазоном призводять до
IndexError
. Буде поверненоlen()
.
- _type_¶
Визначає тип кожного елемента в масиві.
Конструктори підкласу масиву приймають позиційні аргументи, які використовуються для ініціалізації елементів у порядку.
- class ctypes._Pointer¶
Приватний, абстрактний базовий клас для вказівників.
Конкретні типи вказівників створюються шляхом виклику
POINTER()
із типом, на який буде вказувати; це робиться автоматично за допомогоюpointer()
.Якщо вказівник вказує на масив, його елементи можна читати та записувати за допомогою стандартного підрядкового і зрізного доступу. Об’єкти-вказівники не мають розміру, тому
len()
викличеTypeError
. Негативні індекси читатимуться з пам’яті перед вказівником (як у C), а індекси поза межами діапазону, ймовірно, впадуть із порушенням доступу (якщо вам пощастить).- _type_¶
Визначає тип, на який вказується.
- contents¶
Повертає об’єкт, на який вказує вказівник. Призначення цьому атрибуту змінює вказівник на призначений об’єкт.