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_int выводится c_long — на самом деле это один и тот же тип.
Загрузка библиотек динамической компоновки
ctypes экспортирует объекты cdll, а в Windows — windll и oledll, для загрузки библиотек динамической компоновки.
Загрузка библиотек осуществляется путём обращения к ним как к атрибутам этих объектов. cdll загружает библиотеки, экспортирующие функции со стандартным соглашением о вызовах cdecl, а windll — библиотеки с соглашением о вызовах stdcall. oledll также использует соглашение о вызовах stdcall и предполагает, что функции возвращают код ошибки Windows HRESULT. Код ошибки используется для автоматического вызова исключения OSError в случае сбоя вызова функции.
Изменено в версии 3.3: Ошибки Windows ранее вызывали исключение WindowsError, которое теперь является псевдонимом для OSError.
Вот несколько примеров для Windows. Обратите внимание, что msvcrt — это стандартная библиотека C от Microsoft, содержащая большинство стандартных функций 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:
>>> 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, а затем вызывать её с объектами bytes или string соответственно.
Иногда 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. В этом примере используется функция rand(), которая не принимает аргументов и возвращает псевдослучайное целое число:
>>> print(libc.rand()) 1804289383
В Windows можно вызвать функцию GetModuleHandleA(), которая возвращает дескриптор модуля win32 (передавая None в качестве единственного аргумента для вызова с указателем NULL):
>>> 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, целые числа, объекты bytes и (юникод-)строки — единственные встроенные объекты Python, которые можно напрямую использовать в качестве параметров в этих вызовах функций. None передается как указатель C NULL, объекты bytes и строки передаются как указатель на блок памяти, содержащий их данные (char* или wchar_t*). Целые числа Python передаются как используемый платформой по умолчанию тип C int, их значение маскируется в соответствии с размером типа C.
Прежде чем перейти к вызову функций с другими типами параметров, необходимо подробнее изучить типы данных ctypes.
Фундаментальные типы данных
ctypes определяет ряд примитивных C-совместимых типов данных:
Тип ctypes | Тип C | Тип Python |
|---|---|---|
_Bool | bool (1) | |
char | 1-символьный объект bytes | |
| 1-символьная строка | |
char | int | |
unsigned char | int | |
short | int | |
unsigned short | int | |
int | int | |
| int | |
| int | |
| int | |
| int | |
unsigned int | int | |
| int | |
| int | |
| int | |
| int | |
long | int | |
unsigned long | int | |
__int64 or long long | int | |
unsigned __int64 or unsigned long long | int | |
| int | |
| int | |
| int | |
float | float | |
double | float | |
long double | float | |
char* (NUL terminated) | объект bytes или | |
wchar_t* (NUL terminated) | строка или | |
void* | int или |
- Конструктор принимает любой объект, имеющий логическое значение.
Все эти типы можно создавать, вызывая их с необязательным инициализатором подходящего типа и значения:
>>> c_int()
c_long(0)
>>> c_wchar_p("Hello, World")
c_wchar_p(140018365411392)
>>> c_ushort(-3)
c_ushort(65533)
>>>
Поскольку эти типы являются изменяемыми, их значение впоследствии также можно изменить:
>>> i = c_int(42) >>> print(i) c_long(42) >>> print(i.value) 42 >>> i.value = -99 >>> print(i.value) -99 >>>
Присваивание нового значения экземплярам типов указателей c_char_p, c_wchar_p и c_void_p изменяет адрес памяти, на который они указывают, а не содержимое блока памяти (что естественно, так как строковые объекты Python неизменяемы):
>>> s = "Hello, World" >>> c_s = c_wchar_p(s) >>> print(c_s) c_wchar_p(139966785747344) >>> print(c_s.value) Hello World >>> c_s.value = "Hi, there" >>> print(c_s) # the memory location has changed c_wchar_p(139966783348904) >>> print(c_s.value) Hi, there >>> print(s) # first object is unchanged Hello, World >>>
Однако следует соблюдать осторожность и не передавать их в функции, ожидающие указатели на изменяемую память. Если вам требуются изменяемые блоки памяти, в ctypes есть функция create_string_buffer(), которая создает их различными способами. Доступ к текущему содержимому блока памяти (или его изменение) можно получить с помощью свойства raw; если вы хотите обращаться к нему как к NUL-терминированной строке, используйте свойство value:
>>> from ctypes import * >>> p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes >>> print(sizeof(p), repr(p.raw)) 3 b'\x00\x00\x00' >>> p = create_string_buffer(b"Hello") # create a buffer containing a NUL terminated string >>> print(sizeof(p), repr(p.raw)) 6 b'Hello\x00' >>> print(repr(p.value)) b'Hello' >>> p = create_string_buffer(b"Hello", 10) # create a 10 byte buffer >>> print(sizeof(p), repr(p.raw)) 10 b'Hello\x00\x00\x00\x00\x00' >>> p.value = b"Hi" >>> print(sizeof(p), repr(p.raw)) 10 b'Hi\x00lo\x00\x00\x00\x00\x00' >>>
Функция create_string_buffer() заменяет старую функцию c_buffer() (которая все еще доступна как псевдоним). Для создания изменяемого блока памяти, содержащего символы Unicode типа 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> ctypes.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> ctypes.ArgumentError: argument 2: TypeError: 'int' object cannot be interpreted as ctypes.c_char_p >>> printf(b"%s %d %f\n", b"X", 2, 3) X 2 3.000000 13 >>>
Если вы определили собственные классы, которые передаете в вызовы функций, вам необходимо реализовать для них метод класса from_param(), чтобы их можно было использовать в последовательности argtypes. Метод класса from_param() получает объект Python, переданный в вызов функции, должен выполнить проверку типа или другие необходимые действия, чтобы убедиться, что этот объект допустим, а затем вернуть сам объект, его атрибут _as_parameter_ или то, что вы хотите передать в качестве аргумента функции C в данном случае. Опять же, результат должен быть целым числом, строкой, bytes, экземпляром ctypes или объектом с атрибутом _as_parameter_.
Возвращаемые типы
По умолчанию предполагается, что функции возвращают тип C int. Другие возвращаемые типы можно указать, задав атрибут restype объекта функции.
C-прототип функции time() — time_t time(time_t *). Поскольку тип time_t может отличаться от возвращаемого по умолчанию типа int, следует указать атрибут restype:
>>> libc.time.restype = c_time_t
Типы аргументов можно указать с помощью argtypes:
>>> libc.time.argtypes = (POINTER(c_time_t),)
Чтобы вызвать функцию с указателем NULL в качестве первого аргумента, используйте None:
>>> print(libc.time(None)) 1150640792
Вот более сложный пример: в нем используется функция strchr(), которая ожидает указатель на строку и char, а возвращает указатель на строку:
>>> strchr = libc.strchr
>>> strchr(b"abcdef", ord("d"))
8059983
>>> strchr.restype = c_char_p # c_char_p is a pointer to a string
>>> strchr(b"abcdef", ord("d"))
b'def'
>>> print(strchr(b"abcdef", ord("x")))
None
>>>
Если вы хотите избежать приведенных выше вызовов ord("x"), можно задать атрибут argtypes, и второй аргумент будет преобразован из односимвольного объекта bytes Python в C char:
>>> strchr.restype = c_char_p >>> strchr.argtypes = [c_char_p, c_char] >>> strchr(b"abcdef", b"d") b'def' >>> strchr(b"abcdef", b"def") Traceback (most recent call last): ctypes.ArgumentError: argument 2: TypeError: one character bytes, bytearray or integer expected >>> print(strchr(b"abcdef", b"x")) None >>> strchr(b"abcdef", b"d") b'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_ должен быть списком кортежей из двух элементов, содержащих имя поля и тип поля.
Тип поля должен быть типом 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. Также можно задать минимальное выравнивание для упаковки самого подкласса аналогично тому, как работает #pragma align(n) в MSVC. Этого можно достичь, указав атрибут класса _align_ в определении подкласса.
ctypes использует нативный порядок байтов для Structures и Unions. Для создания структур с ненативным порядком байтов можно использовать один из базовых классов: 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 атрибуту contents указателя приведет к тому, что указатель будет указывать на область памяти, где он хранится:
>>> i = c_int(99) >>> pi.contents = i >>> pi.contents c_long(99) >>>
Экземпляры указателей также можно индексировать целыми числами:
>>> pi[0] 99 >>>
Присваивание по целочисленному индексу изменяет значение, на которое указывает указатель:
>>> print(i) c_long(99) >>> pi[0] = 22 >>> print(i) c_long(22) >>>
Также можно использовать индексы, отличные от 0, но вы должны понимать, что делаете, точно так же, как в C: вы можете получить доступ или изменить произвольные области памяти. Обычно эта возможность используется только в том случае, если вы получаете указатель из функции C и знаете, что указатель на самом деле указывает на массив, а не на отдельный элемент.
Под капотом функция pointer() делает больше, чем просто создание экземпляров указателей: сначала она должна создать типы указателей. Это делается с помощью функции POINTER(), которая принимает любой тип ctypes и возвращает новый тип:
>>> PI = POINTER(c_int) >>> PI <class 'ctypes.LP_c_long'> >>> PI(42) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: expected c_long instead of int >>> PI(c_int(42)) <ctypes.LP_c_long object at 0x...> >>>
Вызов типа указателя без аргументов создает указатель NULL. Указатели NULL имеют логическое значение False:
>>> null_ptr = POINTER(c_int)() >>> print(bool(null_ptr)) False >>>
ctypes проверяет на NULL при разыменовании указателей (однако разыменование недействительных указателей, отличных от NULL, приведет к аварийному завершению Python):
>>> null_ptr[0]
Traceback (most recent call last):
....
ValueError: NULL pointer access
>>>
>>> null_ptr[0] = 1234
Traceback (most recent call last):
....
ValueError: NULL pointer access
>>>
Преобразования типов
Обычно 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 они задаются опережающими объявлениями (forward declarations), которые определяются позже:
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 недоступен внутри самой инструкции 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 = 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. Их иногда называют функциями обратного вызова (callback functions).
Сначала необходимо создать класс для функции обратного вызова. Класс знает соглашение о вызовах, возвращаемый тип, а также количество и типы аргументов, которые получит эта функция.
Фабричная функция CFUNCTYPE() создает типы для функций обратного вызова, использующих соглашение о вызовах cdecl. В Windows фабричная функция WINFUNCTYPE() создает типы для функций обратного вызова, использующих соглашение о вызовах stdcall.
Обе эти фабричные функции вызываются с типом результата в качестве первого аргумента и ожидаемыми типами аргументов функции обратного вызова в качестве остальных аргументов.
Здесь мы приведем пример, использующий функцию qsort() стандартной библиотеки C, которая применяется для сортировки элементов с помощью функции обратного вызова. Функция qsort() будет использоваться для сортировки массива целых чисел:
>>> IntArray5 = c_int * 5 >>> ia = IntArray5(5, 1, 7, 33, 99) >>> qsort = libc.qsort >>> qsort.restype = None >>>
Функция qsort() должна вызываться с указателем на сортируемые данные, количеством элементов в массиве данных, размером одного элемента и указателем на функцию сравнения (callback). Затем функция обратного вызова будет вызываться с двумя указателями на элементы и должна возвращать отрицательное целое число, если первый элемент меньше второго, ноль, если они равны, и положительное целое число в противном случае.
Таким образом, наша функция обратного вызова принимает указатели на целые числа и должна возвращать целое число. Сначала мы создаем type для функции обратного вызова:
>>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int)) >>>
Для начала вот простой callback, который отображает переданные ему значения:
>>> 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 этого не делает, и если вы этого не сделаете, они могут быть удалены сборщиком мусора, что приведет к аварийному завершению программы при вызове callback-функции.
Также обратите внимание, что если функция обратного вызова вызывается в потоке, созданном вне контроля Python (например, внешним кодом, вызывающим callback), ctypes создает новый фиктивный поток Python при каждом вызове. Такое поведение корректно для большинства целей, но это означает, что значения, сохраненные с помощью threading.local, не сохранятся между различными обратными вызовами, даже если эти вызовы выполняются из одного и того же потока C.
Доступ к значениям, экспортируемым из библиотек DLL
Некоторые разделяемые библиотеки экспортируют не только функции, но и переменные. Примером в самой библиотеке Python является Py_Version — номер версии среды выполнения Python, закодированный в виде одной целочисленной константы.
ctypes может обращаться к таким значениям с помощью методов класса in_dll() типа. pythonapi — это предопределенный символ, предоставляющий доступ к Python C API:
>>> version = ctypes.c_int.in_dll(ctypes.pythonapi, "Py_Version") >>> print(hex(version.value)) 0x30c00a0
Более развернутый пример, также демонстрирующий использование указателей, обращается к указателю PyImport_FrozenModules, экспортируемому Python.
Цитата из документации для этого значения:
Этот указатель инициализируется для указания на массив записей _frozen, оканчивающийся записью, все члены которой равны NULL или нулю. При импорте замороженного модуля (frozen module) поиск происходит в этой таблице. Сторонний код может использовать это для предоставления динамически создаваемой коллекции замороженных модулей.
Поэтому манипулирование этим указателем может даже оказаться полезным. Чтобы ограничить размер примера, покажем только, как эту таблицу можно прочитать с помощью 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, мы можем итерироваться по нему, но нам просто нужно убедиться, что наш цикл завершается, поскольку у указателей нет размера. Рано или поздно программа, вероятно, завершилась бы аварийно с ошибкой нарушения прав доступа (access violation) или подобной, поэтому лучше прервать цикл при достижении записи 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 не приводит к ожидаемому результату.
Имейте в виду, что извлечение подобъектов из 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 в блоке памяти не сохраняет сам объект — вместо этого сохраняется 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 в качестве аргументов и возвращают тип результата по умолчанию, заданный загрузчиком библиотек.
Они являются экземплярами закрытого локального класса _FuncPtr (не экспортируемого в ctypes), который наследуется от закрытого класса _CFuncPtr:
>>> import ctypes >>> lib = ctypes.CDLL(None) >>> issubclass(lib._FuncPtr, ctypes._CFuncPtr) True >>> lib._FuncPtr is ctypes._CFuncPtr False
-
class ctypes._CFuncPtr -
Базовый класс для вызываемых внешних функций 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.set_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, закрытая копия системной переменной
errnoв ctypes обменивается с реальным значениемerrnoдо и после вызова; use_last_error делает то же самое для кода ошибки Windows.
-
ctypes.WINFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False) -
Возвращенный прототип функции создает функции, использующие соглашение о вызовах
stdcall. Функция освобождает GIL во время вызова. Параметры use_errno и use_last_error имеют то же значение, что и выше.Доступность: Windows
-
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-методы используют специальное соглашение о вызовах: в дополнение к параметрам, указанным в кортеже
argtypes, они требуют указатель на COM-интерфейс в качестве первого аргумента.
Необязательный параметр 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 API 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) -
Эта функция аналогична оператору приведения типов (cast) в C. Она возвращает новый экземпляр type, который указывает на тот же блок памяти, что и obj. type должен быть типом указателя, а obj — объектом, который можно интерпретировать как указатель.
-
ctypes.create_string_buffer(init, size=None) - ctypes.create_string_buffer(size)
-
Эта функция создает изменяемый символьный буфер. Возвращаемый объект является массивом ctypes из
c_char.Если задан параметр size (и не равен
None), он должен бытьint. Он определяет размер возвращаемого массива.Если указан аргумент init, он должен быть объектом типа
bytes. Он используется для инициализации элементов массива. Байты, не инициализированные таким образом, обнуляются (NUL).Если параметр size не задан (или равен
None), буфер создается на один элемент больше, чем init, фактически добавляя завершающий NUL-символ.Если указаны оба аргумента, size не должен быть меньше
len(init).Warning
Если size равен
len(init), завершающий NUL-символ не добавляется. Не обрабатывайте такой буфер как C-строку.Например:
>>> bytes(create_string_buffer(2)) b'\x00\x00' >>> bytes(create_string_buffer(b'ab')) b'ab\x00' >>> bytes(create_string_buffer(b'ab', 2)) b'ab' >>> bytes(create_string_buffer(b'ab', 4)) b'ab\x00\x00' >>> bytes(create_string_buffer(b'abcdef', 2)) Traceback (most recent call last): ... ValueError: byte string too long
Вызывает событие аудита
ctypes.create_string_bufferс аргументамиinit,size.
-
ctypes.create_unicode_buffer(init, size=None) - ctypes.create_unicode_buffer(size)
-
Эта функция создает изменяемый буфер символов Юникода. Возвращаемый объект является массивом ctypes из
c_wchar.Функция принимает те же аргументы, что и
create_string_buffer(), за исключением того, что init должен быть строкой, а size задает количествоc_wchar.Вызывает событие аудита
ctypes.create_unicode_bufferс аргументамиinit,size.
-
ctypes.DllCanUnloadNow() -
Эта функция представляет собой хук, позволяющий реализовать внутрипроцессные COM-серверы с помощью ctypes. Она вызывается из функции DllCanUnloadNow, экспортируемой dll-расширением _ctypes.
Доступность: Windows
-
ctypes.DllGetClassObject() -
Эта функция представляет собой хук, позволяющий реализовать внутрипроцессные COM-серверы с помощью ctypes. Она вызывается из функции DllGetClassObject, экспортируемой dll-расширением
_ctypes.Доступность: Windows
-
ctypes.util.find_library(name) -
Пытается найти библиотеку и возвращает путь к ней. name — это имя библиотеки без каких-либо префиксов, таких как
lib, суффиксов, таких как.so,.dylib, или номера версии (это форма, используемая для параметра компоновщика posix-l). Если библиотека не найдена, возвращаетNone.Точная функциональность зависит от системы.
-
ctypes.util.find_msvcrt() -
Возвращает имя файла библиотеки времени выполнения VC, используемой Python и модулями расширения. Если имя библиотеки определить не удается, возвращается
None.Если вам нужно освободить память, выделенную, например, модулем расширения с помощью вызова
free(void *), важно использовать функцию из той же библиотеки, которая выделила эту память.Доступность: Windows
-
ctypes.FormatError([code]) -
Возвращает текстовое описание кода ошибки code. Если код ошибки не указан, используется последний код ошибки, полученный вызовом функции Windows API GetLastError.
Доступность: Windows
-
ctypes.GetLastError() -
Возвращает последний код ошибки, установленный Windows в вызывающем потоке. Эта функция напрямую вызывает функцию Windows
GetLastError()и не возвращает копию кода ошибки, закрытую для ctypes.Доступность: Windows
-
ctypes.get_errno() -
Возвращает текущее значение закрытой для ctypes копии системной переменной
errnoв вызывающем потоке.Вызывает событие аудита
ctypes.get_errnoбез аргументов.
-
ctypes.get_last_error() -
Возвращает текущее значение закрытой для ctypes копии системной переменной
LastErrorв вызывающем потоке.Доступность: Windows
Вызывает событие аудита
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 значением c в количестве count байтов. 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) -
Устанавливает текущее значение закрытой для ctypes копии системной переменной
LastErrorв вызывающем потоке в value и возвращает предыдущее значение.Доступность: Windows
Вызывает событие аудита
ctypes.set_last_errorс аргументомerror.
-
ctypes.sizeof(obj_or_type) -
Возвращает размер в байтах буфера памяти типа или экземпляра ctypes. Выполняет то же действие, что и оператор C
sizeof.
-
ctypes.string_at(ptr, size=-1) -
Возвращает байтовую строку по адресу void *ptr. Если указан параметр size, он используется в качестве размера, в противном случае предполагается, что строка завершается нулем.
Вызывает событие аудита
ctypes.string_atс аргументамиptr,size.
-
ctypes.WinError(code=None, descr=None) -
Эта функция, вероятно, имеет самое неудачное название в ctypes. Она создает экземпляр
OSError. Если code не указан, вызываетсяGetLastErrorдля определения кода ошибки. Если descr не указан, вызываетсяFormatError()для получения текстового описания ошибки.Доступность: Windows
Изменено в версии 3.3: Раньше создавался экземпляр
WindowsError, который теперь является псевдонимом дляOSError.
-
ctypes.wstring_at(ptr, size=-1) -
Возвращает строку расширенных символов по адресу void *ptr. Если указан параметр size, он используется как количество символов строки, в противном случае предполагается, что строка завершается нулем.
Вызывает событие аудита
ctypes.wstring_atс аргументамиptr,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, которые не являются указателями и не содержат их, теперь поддерживают сериализацию через pickle.Экземпляры имеют единственный атрибут:
-
value -
Этот атрибут содержит фактическое значение экземпляра. Для целочисленных типов и типов указателей это целое число, для символьных типов — объект bytes из одного символа или строка, для типов указателей на символы — объект bytes или строка Python.
При извлечении атрибута
valueиз экземпляра ctypes обычно каждый раз возвращается новый объект. Модульctypesне возвращает исходный объект, а всегда конструирует новый. То же самое справедливо и для всех остальных экземпляров объектов ctypes.
-
Фундаментальные типы данных при возврате в качестве результатов вызова внешних функций или, например, при получении полей структур или элементов массивов, прозрачно преобразуются в нативные типы Python. Другими словами, если внешняя функция имеет restype, равный c_char_p, вы всегда получите объект bytes языка 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). Конструктор принимает целочисленный адрес или объект 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 -
Представляет 8-битный тип данных C signed int. Является псевдонимом для
c_byte.
-
class ctypes.c_int16 -
Представляет 16-битный тип данных C signed int. Обычно является псевдонимом для
c_short.
-
class ctypes.c_int32 -
Представляет 32-битный тип данных C signed int. Обычно является псевдонимом для
c_int.
-
class ctypes.c_int64 -
Представляет 64-битный тип данных C 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_time_t -
Представляет тип данных C
time_t.Добавлено в версии 3.12.
-
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-битный тип данных C unsigned int. Является псевдонимом для
c_ubyte.
-
class ctypes.c_uint16 -
Представляет 16-битный тип данных C unsigned int. Обычно является псевдонимом для
c_ushort.
-
class ctypes.c_uint32 -
Представляет 32-битный тип данных C unsigned int. Обычно является псевдонимом для
c_uint.
-
class ctypes.c_uint64 -
Представляет 64-битный тип данных C 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и интерпретирует значение как односимвольную строку юникода. Конструктор принимает необязательный строковый инициализатор, длина строки должна составлять ровно один символ.
-
class ctypes.c_wchar_p -
Представляет тип данных C wchar_t*, который должен быть указателем на завершающуюся нулем строку расширенных символов (wide character string). Конструктор принимает целочисленный адрес или строку.
-
class ctypes.c_bool -
Представляет тип данных C bool (точнее, _Bool из C99). Его значением может быть
TrueилиFalse, а конструктор принимает любой объект, имеющий логическое значение (truth value).
-
class ctypes.HRESULT -
Представляет значение
HRESULT, которое содержит информацию об успехе или ошибке вызова функции или метода.Доступность: Windows
-
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_можно определить после инструкции class, объявляющей подкласс Structure; это позволяет создавать типы данных, прямо или косвенно ссылающиеся на самих себя:class List(Structure): pass List._fields_ = [("pnext", POINTER(List)), ... ]Однако переменная класса
_fields_должна быть определена до первого использования типа (создания экземпляра, вызова на немsizeof()и т. д.). Последующие присваивания переменной класса_fields_вызовут исключение AttributeError.Можно определять подклассы от подклассов типов структур; они наследуют поля базового класса плюс поля
_fields_, определенные в дочернем подклассе, если таковые имеются.
-
_pack_ -
Необязательное небольшое целое число, позволяющее переопределить выравнивание полей структуры в экземпляре. Атрибут
_pack_уже должен быть определен на момент присваивания_fields_, в противном случае он не возымеет эффекта. Установка этого атрибута в 0 аналогична его отсутствию.
-
_align_ -
Необязательное небольшое целое число, позволяющее переопределить выравнивание структуры при упаковке в память или распаковке из памяти. Установка этого атрибута в 0 аналогична его отсутствию.
Добавлено в версии 3.13.
-
_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_ -
Задает тип каждого элемента в массиве.
Конструкторы подклассов массивов принимают позиционные аргументы, используемые для последовательной инициализации элементов.
-
-
ctypes.ARRAY(type, length) -
Создает массив. Эквивалентно выражению
type * length, где type — тип данныхctypes, а length — целое число.Эта функция мягко устарела (soft deprecated) в пользу умножения. Планов по ее удалению нет.
-
class ctypes._Pointer -
Приватный абстрактный базовый класс для указателей.
Конкретные типы указателей создаются путем вызова функции
POINTER()с типом, на который будет ссылаться указатель; функцияpointer()делает это автоматически.Если указатель указывает на массив, его элементы можно читать и записывать с помощью стандартных операций индексации и срезов. Объекты-указатели не имеют размера, поэтому
len()вызовет исключениеTypeError. Отрицательные индексы будут читать из памяти перед указателем (как в C), а индексы вне диапазона, вероятно, приведут к аварийному завершению из-за нарушения прав доступа (если вам повезет).-
_type_ -
Задает тип, на который указывает указатель.
-
contents -
Возвращает объект, на который ссылается указатель. Присваивание этому атрибуту изменяет указатель так, чтобы он указывал на присваиваемый объект.
-
© 2001–2025 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/library/ctypes.html