ctypes
— Python библиотека внешних функций
ctypes
— Python библиотека внешних функций. Она
предоставляет C-совместимые типы данных и позволяет вызывать функции из DLL или
разделяемых библиотек. Её можно использовать для оборачивания этих библиотек в
чистый Python.
Учебник по ctypes
Примечание. Примеры кода в данном учебнике используют doctest
,
чтобы убедиться в их работоспособности. Поскольку некоторые образцы
кода ведут себя по-разному в Linux, Windows или Mac OS X, они содержат
директивы doctest в комментариях.
Примечание. Некоторые примеры кода ссылаются на тип ctypes c_int
. На
платформах, где sizeof(long) == sizeof(int)
является псевдонимом
c_long
. Таким образом, вы не должны запутаться, если будет напечатан
c_long
, а вы ожидаете, что это c_int
,— они на самом деле
одного типа.
Загрузка динамически подключаемых библиотек
ctypes
экспортирует cdll, объекты Windows windll и
oledll для загрузки динамически подключаемых библиотек.
Библиотеки загружаются путём обращения к ним как к атрибутам из этих объектов.
cdll загружает экспортирующие функции библиотеки, используя
стандартное соглашение о вызовах cdecl
, в то время как библиотеки windll
вызывают функции, используя соглашение о вызовах stdcall
. oledll также
использует соглашение о вызовах stdcall
и предполагает, что функции возвращают
Windows HRESULT
код ошибки. Код ошибки используется для
автоматического вызова исключение OSError
, когда вызов функции терпит
неудачу.
Изменено в версии 3.3: Ошибки Windows используются для вызова WindowsError
, который теперь
является алиасом OSError
.
Вот несколько примеров для Windows. Следует отметить, что msvcrt
является
стандартной библиотекой MS C, содержащей большинство стандартных функций C и
использует соглашение о вызовах cdecl:
>>> from ctypes import *
>>> print(windll.kernel32)
<WinDLL 'kernel32', handle ... at ...>
>>> print(cdll.msvcrt)
<CDLL 'msvcrt', handle ... at ...>
>>> libc = cdll.msvcrt
>>>
Windows автоматически добавляет обычный суффикс файла .dll
.
Примечание
Доступ к стандартной библиотеке C через cdll.msvcrt
будет использовать
устаревшую версию библиотеки, которая может быть несовместима с
используемой в Python. По возможности используйте встроенные функции Python или
импортируйте и используйте msvcrt
модуль.
В Linux обязательным требованием, является определение имени файла содержащего расширение
загружаемой библиотеки, таким образом атрибут доступа не может использоваться для
загрузки библиотеки. Следует либо использовать LoadLibrary()
метод загрузчиков dll, либо загрузить библиотеку, создав экземпляр CDLL
путём вызова конструктора:
>>> cdll.LoadLibrary("libc.so.6")
<CDLL 'libc.so.6', handle ... at ...>
>>> libc = CDLL("libc.so.6")
>>> libc
<CDLL 'libc.so.6', handle ... at ...>
>>>
Доступ к функциям из загруженных dll
Функции доступны как атрибуты объектов dll:
>>> from ctypes import *
>>> libc.printf
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.GetModuleHandleA)
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.MyOwnFunction)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "ctypes.py", line 239, in __getattr__
func = _StdcallFuncPtr(name, self)
AttributeError: function 'MyOwnFunction' not found
>>>
Обратите внимание, что win32 системные dll (например, kernel32
и
user32
) часто экспортируют ANSI, а также Юникод версии функции.
Юникод версия экспортируется с добавлением к имени W
, а версия ANSI
экспортируется с добавлением к имени A
. У функции win32 GetModuleHandle
,
которая возвращает обработчик модуля для данного имени модуля, есть следующий прототип
C и макрос, используемый для предоставления одного из них как GetModuleHandle
в
зависимости от того, определен ли Юникод или нет:
/* ANSI версия */
HMODULE GetModuleHandleA(LPCSTR lpModuleName);
/* UNICODE версия */
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
>>>
Вызов функций
Вы можете вызвать эти функции как любую другую Python вызываемую функцию. В
этом примере используется функция time()
, которая возвращает системное время
в секундах с момента начала эпохи Unix, и функция GetModuleHandleA()
,
возвращающая дескриптор модуля win32.
Далее пример вызывает обе функции с NULL
указателем (None
должен
использоваться как указатель на NULL
):
>>> print(libc.time(None))
1150640792
>>> print(hex(windll.kernel32.GetModuleHandleA(None)))
0x1d000000
>>>
Вызывается ValueError
, когда вызывается функция stdcall
с соглашением
о вызовах cdecl
, или наоборот:
>>> cdll.kernel32.GetModuleHandleA(None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with not enough arguments (4 bytes missing)
>>>
>>> windll.msvcrt.printf(b"spam")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with too many arguments (4 bytes in excess)
>>>
Для определения правильного соглашения о вызове необходимо просмотреть файл заголовка C или документацию для вызываемой функции.
В Windows ctypes
использует структурированную обработку исключений win32,
чтобы предотвратить сбои общей защиты при вызове функций с недопустимыми
значениями аргументов:
>>> windll.kernel32.GetModuleHandleA(32)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: exception: access violation reading 0x00000020
>>>
Есть, однако, достаточно способов крушения Python с ctypes
, таким
образом, вы должны проявлять осторожность. Модуль faulthandler
может быть
полезен при отладке сбоев (например, из-за сбоев сегментации, вызванных
ошибочными вызовами библиотеки C).
None
, целые числа, байты объектов и (Юникод) строки являются
единственными собственными объектами Python, которые могут
непосредственно использоваться в качестве параметров в вызовах функции.
None
передается как указатель C NULL
, байты объектов и строки
передаются как указатель на блок памяти, который содержит их данные
(char *
или: c:type:wchar_t *). Python целые числа передаются как тип
платформы по умолчанию C int
, их значение маскируется для
соответствия типу C.
Прежде чем переходить к вызову функций с другими типами параметров, мы должны
узнать больше о ctypes
типах данных.
Фундаментальные типы данных
ctypes
определяет ряд примитивных C-совместимых типов данных:
тип ctypes | тип C | тип Python |
---|---|---|
c_bool |
_Bool |
bool (1) |
c_char |
char |
1-символьный байтовый объект |
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 * (оканчивающийся на NUL) |
байтовый объект или None |
c_wchar_p |
wchar_t *
(оканчивающийся на NUL) |
string или None |
c_void_p |
void * |
int или None |
- Конструктор принимает любой объект с истинным значением.
Все перечисленные типы могут быть созданы путём вызова их с помощью дополнительного инициализатора правильного типа и значения:
>>> 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 неизменяемы):
>>> 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) # место в памяти изменилось
c_wchar_p(139966783348904)
>>> print(c_s.value)
Hi, there
>>> print(s) # первый объект остается неизменным
Hello, World
>>>
Однако следует быть осторожным, чтобы не передавать их функциям, ожидающим
указатели в изменяемой памяти. Если вам нужны изменяемые блоки памяти, ctypes
содержит функцию create_string_buffer()
, которая создаёт их различными
способами. К текущему содержимому блока памяти можно обращаться (или изменять)
с помощью raw
свойства; если вы хотите получить доступ к нему как к
строке оканчивающейся на NUL, используйте свойство value
:
>>> from ctypes import *
>>> p = create_string_buffer(3) # создание 3-байтового буфера, инициализированного NUL байтами
>>> print(sizeof(p), repr(p.raw))
3 b'\x00\x00\x00'
>>> p = create_string_buffer(b"Hello") # создание буфера, содержащего строку оканчивающуюся NUL
>>> print(sizeof(p), repr(p.raw))
6 b'Hello\x00'
>>> print(repr(p.value))
b'Hello'
>>> p = create_string_buffer(b"Hello", 10) # создание 10 байт буфера
>>> print(sizeof(p), repr(p.raw))
10 b'Hello\x00\x00\x00\x00\x00'
>>> p.value = b"Hi"
>>> print(sizeof(p), repr(p.raw))
10 b'Hi\x00lo\x00\x00\x00\x00\x00'
>>>
Функция create_string_buffer()
заменяет функцию c_buffer()
(которая по прежнему доступна
как псевдоним), а также функцию c_string()
из более ранних версий ctypes. Чтобы
создать изменяемый блок памяти, содержащий Юникод символы C типа wchar_t
,
используют функцию create_unicode_buffer()
.
Вызов функций, продолжение
Обратите внимание, что 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: exceptions.TypeError: Don't know how to convert parameter 2
>>>
Как было упомянуто ранее, все типы Python, за исключением целых чисел,
строк и объектов байтов, должны быть упакованы в соответствующий тип ctypes
,
чтобы их можно было преобразовать в требуемый тип данных C:
>>> printf(b"An int %d, a double %f\n", 1234, c_double(3.14))
An int 1234, a double 3.140000
31
>>>
Вызов функций с собственными пользовательскими типами данных
Вы также можете настроить преобразование аргумента ctypes
, чтобы
разрешить использование экземпляров ваших собственных классов в качестве
аргументов функции. ctypes
ищет атрибут _as_parameter_
и
использует его в качестве аргумента функции. Конечно, он должен быть
целым числом, строкой или байтом:
>>> class Bottles:
... def __init__(self, number):
... self._as_parameter_ = number
...
>>> bottles = Bottles(42)
>>> printf(b"%d bottles of beer\n", bottles)
42 bottles of beer
19
>>>
Если не требуется хранить данные экземпляра в переменной _as_parameter_
экземпляра, можно определить property
, которая делает атрибут доступным по
запросу.
Указание требуемых типов аргументов (прототипов функций)
Можно указать требуемые типы аргументов функций, экспортируемых из 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 <module>
ArgumentError: argument 2: exceptions.TypeError: wrong type
>>> printf(b"%s %d %f\n", b"X", 2, 3)
X 2 3.000000
13
>>>
Если вы определили свои передаваемые вызовам функций классы,
то необходимо реализовать метод класса from_param()
, чтобы они
могли использовать их в последовательности argtypes
. Метод класса
from_param()
получает переданный в вызов функции объект Python, далее он
должен выполнить проверку типа или всё, что необходимо, чтобы убедиться, что
этот объект приемлем, а затем вернуть сам объект, его атрибут
_as_parameter_
или всё, что нужно передать в качестве аргумента
C функции. Опять же, результатом должно быть целое число, строка, байты,
экземпляр ctypes
или объект с атрибутом _as_parameter_
.
Возвращаемые типы
По умолчанию предполагается, что функции возвращают C тип int
. Другие
типы возвращаемых значений можно задать, задав атрибут restype
объекта
функции.
Вот более продвинутый пример, использует функцию strchr
, которая ожидает
указатель строки и символ, и возвращает указатель на строку:
>>> strchr = libc.strchr
>>> strchr(b"abcdef", ord("d"))
8059983
>>> strchr.restype = c_char_p # c_char_p указатель на строку
>>> strchr(b"abcdef", ord("d"))
b'def'
>>> print(strchr(b"abcdef", ord("x")))
None
>>>
Если нужно избежать вышеупомянутых вызовов ord("x")
, можно
установить атрибут argtypes
, и второй аргумент будет преобразован из
односимвольного байтового объекта Python в C char:
>>> strchr.restype = c_char_p
>>> strchr.argtypes = [c_char_p, c_char]
>>> strchr(b"abcdef", b"d")
'def'
>>> strchr(b"abcdef", b"def")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ArgumentError: argument 2: exceptions.TypeError: one character string expected
>>> print(strchr(b"abcdef", b"x"))
None
>>> strchr(b"abcdef", b"d")
'def'
>>>
Также можно использовать вызываемый объект Python (например, функцию или
класс) в качестве атрибута restype
, если внешняя функция возвращает
целое число. Вызываемый объект будет вызываться с целым числом, возвращаемым
функцией C, и результат этого вызова будет использоваться как результат вызова
вашей функции. Это полезно для проверки возвращаемых значений ошибок и
автоматического создания исключения:
>>> GetModuleHandle = windll.kernel32.GetModuleHandleA
>>> def ValidHandle(value):
... if value == 0:
... raise WinError()
... return value
...
>>>
>>> GetModuleHandle.restype = ValidHandle
>>> GetModuleHandle(None)
486539264
>>> GetModuleHandle("something silly")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in ValidHandle
OSError: [Errno 126] The specified module could not be found.
>>>
WinError
— это функция, которая вызывает API-интерфейс Windows
FormatMessage()
для получения строкового представления кода ошибки и
возвращения исключения. WinError
принимает необязательный параметр кода
ошибки, если он не используется, она вызывает GetLastError()
для его
получения.
Обратите внимание, что гораздо более мощный механизм проверки ошибок доступен
через атрибут errcheck
; для получения дополнительной информации см.
справочное руководство.
Передача указателей (или передача параметров по ссылке)
Иногда функция C api ожидает указатель на тип данных в качестве параметра, вероятно, для записи в соответствующее местоположение, или если данные слишком велики для передачи значения. Это также известно как передача параметров по ссылке.
ctypes
экспортирует функцию byref()
, которая используется, чтобы
передать параметры по ссылке. Тот же эффект может быть достигнут с функцией
pointer()
, хотя pointer()
делает гораздо больше работы, т. к. она создаёт
реальный объект указателя, поэтому он быстрее использования byref()
, если вы
не нуждаетесь в объекте указателя в самом Python:
>>> i = c_int()
>>> f = c_float()
>>> s = create_string_buffer(b'\000' * 32)
>>> print(i.value, f.value, repr(s.value))
0 0.0 b''
>>> libc.sscanf(b"1 3.14 Hello", b"%d %f %s",
... byref(i), byref(f), s)
3
>>> print(i.value, f.value, repr(s.value))
1 3.1400001049 b'Hello'
>>>
Структуры и объединения
Структуры и объединения должны произойти от базовых Structure
и Union
классов, которые определены в модуле ctypes
. Каждый подкласс должен
определить _fields_
атрибут. _fields_
должен быть списком 2 кортежей,
содержащим имя поля и тип поля.
Тип поля должен быть типом 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, которая содержит два POINT с именами 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))
Поле дескриптора может извлекаться из класса. Они полезны для отладки, т. к. могут предоставить полезную информацию:
>>> print(POINT.x)
<Field type=c_long, ofs=0, size=4>
>>> print(POINT.y)
<Field type=c_long, ofs=4, size=4>
>>>
Предупреждение
ctypes
не поддерживает передачу объединения или структуры с битовыми
полями в функции по значению. Хотя это может работать на 32-разрядных x86,
библиотека не гарантирует работу в общем случае. Объединения и структуры с
битовыми полями всегда должны передаваться функциям по указателю.
Выравнивание структуры/объединения и порядок байтов
По умолчанию поля Structure и Union выравниваются таким же образом, как это
делает компилятор C. Возможно переопределить это поведение, определив _pack_
класс атрибут в определении подкласса. Значение должно быть
положительным целым числом и указывать максимальное выравнивание для полей. Это
то, что #pragma pack(n)
также делает в MSVC.
ctypes
использует собственный порядок байтов для структур и объединений. Для
построения структур с неродным порядком байтов можно использовать один из
BigEndianStructure
, LittleEndianStructure
, BigEndianUnion
и LittleEndianUnion
базовые классы. Эти
классы не могут содержать поля указателя.
Битовые поля в структурах и объединениях
Можно создавать структуры и объединения, содержащие битовые поля. Битовые поля
возможны только для целых полей, битовая ширина указывается в качестве третьего
элемента в кортежах _fields_
:
>>> class Int(Structure):
... _fields_ = [("first_16", c_int, 16),
... ("second_16", c_int, 16)]
...
>>> print(Int.first_16)
<Field type=c_long, ofs=0:0, bits=16>
>>> print(Int.second_16)
<Field type=c_long, ofs=0:16, bits=16>
>>>
Массивы
Массивы — это последовательности, содержащие фиксированное число экземпляров одного типа.
Рекомендуется создавать типы массивов путём умножения типа данных на положительное целое число:
TenPointsArrayType = POINT * 10
Вот пример некоторого искусственного типа данных, структуры, содержащей 4 POINT среди прочего:
>>> 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
экземпляры содержимому указателя атрибута
приведёт к указанию указателя на расположение памяти, в котором он хранится:
>>> 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
>>>
Преобразование типов
Обычно 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 <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; /* прямая декларация */
struct cell {
char *name;
struct cell *next;
};
Прямой перевод в код ctypes будет таким, но он не работает:
>>> class cell(Structure):
... _fields_ = [("name", c_char_p),
... ("next", POINTER(cell))]
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in cell
NameError: name 'cell' is not defined
>>>
потому что новый class cell
недоступен в самом операторе class. В
ctypes
можно определить cell
класс и установить _fields_
атрибут позже, после оператора class:
>>> from ctypes import *
>>> class cell(Structure):
... pass
...
>>> cell._fields_ = [("name", c_char_p),
... ("next", POINTER(cell))]
>>>
Проверим это. Создадим два экземпляра cell
и позволяем им указывать друг на
друга и в завершении, проследовать за цепочкой указателей несколько раз:
>>> c1 = cell()
>>> c1.name = "foo"
>>> c2 = cell()
>>> c2.name = "bar"
>>> c1.next = pointer(c2)
>>> c2.next = pointer(c1)
>>> p = c1
>>> for i in range(8):
... print(p.name, end=" ")
... p = p.next[0]
...
foo bar foo bar foo bar foo bar
>>>
Колбэк функции
ctypes
позволяет создавать указатели вызываемых функции C из
Python вызова. Их иногда называют колбэк функции.
Сначала необходимо создать класс для функции колбэка. Класс знает соглашение о вызове, возвращаемый тип, а также количество и типы аргументов, которые получит функция.
Функция фабрика CFUNCTYPE()
создаёт типы для функций колбэков с
использованием соглашения о вызовах cdecl
. В Windows функция фабрика
WINFUNCTYPE()
создаёт типы для колбэк функций с помощью соглашения о вызовах
stdcall
.
Обе фабричные функции вызываются с типом результата в качестве первого аргумента, а колбэк функции ожидаемого типа аргумента в качестве остальных аргументов.
Я предлагаю рассмотреть использующий стандартную функцию библиотеки C
qsort()
пример, которая используется для сортировки элементов с
помощью колбэк функции. qsort()
будет использоваться для сортировки
массива целых чисел:
>>> IntArray5 = c_int * 5
>>> ia = IntArray5(5, 1, 7, 33, 99)
>>> qsort = libc.qsort
>>> qsort.restype = None
>>>
qsort()
должен вызываться с указателем на сортируемые данные, количество
элементов в массиве данных, размер одного элемента и указатель на функцию
сравнения, т.е. колбэк. Затем колбэк вызывается с двумя указателями на
элементы и должен возвращать отрицательное целое число, если первый элемент
меньше второго, ноль, если они равны, и положительное целое число в противном
случае.
Так колбэк функция получает указатели на целые числа, и должна
возвращать целое число. Сначала создадим type
для колбэк функции:
>>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
>>>
Для начала, далее представлен простой колбэк, показывающий переданные ему значения:
>>> def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return 0
...
>>> cmp_func = CMPFUNC(py_cmp_func)
>>>
Результат:
>>> qsort(ia, len(ia), sizeof(c_int), cmp_func)
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 5 7
py_cmp_func 1 7
>>>
Теперь мы можем сравнить два элемента и вернуть полезный результат:
>>> def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return a[0] - b[0]
...
>>>
>>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func))
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>
Как мы можем легко проверить, наш массив теперь отсортирован:
>>> for i in ia: print(i, end=" ")
...
1 5 7 33 99
>>>
Фабрики функций могут использоваться как фабрики-декораторы, так что допустимо следующее:
>>> @CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
... def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return a[0] - b[0]
...
>>> qsort(ia, len(ia), sizeof(c_int), py_cmp_func)
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>
Примечание
Убедитесь, что ссылки на объекты CFUNCTYPE()
сохраняются до тех пор, пока они
используются из C кода. В ctypes
их нет, а если нет, то они могут
быть мусором, разрушающим вашу программу при создании колбэков.
Кроме того, следует отметить, что если функция колбэк вызывается в
потоке, созданном вне Python управления (например, внешним
коде, вызывающим колбэк), ctypes создаёт новый фиктивный
Python поток при каждом вызове. Это поведение правильно в большинстве
применений, но это означает, что значения, находящиеся в threading.local
,
не будут выживать в различных вызовах, даже когда эти вызовы сделаны от того
же C потока.
Доступ к значениям, экспортированным из dll
Некоторые разделяемые библиотеки не только экспортируют функции, но и экспортируют
переменные. Примером в самой библиотеке Python является Py_OptimizeFlag
,
целое число, равное 0, 1 или 2, в зависимости от флага -O
или
-OO
, заданного при запуске.
ctypes
может обращаться к подобным значениям с помощью in_dll()
методов класса типа. pythonapi является предопределенным символом,
предоставляющим доступ к C API Python:
>>> opt_flag = c_int.in_dll(pythonapi, "Py_OptimizeFlag")
>>> print(opt_flag)
c_long(0)
>>>
Если бы интерпретатор начинался с -O
, образец был бы напечатан
c_long(1)
, или c_long(2)
, если бы был указан -OO
.
Расширенный пример, который также демонстрирует использование указателей доступа
PyImport_FrozenModules
экспортируемому указателю Python.
Цитата из документации:
Указатель инициализируется, чтобы указать на массив записейstruct _frozen
, заканчивающийся одной, все члены которой равныNULL
или нулю. При импорте замороженного модуля, его поиск выполняется в этой таблице. Сторонний код может сыграть с ним злую шутку, чтобы предоставить динамически создаваемую коллекцию замороженных модулей.
Поэтому манипулирование указателем может оказаться даже полезным. Чтобы ограничить
размер примера, мы покажем только то, как можно прочитать таблицу с помощью
ctypes
:
>>> from ctypes import *
>>>
>>> class struct_frozen(Structure):
... _fields_ = [("name", c_char_p),
... ("code", POINTER(c_ubyte)),
... ("size", c_int)]
...
>>>
После определения тип данных struct _frozen
, можем получить указатель на таблицу:
>>> FrozenTable = POINTER(struct_frozen)
>>> table = FrozenTable.in_dll(pythonapi, "PyImport_FrozenModules")
>>>
Поскольку table
является указателем
для массива записей struct_frozen
,
мы можем перебирать его, но нам нужно убедиться, что наш цикл
завершается, потому что указатели не имеют размера. Рано или поздно он,
вероятно, выйдет из строя из-за нарушения доступа или чего-то ещё, поэтому
лучше выйти из цикла, когда мы попадём на NULL
запись:
>>> for item in table:
... if item.name is None:
... break
... print(item.name.decode("ascii"), item.size)
...
_frozen_importlib 31764
_frozen_importlib_external 41499
__hello__ 161
__phello__ -161
__phello__.spam 161
>>>
Тот факт, что стандартный Python содержит замороженный модуль и замороженный пакет
(обозначенный отрицательным атрибутом size
) не очень хорошо известен, он используется
только для тестирования. Проверьте это сами с import __hello__
.
Неожиданности
Существуют некоторые странности в ctypes
, когда ожидается одно,
а по факту происходит что-то другое.
Рассмотрим следующий пример:
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = ("x", c_int), ("y", c_int)
...
>>> class RECT(Structure):
... _fields_ = ("a", POINT), ("b", POINT)
...
>>> p1 = POINT(1, 2)
>>> p2 = POINT(3, 4)
>>> rc = RECT(p1, p2)
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
1 2 3 4
>>> # теперь поменяем местами две точки
>>> 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
, не содержит ожидаемого эффекта.
Имейте в виду, что получение подобъектов из Structure, Unions и Arrays не копирует подобъект, вместо этого он получает объект-оболочку, обращающийся к базовому буферу корневого объекта.
Другой пример, который может вести себя иначе, чем ожидалось:
>>> 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 экземпляры — это объекты, содержащие блок
памяти и некоторые дескриптор’ы, обращающиеся к содержимому памяти. Сохранение
объекта 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
Назначает тип ctypes для определения типа результата внешней функции. Используйте
None
дляvoid
, если функция ничего не возвращает.Можно назначить вызываемый объект Python, который не является типом ctypes, в этом случае предполагается, что функция возвращает C
int
, и вызываемый объект будет вызван с этим целым числом, позволяя дальнейшую обработку или проверку ошибок. Это использование запрещается, поскольку более гибкая пост обработка или проверка ошибок используют ctype тип данных в качествеrestype
и назначает вызываемый наerrcheck
атрибут.
-
argtypes
Назначает кортеж типов ctypes, чтобы указать типы аргументов, которые принимает функция. Функции, использующие соглашение о вызове
stdcall
, могут вызываться только с тем же количеством аргументов, что и длина этого кортежа; функции, использующие соглашение о вызове C, также принимают дополнительные, не указанные аргументы.Когда вызывается внешняя функция, каждый фактический аргумент передается методу класса
from_param()
элементов в кортежеargtypes
, этот метод позволяет адаптировать фактический аргумент к объекту, который принимает внешняя функция. Например, элементc_char_p
в кортежеargtypes
преобразует строку, переданную в качестве аргумента, в объект байтов, используя правила преобразования ctypes.Новое: теперь можно поместить элементы в argtypes, которые не являются типами ctypes, но каждый элемент должен содержать возвращающий значение метод
from_param()
, используемое в качестве аргумента (целое число, строка, экземпляр ctypes). Это позволяет определить адаптеры, которые могут адаптировать пользовательские объекты в качестве функциональных параметров.
-
errcheck
Назначает функцию Python или другого вызываемого к данному атрибуту. Вызываемый объект будет вызван с тремя или более аргументами:
-
callable
(result, func, arguments) result — это то, что возвращает внешняя функция, как указан
restype
атрибут.func является самим объектом внешней функции, что позволяет повторно использовать один и тот же вызываемый объект для проверки или постобработки результатов нескольких функций.
arguments — кортеж, содержащий параметры, первоначально переданные вызову функции, что позволяет специализироваться на поведении используемых аргументов.
Возвращаемый данной функцией объект, будет возвращён из вызова внешней функции, но он также может проверить значение результата и вызвать исключение, если вызов внешней функции не удался.
-
-
-
exception
ctypes.
ArgumentError
Вызывается исключение, когда вызов внешней функции не может преобразовать один из переданных аргументов.
В Windows, когда вызов внешней функции вызывает системное исключение (например,
из-за нарушения доступа), оно будет захвачено и заменено подходящим исключением
Python. Кроме того, будет вызвано событие аудита ctypes.seh_exception
с аргументом
code
, что позволит хуку аудита заменить исключение своим
собственным.
Некоторые способы вызова внешних функций могут вызвать события аудита
ctypes.call_function
с аргументами function pointer
и arguments
.
Прототипы функции
Внешние функции также могут быть созданы путём создания прототипов функций.
Прототипы функций аналогичны прототипам функций в 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
, за исключением Windows CE, гдеWINFUNCTYPE()
совпадает сCFUNCTYPE()
. Функция освободит 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 должен быть кортежем из двух элементов
(name_or_ordinal, library)
. Первый элемент — это имя экспортируемой функции в виде строки или порядкового номера экспортируемой функции в виде малого целого числа. Второй элемент — экземпляр разделяемой библиотеки.
prototype
(vtbl_index, name[, paramflags[, iid]])Возвращает внешнюю функцию, вызывающую COM методом. vtbl_index — индекс в таблице виртуальных функций, небольшое неотрицательное целое число. name — имя COM метода. iid является необязательным указателем на идентификатор интерфейса, который используемый в расширенном отчете об ошибках.
COM методы используют специальное соглашение о вызовах: они требуют указателя на интерфейс COM как первый аргумент, в дополнение к тем параметрам, которые определены в кортеже
argtypes
.Дополнительный параметр paramflags создаёт внешние функциональные обёртки с гораздо большей функциональностью, чем описанные выше функции.
paramflags должен быть кортежем той же длины, что и
argtypes
.Каждый элемент этого кортежа содержит дополнительную информацию о параметре, он должен быть кортежем, содержащим один, два или три элемента.
Первый элемент представляет собой целое число, содержащее комбинацию флагов направления для параметра:
- 1
- Задаёт входной параметр для функции.
- 2
- Выходной параметр. Внешняя функция заполняет значение.
- 4
- Входной параметр, который по умолчанию равен целому нулю.
Необязательным вторым элементом является имя параметра как строка. Если он указан, внешняя функция может быть вызвана с именованными параметрами.
Дополнительный третий элемент — значение по умолчанию для этого параметра.
Далее пример демонстрирует, как обернуть функцию Windows MessageBoxW
так, чтобы
она поддерживала параметры по умолчанию и именованные аргументы. Объявление C из
заголовочного файла Windows таково:
WINUSERAPI int WINAPI
MessageBoxW(
HWND hWnd,
LPCWSTR lpText,
LPCWSTR lpCaption,
UINT uType);
Обертка с ctypes
:
>>> from ctypes import c_int, WINFUNCTYPE, windll
>>> from ctypes.wintypes import HWND, LPCWSTR, UINT
>>> prototype = WINFUNCTYPE(c_int, HWND, LPCWSTR, LPCWSTR, UINT)
>>> paramflags = (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", "Hello from ctypes"), (1, "flags", 0)
>>> MessageBox = prototype(("MessageBoxW", windll.user32), paramflags)
Внешняя функция MessageBox
может теперь быть вызвана следующими способами:
>>> MessageBox()
>>> MessageBox(text="Spam, spam, spam")
>>> MessageBox(flags=2, text="foo bar")
Второй пример демонстрирует выходные параметры. Функция win32 GetWindowRect
восстанавливает размеры указанного окна, копируя их в структуру RECT
,
которую должен вызвать потребитель. Далее C декларация:
WINUSERAPI BOOL WINAPI
GetWindowRect(
HWND hWnd,
LPRECT lpRect);
Обёртка с ctypes
:
>>> from ctypes import POINTER, WINFUNCTYPE, windll, WinError
>>> from ctypes.wintypes import BOOL, HWND, RECT
>>> prototype = WINFUNCTYPE(BOOL, HWND, POINTER(RECT))
>>> paramflags = (1, "hwnd"), (2, "lprect")
>>> GetWindowRect = prototype(("GetWindowRect", windll.user32), paramflags)
>>>
Функции с выходными параметрами автоматически возвращают значение выходного параметра, если есть один, или кортеж, содержащий значения выходного параметра, если их несколько, то функция GetCreateRect возвращает экземпляр 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.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) Функция подобна оператору cast в C. Она возвращает новую сущность type, которая указывает на тот же блок памяти как obj. type должен быть типом указателя, а obj должен быть объектом, который может быть интерпретирован как указатель.
-
ctypes.
create_string_buffer
(init_or_size, size=None) Функция создаёт изменяемый символьный буфер. Возвращаемый объект является массивом ctypes
c_char
.init_or_size должен быть целым числом, определяющим размер массива или объект байтов, который будет использован для инициализации элементов массива.
Если в качестве первого аргумента указан объект байтов, буфер становится на один элемент больше, чем его длина, так что последний элемент в массиве является символом завершения NUL. В качестве второго аргумента можно передать целое число, которое позволяет указать размер массива, если длина байтов не должна использоваться.
Вызывает событие аудита
ctypes.create_string_buffer
с аргументамиinit
,size
.
-
ctypes.
create_unicode_buffer
(init_or_size, size=None) Функция создаёт изменяемый Юникод символьный буфер. Возвращаемый объект является массивом ctypes
c_wchar
.init_or_size должен быть целым числом, определяющим размер массива или строка, которая будет использоваться для инициализации элементов массива.
Если в качестве первого аргумента указана строка, буфер делается на один элемент больше, чем длина строки, так что последний элемент в массиве является символом завершения NUL. В качестве второго аргумента можно передать целое число, которое позволяет указать размер массива, если длина строки не должна использоваться.
Вызывает событие аудита
ctypes.create_unicode_buffer
с аргументамиinit
,size
.
-
ctypes.
DllCanUnloadNow
() Только Windows: функция является хуком, который позволяет реализовать внутрипроцессные COM серверы с ctypes. Она вызывается из функции DllCanUnloadNow, которую экспортирует _ctypes расширение dll.
-
ctypes.
DllGetClassObject
() Только Windows: функция является хуком, который позволяет реализовать внутрипроцессные COM серверы с ctypes. Она вызывается из функции CallGetClassObject, которую экспортирует библиотека расширения
_ctypes
.
-
ctypes.util.
find_library
(name) Попытаться найти библиотеку и вернуть pathname. name — имя библиотеки без префикса типа
lib
, суффикса типа.so
,.dylib
или номера версии (форма используется для опции posix линкера-l
). Если не удается найти библиотеку, возвращает значениеNone
.Точная функциональность зависит от системы.
-
ctypes.util.
find_msvcrt
() Только Windows: возвращает имя файла библиотеки времени выполнения VC используемой Python и модулями расширений. Если имя библиотеки не может быть определено, возвращается значение
None
.Если требуется освободить память, например, выделенную внутренним модулем с вызовом
free(void *)
, важно использовать функцию в той же библиотеки, которая выделила память.
-
ctypes.
FormatError
([code]) Только Windows: возвращает текстовое описание кода ошибки code. Если не указан код ошибки, используется последний код ошибки используемый при вызове API функции Windows GetLastError.
-
ctypes.
GetLastError
() Только Windows: возвращает последний код ошибки, установленный Windows в вызывающем потоке. Функция вызывает функцию GetLastError() Windows напрямую, она не возвращает ctypes-приватную копию кода ошибки.
-
ctypes.
get_errno
() Возвращает текущее значение ctypes-приватной копии системной переменной
errno
в вызываемом потоке.Вызывает событие аудита
ctypes.get_errno
без аргументов.
-
ctypes.
get_last_error
() Только Windows: возвращает текущее значение ctypes-приватной копии системной переменной
LastError
в вызываемом потоке.Вызывает событие аудита
ctypes.get_last_error
без аргументов.
-
ctypes.
memmove
(dst, src, count) То же, что и стандартная функция библиотеки C memmove: копирование count байт из src в dst. dst и src должны быть целыми числами или ctypes сущностями, которые могут быть преобразованы в указатели.
-
ctypes.
memset
(dst, c, count) То же, что и стандартная функция библиотеки C memset: заполняет блок памяти по адресу dst count байтами значением c. dst должно быть целым числом, указывающим адрес или ctypes сущностью.
-
ctypes.
POINTER
(type) Функция фабрика создающая и возвращающая новый тип указателя ctypes. Типы указателей кэшируются и повторно используются внутри системы, поэтому повторный вызов этой функции является дешевым. type должен быть типом ctypes.
-
ctypes.
pointer
(obj) Функция создаёт новый экземпляр указателя, указывающий на obj. Возвращаемый объект содержит тип
POINTER(type(obj))
.Примечание: если вы просто хотите передать указатель на объект на вызов чужой функции, вы должны использовать
byref(obj)
, который гораздо быстрее.
-
ctypes.
resize
(obj, size) Функция изменяет размер внутреннего буфера памяти obj, который должен быть сущностью типа ctypes. Невозможно сделать буфер меньше, чем собственный размер объектов типа указанного в параметре
sizeof(type(obj))
, но можно увеличить буфер.
-
ctypes.
set_errno
(value) Установить текущее значение ctypes-приватной копии системной переменной
errno
в вызываемом потоке в value и возвратить предыдущее значение.Вызывает событие аудита
ctypes.set_errno
с аргументомerrno
.
-
ctypes.
set_last_error
(value) Только Windows: установить текущий значение ctypes-приватной копии системной переменной
LastError
в вызываемом потоке в value и возвратить предыдущий значение.Вызывает событие аудита
ctypes.set_last_error
с аргументомerror
.
-
ctypes.
sizeof
(obj_or_type) Возвращает размер в байтах типа ctypes или экземпляры буфера памяти. Выполняет то же как и оператор C
sizeof
.
-
ctypes.
string_at
(address, size=-1) Функция возвращает C строку, начиная с адреса памяти address как байтовый объект. Если указан размер, он используется как размер, в противном случае предполагается, что строка заканчивается нулем.
Вызывает событие аудита
ctypes.string_at
с аргументамиaddress
,size
.
-
ctypes.
WinError
(code=None, descr=None) Только Windows: функция, вероятно, наихудшая в ctypes. Создаётся сущность OSEror. Если code не указан, вызывается
GetLastError
для определения кода ошибки. Если параметр descr не указан, вызывается методFormatError()
для получения текстового описания ошибки.Изменено в версии 3.3: Создана используемая экземпляр
WindowsError
.
-
ctypes.
wstring_at
(address, size=-1) Функция возвращает широкую символьную строку, начиная с адреса памяти address в виде строки. Если определен size, то он используется как количество символов строки, иначе строка, как предполагается, заканчивается нулем.
Вызывает событие аудита
ctypes.wstring_at
с аргументамиaddress
,size
.
Типы данных
-
class
ctypes.
_CData
Не публичный класс, являющийся общим базовым классом всех ctypes типов данных. Среди прочего, все типы ctypes экземпляры содержат блок памяти, который содержит C-совместимые данные; адрес блока памяти возвращается функцией модуля хелпера
addressof()
. Другая переменная экземпляра отображается как_objects
; она содержит другие объекты Python, которые необходимо сохранить в случае, если блок памяти содержит указатели.Общие методы типов данных ctypes, это все методы класса (чтобы быть точным, это методы метакласса):
-
from_buffer
(source[, offset]) Метод возвращает ctypes сущность, которую совместно использует буфер объекта source. Объект source должен поддерживать интерфейс буфера, доступный для записи. Необязательный параметр offset задаёт смещение в исходном буфере в байтах; значение по умолчанию равно нулю. Если исходный буфер не достаточно большой, вызывается
ValueError
.Вызывает событие аудита
ctypes.cdata/buffer
с аргументамиpointer
,size
,offset
.
-
from_buffer_copy
(source[, offset]) Метод создаёт экземпляр ctypes, копируя буфер из буфера объекта source, который должен быть читаемым. Необязательный параметр offset задаёт смещение в исходном буфере в байтах; значение по умолчанию равно нулю. Если исходный буфер не достаточно большой, вызывается
ValueError
.Вызывает событие аудита
ctypes.cdata/buffer
с аргументамиpointer
,size
,offset
.
-
from_address
(address) Метод возвращает ctypes тип экземпляры, используя память, определенную в address, которая должна быть целым числом.
Метод, а также другие, которые косвенно вызывают этот метод, вызывает событие аудита
ctypes.cdata
с аргументомaddress
.
-
from_param
(obj) Метод приспосабливает obj к типу ctypes. Его вызывают с фактическим объектом используемый в внешнем вызове функции, когда тип присутствует в кортеже внешней функции
argtypes
; он должен возвращать объект, который может быть использован как параметр вызова функции.Все типы данных ctypes содержат реализацию по умолчанию данного классметода, который обычно возвращает obj, если является сущностью type. Некоторые типы также принимают другие объекты.
-
in_dll
(library, name) Метод возвращает ctypes тип экземпляра, экспортируемый общей библиотекой. name — название символа, который экспортирует данные, library — загружаемая общая библиотека.
Общие переменные экземпляры ctypes типов данных:
-
_b_base_
Иногда данные ctypes экземпляры не владеют блоком памяти, в котором они содержатся, вместо этого они разделяют часть блока памяти базового объекта.
_b_base_
— атрибут только для чтения коревого ctypes объекта, который владеет блоком памяти.
-
_b_needsfree_
Переменная только для чтения, содержит true, когда данные экземпляры ctypes аллоцировали блок памяти самостоятельно, false иначе.
-
_objects
Атрибут, либо
None
или словарь, содержащий объекты Python, которые должны быть сохранены, чтобы содержание блока памяти было сохранено действительным. Объект доступен только для отладки; никогда не меняйте содержимое этого словаря.
-
Фундаментальные типы данных
-
class
ctypes.
_SimpleCData
Непубличный класс, являющийся базовым классом всех фундаментальных типов данных ctypes. Здесь он упоминается, поскольку содержит общие атрибуты фундаментальных типов данных ctypes.
_SimpleCData
является подклассом_CData
, поэтому наследует его методы и атрибуты. Типы данных ctypes, которых нет и не содержат указатели, теперь могут быть пиклены (pickled).Сущности содержат один атрибут:
-
value
Атрибут содержит фактическое значение экземпляра. Для целочисленных и типов указателей это целое число, для символьных типов — одиночный символ байтового объекта или строка, для указателей символьного типа — это байтовый объект Python или строка.
Когда
value
атрибут извлекается из экземпляра ctypes, обычно каждый раз возвращается новый объект.ctypes
не реализует оригинальный возвращаемый объект, всегда конструируется новый объект. То же самое верно для всех остальных объектов экземпляра ctypes.
-
Фундаментальные типы данных, возвращаемые как результаты вызова внешней функции или,
например, путём извлечения элементов поля структуры или элементов массива,
прозрачным образом преобразуются в собственные типы Python. Другими
словами, если внешняя функция содержит restype
c_char_p
, всегда будет
получен байтовый объект Python, не сущности c_char_p
.
Подклассы фундаментальных типов данных не наследуют это
поведение. Так, если внешние функции restype
будет подклассом
c_void_p
, то вы получите экземпляр этого подкласса из вызова функции.
Конечно, вы можете получить значение указателя, обращаясь к атрибуту value
.
Фундаментальные типы данных ctypes:
-
class
ctypes.
c_byte
Представляет тип данных C
signed char
и интерпретирует значение как малое целое число. Конструктор принимает необязательный инициализатор целого числа; проверка переполнения не выполняется.
-
class
ctypes.
c_char
Представляет тип данных C
char
и интерпретирует значение как один символ. Конструктор принимает необязательный инициализатор строки, длина строки должна быть ровно одному символу.
-
class
ctypes.
c_char_p
Представляет тип данных C
char *
, когда он указывает на строку с нулевым символом в конце. Для общего указателя символа, который также может указывать на двоичные данные, необходимо использоватьPOINTER(c_char)
. Конструктор принимает целочисленный адрес или байтовый объект.
-
class
ctypes.
c_double
Представляет тип данных C
double
. Конструктор принимает дополнительный инициализатор float.
-
class
ctypes.
c_longdouble
Представляет тип данных C
long double
. Конструктор принимает дополнительный инициализатор float. На платформах, гдеsizeof(long double) == sizeof(double)
это алиасc_double
.
-
class
ctypes.
c_float
Представляет тип данных C
float
. Конструктор принимает дополнительный инициализатор float.
-
class
ctypes.
c_int
Представляет тип данных C
signed int
. Конструктор принимает необязательный инициализатор целого числа; проверка переполнения не выполняется. На платформах, гдеsizeof(int) == sizeof(long)
это алиасc_long
.
-
class
ctypes.
c_int8
Представляет 8-битный
signed int
тип данных C. Обычно алиас дляc_byte
.
-
class
ctypes.
c_int16
Представляет 16-битный
signed int
тип данных C. Обычно алиас дляc_short
.
-
class
ctypes.
c_int32
Представляет 32-битный
signed int
тип данных C. Обычно алиас дляc_int
.
-
class
ctypes.
c_int64
Представляет 64-битный
signed int
тип данных C. Обычно алиас дляc_longlong
.
-
class
ctypes.
c_long
Представляет тип данных C
signed long
. Конструктор принимает необязательный инициализатор целого числа; проверка переполнения не выполняется.
-
class
ctypes.
c_longlong
Представляет тип данных C
signed long long
. Конструктор принимает необязательный инициализатор целого числа; проверка переполнения не выполняется.
-
class
ctypes.
c_short
Представляет тип данных C
signed short
. Конструктор принимает необязательный инициализатор целого числа; проверка переполнения не выполняется.
-
class
ctypes.
c_size_t
Представляет тип данных C
size_t
.
-
class
ctypes.
c_ssize_t
Представляет тип данных C
ssize_t
.Добавлено в версии 3.2.
-
class
ctypes.
c_ubyte
Представляет тип данных C
unsigned char
, интерпретирует значение как малое целое число. Конструктор принимает необязательный инициализатор целого числа; проверка переполнения не выполняется.
-
class
ctypes.
c_uint
Представляет тип данных C
unsigned int
. Конструктор принимает необязательный инициализатор целого числа; проверка переполнения не выполняется. На платформах, гдеsizeof(int) == sizeof(long)
это алиас дляc_ulong
.
-
class
ctypes.
c_uint8
Представляет 8-битный
unsigned int
тип данных C. Обычно алиас дляc_ubyte
.
-
class
ctypes.
c_uint16
Представляет 16-битный
unsigned int
тип данных C. Обычно алиас дляc_ushort
.
-
class
ctypes.
c_uint32
Представляет 32-битный
unsigned int
тип данных C. Обычно алиас дляc_uint
.
-
class
ctypes.
c_uint64
Представляет 64-битный
unsigned int
тип данных C. Обычно алиас дляc_ulonglong
.
-
class
ctypes.
c_ulong
Представляет тип данных C
unsigned long
. Конструктор принимает необязательный инициализатор целого числа; проверка переполнения не выполняется.
-
class
ctypes.
c_ulonglong
Представляет тип данных C
unsigned long long
. Конструктор принимает необязательный инициализатор целого числа; проверка переполнения не выполняется.
-
class
ctypes.
c_ushort
Представляет тип данных C
unsigned short
. Конструктор принимает необязательный инициализатор целого числа; проверка переполнения не выполняется.
-
class
ctypes.
c_void_p
Представляет тип C
void *
. значение представлен как целое число. Конструктор принимает необязательный инициализатор целого числа.
-
class
ctypes.
c_wchar
Представляет тип данных C
wchar_t
и интерпретирует значение как один символ Юникод строки. Конструктор принимает необязательный инициализатор строки, длина строки должна быть ровно одному символу.
-
class
ctypes.
c_wchar_p
Представляет тип данных C
wchar_t *
, который должен быть указателем на широкую символьную строку оканчивающуюся нулём. Конструктор принимает целочисленный адрес или строку.
-
class
ctypes.
c_bool
Представляет тип данных C
bool
(более точно: c:type:_Bool от C99). Его значение может бытьTrue
илиFalse
, и конструктор принимает любой объект, принимающий истинное значение.
-
class
ctypes.
HRESULT
Только для Windows: представляет значение
HRESULT
, которое содержит информацию об успехе или ошибке для вызова функции или метода.
-
class
ctypes.
py_object
Представляет тип данных C
PyObject *
. Вызов этого без аргумента создаёт указательNULL
PyObject *
.
Модуль ctypes.wintypes
предоставляет некоторые другие Windows специфические типы
данных например HWND
:, c:type:WPARAM или DWORD
. Также определены
некоторые полезные структуры, например MSG
или RECT
.
Типы структурированных данных
-
class
ctypes.
Union
(*args, **kw) Абстрактный базовый класс для объединений в родном порядке байтов.
-
class
ctypes.
BigEndianStructure
(*args, **kw) Абстрактный базовый класс для структур (с обратным порядком байт) big endian.
-
class
ctypes.
LittleEndianStructure
(*args, **kw) Абстрактный базовый класс для структур в прямом порядке байт (little endian).
Структуры с неродным порядком байтов не могут содержать поля типа указателя или любые другие типы данных, содержащие поля типа указателя.
-
class
ctypes.
Structure
(*args, **kw) Абстрактный базовый класс для структур в родном порядке байт.
Структуры и типы объединений должны создаваться подклассом одного из этих типов и, по крайней мере, определять переменную класса
_fields_
.ctypes
создаст дескриптор, которые позволяют читать и писать поля прямым доступом к атрибуту. Их перечень.-
_fields_
Последовательность, определяющая поля структуры. Элементы должны быть 2-кортежами или 3-кортежами. Первый элемент — имя поля, второй элемент — тип поля; он может быть любым типом данных ctypes.
Для целочисленных полей, таких как
c_int
, может быть задан третий необязательный элемент. Он должен быть небольшим положительным целым числом, определяющее битовую ширину поля.Имена полей должны быть уникальными в рамках одной структуры или объединения. Он не выбирается, при повторении имен можно получить доступ только к одному полю.
Возможно определить переменную
_fields_
класса после class оператора, который определяет подкласс Structure, это позволяет создавать типы данных что прямо или косвенно ссылаются сами на себя:class List(Structure): pass List._fields_ = [("pnext", POINTER(List)), ... ]
Однако, переменная класса
_fields_
должна, быть определена до того, как тип впервые используется (создаётся экземпляр,sizeof()
вызывается на нём, и так далее). Более позднее присвоение переменной класса_fields_
будет вызывать AttributeError.Возможно определить вложенных подклассов типов структуры, они наследуют поля основного класса плюс
_fields_
, определенные в подклассе, если таковые имеются.
-
_pack_
Необязательное малое целое число, позволяющее переопределять выравнивание полей структуры в экземпляры.
_pack_
уже должен быть определен при присвоении_fields_
, в противном случае он не будет иметь эффекта.
-
_anonymous_
Необязательная последовательность, в которой перечислены имена безымянных (анонимных) полей.
_anonymous_
должен быть уже определён при назначении_fields_
, в противном случае он не будет иметь эффекта.Поля, перечисленные в этой переменной, должны быть полями типа структуры или объединения.
ctypes
создаёт дескрипторы в типе структуры, которые позволяют получить доступ к вложенным полям напрямую, без необходимости создания поля структуры или объединения.Вот пример типа (Windows):
class _U(Union): _fields_ = [("lptdesc", POINTER(TYPEDESC)), ("lpadesc", POINTER(ARRAYDESC)), ("hreftype", HREFTYPE)] class TYPEDESC(Structure): _anonymous_ = ("u",) _fields_ = [("u", _U), ("vt", VARTYPE)]
Структура
TYPEDESC
определяет тип данных COM, полеvt
указывает, какое из полей объединения является допустимым. Поскольку полеu
определено как анонимное поле, теперь можно получить доступ к членам непосредственно вне 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
Возвращает объект, на который указывает указатель. Присвоение этому атрибуту изменяет указатель, чтобы указать на назначенный объект.
-