ctypes — Библиотека внешних функций для Python
Исходный код: 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 для загрузки динамических библиотек.
Библиотеки загружаются путем доступа к ним как к атрибутам этих объектов. 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
>>>
Обратите внимание, что системные dll win32, такие как kernel32 и user32, часто экспортируют версии функций ANSI и UNICODE. Версия UNICODE экспортируется с добавленным W к имени, а версия ANSI — с добавленным A к имени. Функция win32 GetModuleHandle, возвращающая дескриптор модуля для данного имени модуля, имеет следующий прототип C, и макрос используется для экспорта одного из них как GetModuleHandle в зависимости от того, определён ли UNICODE или нет:
/* 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
>>>
Вызов функций
Вы можете вызывать эти функции как любые другие вызываемые объекты 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, целые числа, байтовые объекты и (строки Unicode) являются единственными встроенными объектами Python, которые можно напрямую использовать в качестве параметров в этих вызовах функций. None передаётся как указатель C NULL, байтовые объекты и строки передаются как указатели на блок памяти, содержащий их данные (char* или wchar_t*). Целые числа Python передаются как платформенные значения C int, их значение маскируется для соответствия типу C.
Прежде чем перейти к вызову функций с другими типами параметров, нам нужно узнать больше о типах данных ctypes.
Основные типы данных
ctypes определяет ряд примитивных типов данных, совместимых с C:
Тип ctypes | Тип C | Тип Python |
|---|---|---|
_Bool | bool (1) | |
char | объект bytes длиной в 1 символ | |
| строка длиной в 1 символ | |
char | int | |
unsigned char | int | |
short | int | |
unsigned short | int | |
int | int | |
unsigned int | int | |
long | int | |
unsigned long | int | |
__int64 или long long | int | |
unsigned __int64 или unsigned long long | int | |
| int | |
| int | |
float | float | |
double | float | |
long double | float | |
char* (с завершающим нулём) | объект bytes или | |
wchar_t* (с завершающим нулём) | строка или | |
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; если вы хотите получить его как строку с завершающим нулём, используйте свойство value:
>>> from ctypes import * >>> p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes >>> print(sizeof(p), repr(p.raw)) 3 b'\x00\x00\x00' >>> p = create_string_buffer(b"Hello") # create a buffer containing a NUL terminated string >>> print(sizeof(p), repr(p.raw)) 6 b'Hello\x00' >>> print(repr(p.value)) b'Hello' >>> p = create_string_buffer(b"Hello", 10) # create a 10 byte buffer >>> print(sizeof(p), repr(p.raw)) 10 b'Hello\x00\x00\x00\x00\x00' >>> p.value = b"Hi" >>> print(sizeof(p), repr(p.raw)) 10 b'Hi\x00lo\x00\x00\x00\x00\x00' >>>
Функция create_string_buffer() заменяет старую функцию c_buffer() (которая всё ещё доступна как псевдоним). Для создания изменяемого блока памяти, содержащего символы Юникода типа C 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: TypeError: Don't know how to convert parameter 2 >>>
Как уже упоминалось ранее, все типы Python, кроме целых чисел, строк и объектов bytes, должны быть обернуты в соответствующий тип 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 точно такой же, как вызов функций с фиксированным числом параметров. На некоторых платформах, в частности ARM64 для платформ Apple, соглашение о вызове для функций с переменным числом аргументов отличается от соглашения о вызове для обычных функций.
На этих платформах необходимо указать атрибут argtypes для обычных, не-вариативных, аргументов функции:
libc.printf.argtypes = [ctypes.c_char_p]
Поскольку указание атрибута не препятствует переносимости, рекомендуется всегда указывать argtypes для всех функций с переменным числом аргументов.
Вызов функций с собственными пользовательскими типами данных
Вы также можете настроить преобразование аргументов ctypes, чтобы позволить использовать экземпляры ваших собственных классов в качестве аргументов функции. ctypes ищет атрибут _as_parameter_ и использует его в качестве аргумента функции. Атрибут должен быть целым числом, строкой, объектом bytes, экземпляром 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: TypeError: wrong type >>> printf(b"%s %d %f\n", b"X", 2, 3) X 2 3.000000 13 >>>
Если вы определили собственные классы, которые передаёте в вызовы функций, вам нужно реализовать метод класса from_param(), чтобы использовать их в последовательности argtypes. Метод класса from_param() получает объект Python, переданный в вызов функции; он должен выполнить проверку типа или другие необходимые действия, чтобы убедиться, что этот объект приемлем, а затем вернуть сам объект, его атрибут _as_parameter_ или любое другое значение, которое вы хотите передать как аргумент функции C в этом случае. Результат должен снова быть целым числом, строкой, байтами, экземпляром ctypes или объектом с атрибутом _as_parameter_.
Типы возвращаемых значений
По умолчанию предполагается, что функции возвращают тип C int. Другие типы возвращаемых значений можно указать, установив атрибут restype объекта функции.
Вот более сложный пример; он использует функцию strchr, которая ожидает указатель на строку и символ, а возвращает указатель на строку:
>>> strchr = libc.strchr
>>> strchr(b"abcdef", ord("d"))
8059983
>>> strchr.restype = c_char_p # c_char_p is a pointer to a string
>>> strchr(b"abcdef", ord("d"))
b'def'
>>> print(strchr(b"abcdef", ord("x")))
None
>>>
Если вы хотите избежать вызовов ord("x") выше, вы можете установить атрибут argtypes, и второй аргумент будет преобразован из объекта Python типа байты с одним символом в символ C:
>>> strchr.restype = c_char_p >>> strchr.argtypes = [c_char_p, c_char] >>> strchr(b"abcdef", b"d") 'def' >>> strchr(b"abcdef", b"def") Traceback (most recent call last): File "<stdin>", line 1, in <module> ArgumentError: argument 2: TypeError: one character string expected >>> print(strchr(b"abcdef", b"x")) None >>> strchr(b"abcdef", b"d") 'def' >>>
Вы также можете использовать вызываемый объект Python (функцию или класс, например) в качестве атрибута restype, если внешняя функция возвращает целое число. Вызываемый объект будет вызван с целым числом, возвращённым функцией C, а результат этого вызова будет использован в качестве результата вызова вашей функции. Это полезно для проверки значений возврата ошибок и автоматического повышения исключения:
>>> GetModuleHandle = windll.kernel32.GetModuleHandleA
>>> def ValidHandle(value):
... if value == 0:
... raise WinError()
... return value
...
>>>
>>> GetModuleHandle.restype = ValidHandle
>>> GetModuleHandle(None)
486539264
>>> GetModuleHandle("something silly")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in ValidHandle
OSError: [Errno 126] The specified module could not be found.
>>>
WinError — функция, которая вызывает Windows FormatMessage() API, чтобы получить строковое представление кода ошибки, и *возвращает* исключение. WinError принимает необязательный параметр кода ошибки; если он не используется, она вызывает GetLastError(), чтобы получить его.
Обратите внимание, что гораздо более мощный механизм проверки ошибок доступен через атрибут errcheck; подробности см. в справочном руководстве.
Передача указателей (или передача параметров по ссылке)
Иногда функция C API ожидает указатель на тип данных в качестве параметра, вероятно, для записи в соответствующее место или если данные слишком велики для передачи по значению. Это также известно как передача параметров по ссылке.
ctypes экспортирует функцию byref(), которая используется для передачи параметров по ссылке. Тот же эффект можно достичь с помощью функции pointer(), хотя pointer() выполняет намного больше работы, так как создаёт реальный объект указателя, поэтому для использования будет быстрее функция byref(), если вам не нужен сам объект указателя в Python:
>>> i = c_int() >>> f = c_float() >>> s = create_string_buffer(b'\000' * 32) >>> print(i.value, f.value, repr(s.value)) 0 0.0 b'' >>> libc.sscanf(b"1 3.14 Hello", b"%d %f %s", ... byref(i), byref(f), s) 3 >>> print(i.value, f.value, repr(s.value)) 1 3.1400001049 b'Hello' >>>
Структуры и объединения
Структуры и объединения должны наследовать от базовых классов Structure и Union, которые определены в модуле ctypes. Каждый подкласс должен определить атрибут _fields_. _fields_ должен быть списком из пар 2-х элементов, содержащих имя поля и тип поля.
Тип поля должен быть типом 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, это не гарантируется библиотекой для общего случая. Объединения и структуры с битами полей всегда следует передавать функциям по указателю.
Выравнивание и порядок байтов структур/объединений
По умолчанию поля структур и объединений выравниваются так же, как это делает компилятор 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; /* forward declaration */
struct cell {
char *name;
struct cell *next;
};
Прямое преобразование в код ctypes было бы таким, но оно не работает:
>>> class cell(Structure):
... _fields_ = [("name", c_char_p),
... ("next", POINTER(cell))]
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in cell
NameError: name 'cell' is not defined
>>>
потому что новый class cell недоступен в операторе класса. В ctypes мы можем определить класс cell и установить атрибут _fields_ позже, после оператора класса:
>>> 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.
Обе эти функции-фабрики вызываются с типом результата в качестве первого аргумента и ожидаемыми типами аргументов функций обратного вызова как оставшимися аргументами.
Здесь я приведу пример, который использует функцию стандартной 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.
Цитата из документации по этому значению:
Этот указатель инициализируется для указания на массив записей _frozen, завершаемый записью, члены которой равны NULL или нулю. При импорте замороженного модуля он ищется в этой таблице. Код сторонних разработчиков мог бы использовать этот механизм для предоставления динамически созданного набора замороженных модулей.
Таким образом, манипулирование этим указателем может оказаться полезным. Чтобы ограничить размер примера, мы покажем только то, как эта таблица может быть прочитана с помощью 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
... ]
...
>>>
Мы определили тип данных _frozen, поэтому мы можем получить указатель на таблицу:
>>> FrozenTable = POINTER(struct_frozen) >>> table = FrozenTable.in_dll(pythonapi, "_PyImport_FrozenBootstrap") >>>
Поскольку table — это pointer на массив записей struct_frozen, мы можем перебирать его, но нам нужно убедиться, что наш цикл завершается, так как у указателей нет размера. Рано или поздно это, вероятно, приведет к ошибке доступа или тому подобному, поэтому лучше прервать цикл, когда мы встретим запись NULL:
>>> for item in table:
... if item.name is None:
... break
... print(item.name.decode("ascii"), item.size)
...
_frozen_importlib 31764
_frozen_importlib_external 41499
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 — это объекты, содержащие блок памяти плюс некоторые дескрипторы, которые обращаются к содержимому памяти. Сохранение объекта Python в блоке памяти не сохраняет сам объект, вместо этого сохраняется contents объекта. При повторном доступе к содержимому каждый раз создаётся новый объект 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, а вызываемый объект будет вызван с этим целым числом, что позволит произвести дальнейшую обработку или проверку ошибок. Использование этого метода устарело; для более гибкой обработки или проверки ошибок используйте тип данных ctypes в качестве
restypeи назначьте вызываемый объект атрибутуerrcheck.
-
argtypes -
Назначьте кортеж типов ctypes, чтобы указать типы аргументов, которые принимает функция. Функции, использующие
stdcallсоглашение о вызове, могут вызываться только с таким же количеством аргументов, как длина этого кортежа; функции, использующие соглашение о вызове C, также принимают дополнительные неопределённые аргументы.При вызове внешней функции каждый фактический аргумент передается методу
from_param()элементов кортежаargtypes; этот метод позволяет адаптировать фактический аргумент к объекту, который принимает внешняя функция. Например, элементc_char_pв кортежеargtypesпреобразует строку, переданную в качестве аргумента, в объект bytes в соответствии с правилами преобразования 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. Функция освободит 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 — индекс в таблице виртуальных функций, небольшое неотрицательное целое число. 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)
>>>
Функции с выходными параметрами автоматически возвращают значение выходного параметра, если оно одно, или кортеж, содержащий значения выходных параметров, если их несколько, поэтому функция GetWindowRect теперь возвращает экземпляр RECT при вызове.
Выходные параметры можно комбинировать с протоколом errcheck для дальнейшей обработки вывода и проверки ошибок. Функция win32 GetWindowRect возвращает 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) -
Эта функция аналогична оператору приведения типов в C. Она возвращает новый экземпляр type, который указывает на тот же блок памяти, что и obj. type должен быть типом указателя, а obj — объектом, который может быть интерпретирован как указатель.
-
ctypes.create_string_buffer(init_or_size, size=None) -
Эта функция создаёт изменяемый буфер символов. Возвращаемый объект — массив ctypes типа
c_char.init_or_size должно быть целым числом, определяющим размер массива, или объектом типа bytes, который будет использоваться для инициализации элементов массива.
Если в качестве первого аргумента задан объект типа bytes, буфер делается на один элемент больше его длины, так что последний элемент массива — символ NUL. Целое число можно передать в качестве второго аргумента, чтобы задать размер массива, если длина bytes не должна использоваться.
Вызывает событие аудита
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, которую экспортирует DLL расширения _ctypes.
-
ctypes.DllGetClassObject() -
Только Windows: Эта функция — обработчик, который позволяет реализовывать COM-серверы в процессе с помощью ctypes. Она вызывается функцией DllGetClassObject, которую экспортирует DLL расширения
_ctypes.
-
ctypes.util.find_library(name) -
Попытается найти библиотеку и вернуть путь к ней. name — имя библиотеки без каких-либо префиксов, таких как
lib, суффиксов, таких как.so,.dylib, или номера версии (это формат, используемый для опции позикс-линкера-l). Если библиотека не найдена, возвращаетNone.Точное поведение зависит от операционной системы.
-
ctypes.util.find_msvcrt() -
Только Windows: возвращает имя файла библиотеки времени выполнения VC, используемой Python и модулями расширений. Если имя библиотеки определить невозможно, возвращается
None.Если вам нужно освободить память, например, выделенную модулем расширения с помощью вызова
free(void *), важно использовать функцию из той же библиотеки, которая выделила память.
-
ctypes.FormatError([code]) -
Только Windows: Возвращает текстовое описание кода ошибки code. Если код ошибки не задан, используется последний код ошибки, полученный вызовом Windows-функции GetLastError.
-
ctypes.GetLastError() -
Только Windows: Возвращает последний код ошибки, установленный Windows в потоке вызова. Эта функция вызывает Windows-функцию
GetLastError()напрямую, она не возвращает частную копию кода ошибки в 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, в виде объекта bytes. Если задан размер, он используется как размер, в противном случае строка предполагается нуль-терминированной.
Вызывает событие аудита
ctypes.string_atс аргументамиaddress,size.
-
ctypes.WinError(code=None, descr=None) -
Только Windows: эта функция, вероятно, имеет самое неудачное название в ctypes. Она создаёт экземпляр
OSError. Если code не указан, вызываетсяGetLastErrorдля определения кода ошибки. Если descr не указан, вызываетсяFormatError()для получения текстового описания ошибки.Изменено в версии 3.3: Ранее создавался экземпляр
WindowsError, который теперь является псевдонимомOSError.
-
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, если это экземпляр типа. Некоторые типы принимают и другие объекты тоже.
-
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, которые не являются и не содержат указателей, теперь могут быть сериализованы.Объекты имеют один атрибут:
-
value -
Этот атрибут содержит фактическое значение объекта. Для целочисленных и указательных типов — это целое число, для символьных типов — это объект типа bytes или строка с одним символом, для указателей на символы — это объект типа Python bytes или строка.
При получении атрибута
valueот объекта ctypes, обычно каждый раз возвращается новый объект.ctypesне реализует возвращение исходного объекта, всегда создается новый объект. То же самое относится ко всем другим объектам ctypes.
-
Основные типы данных при возвращении в качестве результатов вызова внешних функций или, например, при получении членов структуры или элементов массива, прозрачно преобразуются в родные типы Python. Другими словами, если у внешней функции есть restype типа c_char_p, вы всегда получите объект типа Python bytes, а не объект 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). Конструктор принимает целочисленный адрес или объект типа bytes.
-
class ctypes.c_double -
Представляет тип данных C double. Конструктор принимает необязательный инициализатор с плавающей запятой.
-
class ctypes.c_longdouble -
Представляет тип данных C long double. Конструктор принимает необязательный инициализатор с плавающей запятой. На платформах, где
sizeof(long double) == sizeof(double), он является псевдонимомc_double.
-
class ctypes.c_float -
Представляет тип данных C float. Конструктор принимает необязательный инициализатор с плавающей запятой.
-
class ctypes.c_int -
Представляет тип данных C signed int. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется. На платформах, где
sizeof(int) == sizeof(long), он является псевдонимомc_long.
-
class ctypes.c_int8 -
Представляет тип данных C 8-битное signed int. Обычно является псевдонимом
c_byte.
-
class ctypes.c_int16 -
Представляет тип данных C 16-битное signed int. Обычно является псевдонимом
c_short.
-
class ctypes.c_int32 -
Представляет тип данных C 32-битное signed int. Обычно является псевдонимом
c_int.
-
class ctypes.c_int64 -
Представляет тип данных C 64-битное signed int. Обычно является псевдонимом
c_longlong.
-
class ctypes.c_long -
Представляет тип данных C signed long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_longlong -
Представляет тип данных C signed long long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_short -
Представляет тип данных C signed short. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_size_t -
Представляет тип данных C
size_t.
-
class ctypes.c_ssize_t -
Представляет тип данных C
ssize_t.Добавлен в версии 3.2.
-
class ctypes.c_ubyte -
Представляет тип данных C unsigned char, интерпретирует значение как целое число без знака. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_uint -
Представляет тип данных C unsigned int. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется. На платформах, где
sizeof(int) == sizeof(long), он является псевдонимомc_ulong.
-
class ctypes.c_uint8 -
Представляет тип данных C 8-битное unsigned int. Обычно является псевдонимом
c_ubyte.
-
class ctypes.c_uint16 -
Представляет тип данных C 16-битное unsigned int. Обычно является псевдонимом
c_ushort.
-
class ctypes.c_uint32 -
Представляет тип данных C 32-битное unsigned int. Обычно является псевдонимом
c_uint.
-
class ctypes.c_uint64 -
Представляет тип данных C 64-битное unsigned int. Обычно является псевдонимом
c_ulonglong.
-
class ctypes.c_ulong -
Представляет тип данных C unsigned long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_ulonglong -
Представляет тип данных C unsigned long long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_ushort -
Представляет тип данных C unsigned short. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_void_p -
Представляет тип C void*. Значение представлено как целое число. Конструктор принимает необязательный целочисленный инициализатор.
-
class ctypes.c_wchar -
Представляет тип данных C
wchar_tи интерпретирует значение как строку Unicode с одним символом. Конструктор принимает необязательный строковый инициализатор, длина строки должна быть ровно один символ.
-
class ctypes.c_wchar_p -
Представляет тип данных C wchar_t*, который должен быть указателем на нуль-терминированную строку широких символов. Конструктор принимает целочисленный адрес или строку.
-
class ctypes.c_bool -
Представляет тип данных C bool (точнее, _Bool из C99). Его значение может быть
TrueилиFalse, и конструктор принимает любой объект, имеющий истинностное значение.
-
class ctypes.HRESULT -
Только Windows: Представляет значение
HRESULT, которое содержит информацию об успехе или ошибке для вызова функции или метода.
-
class ctypes.py_object -
Представляет тип данных C PyObject*. Вызов без аргумента создает указатель
NULLPyObject*.
Модуль ctypes.wintypes предоставляет ещё ряд других типов данных, специфичных для Windows, например, HWND, WPARAM или DWORD. Также определены некоторые полезные структуры, такие как MSG или RECT.
Структурированные типы данных
-
class ctypes.Union(*args, **kw) -
Абстрактный базовый класс для объединений в родном порядке байтов.
-
class ctypes.BigEndianUnion(*args, **kw) -
Абстрактный базовый класс для объединений в big endian порядке байтов.
Добавлен в версии 3.11.
-
class ctypes.LittleEndianUnion(*args, **kw) -
Абстрактный базовый класс для объединений в little endian порядке байтов.
Добавлен в версии 3.11.
-
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_после объявления класса, определяющего подкласс 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 -
Возвращает объект, на который указывает указатель. Присвоение этому атрибуту изменяет указатель на указание на присвоенный объект.
-
© 2001–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.11/library/ctypes.html