ctypes — библиотека внешних функций для Python
Исходный код: Lib/ctypes
ctypes — это библиотека внешних функций для Python. Она предоставляет совместимые с C типы данных и позволяет вызывать функции из DLL и общих библиотек. С её помощью можно создавать обёртки для этих библиотек на чистом Python.
Это необязательный модуль. Если он отсутствует в вашей копии CPython, обратитесь к документации вашего дистрибутива (то есть к документации того, кто предоставил вам Python). Если вы являетесь поставщиком дистрибутива, см. раздел Требования к необязательным модулям.
Предупреждение
ctypes предоставляет низкоуровневый доступ к нативным библиотекам и памяти процесса, обходя механизмы безопасности Python и позволяя выполнять произвольный нативный код. Неправильное использование может привести к повреждению данных и объектов, раскрытию конфиденциальной информации, сбоям или иным нарушениям работы выполняющегося процесса.
Руководство по ctypes
Примечание. В некоторых примерах кода используется тип 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 — это стандартная библиотека 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.
В других системах для загрузки библиотеки требуется имя файла включая расширение, поэтому для загрузки библиотек нельзя использовать обращение к атрибутам. Следует использовать метод LoadLibrary() загрузчиков DLL или загрузить библиотеку, создав экземпляр CDLL вызовом конструктора.
Например, в Linux:
>>> cdll.LoadLibrary("libc.so.6")
<CDLL 'libc.so.6', handle ... at ...>
>>> libc = CDLL("libc.so.6")
>>> libc
<CDLL 'libc.so.6', handle ... at ...>
>>>
В macOS:
>>> cdll.LoadLibrary("libc.dylib")
<CDLL 'libc.dylib', handle ... at ...>
>>> libc = CDLL("libc.dylib")
>>> libc
<CDLL 'libc.dylib', 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 или строку.
Иногда 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 >>>
Модуль faulthandler может помочь при отладке сбоев, например ошибок сегментации, вызванных некорректными вызовами библиотек C.
None, целые числа, объекты bytes и строки (Unicode) — единственные нативные объекты Python, которые можно напрямую использовать в качестве параметров этих вызовов функций. None передаётся как указатель C NULL, объекты bytes и строки передаются как указатели на блок памяти, содержащий их данные (char* или wchar_t*). Целые числа Python передаются как тип C int, используемый по умолчанию на данной платформе; их значение маскируется, чтобы оно помещалось в тип C.
Прежде чем перейти к вызову функций с параметрами других типов, нужно узнать больше о типах данных ctypes.
Основные типы данных
ctypes определяет ряд примитивных типов данных, совместимых с C:
Тип ctypes | Тип C | Тип Python | |
|---|---|---|---|
_Bool |
| ||
char |
|
| |
|
|
| |
char |
| ||
unsigned char |
| ||
short |
| ||
unsigned short |
| ||
int |
| ||
| * | ||
| * | ||
| * | ||
| * | ||
unsigned int |
| ||
| * | ||
| * | ||
| * | ||
| * | ||
long |
| ||
unsigned long |
| ||
long long |
| ||
unsigned long long |
| ||
| * | ||
* | |||
| * | ||
float |
| ||
double |
| ||
long double |
| ||
char* (с завершающим NUL) |
|
| |
wchar_t* (с завершающим NUL) |
|
| |
void* |
|
| |
| |||
short int |
|
Кроме того, если арифметика комплексных чисел, совместимая с IEC 60559 (приложение G), поддерживается и в C, и в libffi, доступны следующие комплексные типы:
Тип ctypes | Тип C | Тип Python | |
|---|---|---|---|
float complex |
| ||
double complex |
| ||
long double complex |
|
Все эти типы можно создать, вызвав их с необязательным инициализатором соответствующего типа и значения:
>>> c_int()
c_long(0)
>>> c_wchar_p("Hello, World")
c_wchar_p(140018365411392)
>>> c_ushort(-3)
c_ushort(65533)
>>>
Конструкторы числовых типов преобразуют входные данные с помощью __bool__(), __index__() (для int), __float__() или __complex__(). Это означает, что c_bool принимает любой объект, имеющий логическое значение:
>>> empty_list = [] >>> c_bool(empty_list) c_bool(False)
Поскольку эти типы изменяемы, их значение можно изменить и позднее:
>>> 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, и тогда второй аргумент будет преобразован из однобайтового объекта Python bytes в символ типа 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' >>>
Если внешняя функция возвращает целое число, в качестве атрибута restype можно также использовать вызываемый объект Python (например, функцию или класс). Вызываемый объект будет вызван с целым числом, возвращённым функцией 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; подробности см. в справочном руководстве.
Передача указателей (или передача параметров по ссылке)
Иногда функция API C ожидает в качестве параметра указатель на тип данных — вероятно, чтобы записать значение в соответствующее место или передать данные, слишком большие для передачи по значению. Это также называют передачей параметров по ссылке.
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))
Дескрипторы полей можно получить из класса; они полезны при отладке, поскольку могут предоставлять полезную информацию. См. CField:
>>> POINT.x <ctypes.CField 'x' type=c_int, ofs=0, size=4> >>> POINT.y <ctypes.CField 'y' type=c_int, ofs=4, size=4> >>>
Предупреждение
ctypes не поддерживает передачу объединений или структур с битовыми полями в функции по значению. Хотя это может работать на 32-разрядной платформе x86, библиотека не гарантирует работу в общем случае. Объединения и структуры с битовыми полями следует всегда передавать в функции по указателю.
Расположение, выравнивание и порядок байтов структур и объединений
По умолчанию поля структур и объединений располагаются так же, как это делает компилятор C. Это поведение можно полностью переопределить, задав атрибут класса _layout_ в определении подкласса; подробности см. в документации атрибута.
Максимальное выравнивание полей и/или самой структуры можно задать с помощью атрибутов класса _pack_ и/или _align_ соответственно. Подробности см. в документации атрибутов.
ctypes использует для структур и объединений собственный порядок байтов платформы. Чтобы создавать структуры с нестандартным порядком байтов, можно использовать один из базовых классов: BigEndianStructure, LittleEndianStructure, BigEndianUnion и LittleEndianUnion. Эти классы не могут содержать поля-указатели.
Битовые поля в структурах и объединениях
Можно создавать структуры и объединения, содержащие битовые поля. Битовые поля допустимы только для целочисленных полей; их ширина задаётся третьим элементом кортежей _fields_:
>>> class Int(Structure):
... _fields_ = [("first_16", c_int, 16),
... ("second_16", c_int, 16)]
...
>>> print(Int.first_16)
<ctypes.CField 'first_16' type=c_int, ofs=0, bit_size=16, bit_offset=0>
>>> print(Int.second_16)
<ctypes.CField 'second_16' type=c_int, ofs=0, bit_size=16, bit_offset=16>
Важно отметить, что стандарт C не определяет распределение битовых полей и их расположение в памяти; реализация зависит от компилятора. По умолчанию Python пытается соответствовать поведению «родного» компилятора для текущей платформы. Подробности о поведении по умолчанию и его изменении см. в описании атрибута _layout_.
Массивы
Массивы — это последовательности, содержащие фиксированное количество экземпляров одного и того же типа.
Рекомендуемый способ создания типов массивов — умножить тип данных на положительное целое число:
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, original object return): при каждом получении атрибута создаётся новый эквивалентный объект:
>>> pi.contents is i False >>> pi.contents is pi.contents False >>>
Если присвоить атрибуту contents указателя другой экземпляр 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
>>>
Потокобезопасность без GIL
Начиная с Python 3.13, GIL можно отключить в сборке без GIL. В ctypes параллельное чтение и запись одного объекта безопасны, но работа с несколькими объектами одновременно — нет:
>>> number = c_int(42) >>> pointer_a = pointer(number) >>> pointer_b = pointer(number)
В приведённом выше примере при отключённом GIL безопасно, только если в каждый момент времени один объект читает данные по этому адресу или записывает их. Поэтому pointer_a можно совместно использовать и изменять из нескольких потоков, но только если pointer_b не пытается делать то же самое. Если это представляет проблему, рассмотрите возможность использования threading.Lock для синхронизации доступа к памяти:
>>> import threading >>> lock = threading.Lock() >>> # Thread 1 >>> with lock: ... pointer_a.contents = 24 >>> # Thread 2 >>> with lock: ... pointer_b.contents = 42
Преобразование типов
Обычно 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_Version — номер версии среды выполнения Python, закодированный одним целочисленным значением.
ctypes может получать доступ к таким значениям с помощью методов класса in_dll(). pythonapi — это предопределённый символ, предоставляющий доступ к API C Python:
>>> version = ctypes.c_int.in_dll(ctypes.pythonapi, "Py_Version") >>> print(hex(version.value)) 0x30c00a0
Расширенный пример, демонстрирующий также использование указателей, обращается к указателю 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 и возвращают тип результата, заданный загрузчиком библиотеки по умолчанию.
Это экземпляры закрытого локального класса _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 — кортеж, содержащий параметры, изначально переданные при вызове функции. Это позволяет настроить поведение в зависимости от использованных аргументов.
Объект, возвращаемый этой функцией, будет возвращён при вызове внешней функции, но функция также может проверить значение результата и вызвать исключение, если вызов внешней функции завершился неудачей.
-
В 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 должен быть кортежем из двух элементов
(name_or_ordinal, library). Первый элемент — имя экспортированной функции в виде строки или порядковый номер экспортированной функции в виде небольшого целого числа. Второй элемент — экземпляр разделяемой библиотеки.
- prototype(vtbl_index, name[, paramflags[, iid]])
-
Возвращает иностранную функцию, которая вызывает метод COM. vtbl_index — индекс в таблице виртуальных функций, небольшое неотрицательное целое число. name — имя метода COM. iid — необязательный указатель на идентификатор интерфейса, используемый для расширенного формирования отчётов об ошибках.
Если iid не указан, при сбое вызова метода COM возникает исключение
OSError. Если iid указан, вместо него возникает исключениеCOMError.Методы COM используют специальное соглашение о вызовах: в качестве первого аргумента им требуется указатель на интерфейс COM, помимо параметров, указанных в кортеже
argtypes.Доступность: Windows
Необязательный параметр 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 для дополнительной обработки выходных данных и проверки ошибок. Функция API 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.CopyComPointer(src, dst) -
Копирует указатель COM из src в dst и возвращает специфичное для Windows значение
HRESULT.Если src не является
NULL, вызывается его методAddRef, увеличивающий счётчик ссылок.В отличие от этого, перед присваиванием нового значения счётчик ссылок dst не уменьшается. Если dst не является
NULL, вызывающий код должен при необходимости уменьшить счётчик ссылок, вызвав его методRelease.Доступность: Windows
Добавлено в версии 3.14.
-
ctypes.cast(obj, type) -
Эта функция аналогична оператору приведения типов в 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).Предупреждение
Если 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)
-
Эта функция создаёт изменяемый буфер символов Unicode. Возвращаемый объект — массив ctypes типа
c_wchar.Функция принимает те же аргументы, что и
create_string_buffer(), за исключением того, что init должен быть строкой, а size задаёт количество элементовc_wchar.Вызывает событие аудита
ctypes.create_unicode_bufferс аргументамиinit,size.
-
ctypes.DllCanUnloadNow() -
Эта функция является точкой перехвата, позволяющей реализовать внутрипроцессные COM-серверы с помощью ctypes. Она вызывается функцией DllCanUnloadNow, экспортируемой библиотекой расширения _ctypes.
Доступность: Windows
-
ctypes.DllGetClassObject() -
Эта функция является точкой перехвата, позволяющей реализовать внутрипроцессные COM-серверы с помощью ctypes. Она вызывается функцией DllGetClassObject, экспортируемой библиотекой расширения
_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.util.dllist() -
Пытается получить список путей к разделяемым библиотекам, загруженным в текущий процесс. Эти пути не нормализуются и никак не обрабатываются. Если системные API платформы завершаются с ошибкой, функция может вызвать исключение
OSError. Точная функциональность зависит от системы.На большинстве платформ первый элемент списка обозначает текущий исполняемый файл. Это может быть пустая строка.
Доступность: Windows, macOS, iOS, glibc, BSD libc, musl
Добавлено в версии 3.14.
-
ctypes.FormatError([code]) -
Возвращает текстовое описание кода ошибки code. Если код ошибки не указан, используется последний код ошибки, полученный вызовом функции Windows API
GetLastError().Доступность: Windows
-
ctypes.GetLastError() -
Возвращает последний код ошибки, установленный Windows в вызывающем потоке. Эта функция напрямую вызывает функцию Windows
GetLastError(); она не возвращает закрытую копию кода ошибки из ctypes.Доступность: Windows
-
ctypes.get_errno() -
Возвращает текущее значение закрытой копии системной переменной
errnoв ctypes для вызывающего потока.Вызывает событие аудита
ctypes.get_errnoбез аргументов.
-
ctypes.get_last_error() -
Возвращает текущее значение закрытой копии системной переменной
LastErrorв ctypes для вызывающего потока.Доступность: 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.
Особенность реализации CPython: Результирующий тип указателя кэшируется в атрибуте
__pointer_type__объекта type. До первого вызоваPOINTERэтому атрибуту можно присвоить значение, чтобы задать пользовательский тип указателя. Однако делать это не рекомендуется: вручную создать подходящий тип указателя сложно, не полагаясь на особенности реализации, которые могут измениться в будущих версиях Python.
-
ctypes.pointer(obj, /) -
Создаёт новый экземпляр указателя на obj. Возвращаемый объект имеет тип
POINTER(type(obj)).Примечание. Если нужно лишь передать указатель на объект при вызове иностранной функции, используйте
byref(obj)— этот способ намного быстрее.
-
ctypes.resize(obj, size) -
Эта функция изменяет размер внутреннего буфера памяти объекта obj, который должен быть экземпляром типа ctypes. Нельзя уменьшить буфер так, чтобы он стал меньше собственного размера типа объекта, задаваемого
sizeof(type(obj)), но можно увеличить его.
-
ctypes.set_errno(value) -
Устанавливает для закрытой копии системной переменной
errnoв ctypes в вызывающем потоке значение value и возвращает предыдущее значение.Вызывает событие аудита
ctypes.set_errnoс аргументомerrno.
-
ctypes.set_last_error(value) -
Устанавливает для закрытой копии системной переменной
LastErrorв ctypes в вызывающем потоке значение 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) -
Создаёт экземпляр
OSError. Если code не указан, для определения кода ошибки вызываетсяGetLastError(). Если descr не указан, для получения текстового описания ошибки вызываетсяFormatError().Доступность: Windows
Изменено в версии 3.3: Ранее создавался экземпляр
WindowsError, который теперь является псевдонимомOSError.
-
ctypes.wstring_at(ptr, size=-1) -
Возвращает строку широких символов по адресу void *ptr. Если указан size, он задаёт количество символов в строке; в противном случае предполагается, что строка завершается нулевым символом.
Вызывает событие аудита
ctypes.wstring_atс аргументамиptr,size.
-
ctypes.memoryview_at(ptr, size, readonly=False) -
Возвращает объект
memoryviewдлиной size, ссылающийся на память, начинающуюся по адресу void *ptr.Если readonly имеет значение true, возвращённый объект
memoryviewнельзя использовать для изменения базовой памяти. (Изменения, внесённые другими способами, по-прежнему будут отражаться в возвращённом объекте.)Эта функция похожа на
string_at(), но не копирует указанную область памяти. Она является семантически эквивалентной, но более эффективной альтернативойmemoryview((c_byte * size).from_address(ptr)). (Хотяfrom_address()принимает только целые числа, ptr также может быть объектом типаctypes.POINTERилиbyref().)Вызывает событие аудита
ctypes.memoryview_atс аргументамиaddress,size,readonly.Добавлено в версии 3.14.
Типы данных
-
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:
-
__pointer_type__ -
Тип указателя, созданный вызовом
POINTER()для соответствующего типа данных ctypes. Если тип указателя ещё не создан, атрибут отсутствует.Добавлено в версии 3.14.
Общие переменные экземпляров типов данных 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.
У каждого подкласса есть атрибут класса:
-
_type_ -
Атрибут класса, содержащий внутренний код типа в виде строки из одного символа. Сводную информацию см. в разделе Основные типы данных.
Типы, отмеченные в сводке звёздочкой (*), могут быть (или всегда являются) псевдонимами другого подкласса
_SimpleCDataи не обязательно используют указанный код типа. Например, если типы C long, long long и time_t на платформе совпадают, тоc_long,c_longlongиc_time_tссылаются на один класс —c_long, кодом_type_которого является'l'. Код'L'использоваться не будет.
-
Основные типы данных при возврате в качестве результата вызова внешней функции, а также, например, при получении членов полей структуры или элементов массива прозрачно преобразуются в собственные типы 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_double_complex -
Представляет тип данных C double complex, если он доступен. Конструктор принимает необязательное начальное значение
complex.Добавлено в версии 3.14.
-
class ctypes.c_float_complex -
Представляет тип данных C float complex, если он доступен. Конструктор принимает необязательное начальное значение
complex.Добавлено в версии 3.14.
-
class ctypes.c_longdouble_complex -
Представляет тип данных C long double complex, если он доступен. Конструктор принимает необязательное начальное значение
complex.Добавлено в версии 3.14.
-
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. Конструктор принимает необязательное целочисленное начальное значение; проверка переполнения не выполняется. На платформах, где
sizeof(long long) == sizeof(long), этот тип является псевдонимомc_long.
-
class ctypes.c_short -
Представляет тип данных C signed short. Конструктор принимает необязательное целочисленное начальное значение; проверка переполнения не выполняется.
-
class ctypes.c_size_t -
Представляет тип данных C
size_t. Обычно это псевдоним другого беззнакового целочисленного типа.
-
class ctypes.c_ssize_t -
Представляет тип данных
Py_ssize_t. Это знаковая версияsize_t, то есть тип POSIXssize_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. Конструктор принимает необязательное целочисленное начальное значение; проверка переполнения не выполняется. На платформах, где
sizeof(long long) == sizeof(long), этот тип является псевдонимомc_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 -
Представляет значение
HRESULT, содержащее сведения об успешном выполнении или ошибке при вызове функции или метода.Доступность: Windows
-
class ctypes.py_object -
Представляет тип данных C PyObject*. Вызов без аргумента создаёт указатель
NULLPyObject*.Изменено в версии 3.14:
py_objectтеперь является обобщённым типом.
Модуль ctypes.wintypes предоставляет множество других типов данных, специфичных для Windows, например HWND, WPARAM, VARIANT_BOOL или DWORD. Там также определены некоторые полезные структуры, например MSG или RECT.
Структурированные типы данных
-
class ctypes.Union(*args, **kw) -
Абстрактный базовый класс для объединений с порядком байтов, используемым в нативном формате.
Объединения имеют общие атрибуты и поведение со структурами; подробности см. в документации
Structure.
-
class ctypes.BigEndianUnion(*args, **kw) -
Абстрактный базовый класс для объединений с порядком байтов от старшего к младшему.
Добавлено в версии 3.11.
-
class ctypes.LittleEndianUnion(*args, **kw) -
Абстрактный базовый класс для объединений с порядком байтов от младшего к старшему.
Добавлено в версии 3.11.
-
class ctypes.BigEndianStructure(*args, **kw) -
Абстрактный базовый класс для структур с порядком байтов от старшего к младшему.
-
class ctypes.LittleEndianStructure(*args, **kw) -
Абстрактный базовый класс для структур с порядком байтов от младшего к старшему.
Структуры и объединения с ненативным порядком байтов не могут содержать поля указательного типа или другие типы данных, содержащие поля указательного типа.
-
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_можно задать только один раз. Последующие присваивания приведут к возникновению исключенияAttributeError.Кроме того, переменную класса
_fields_необходимо определить до первого использования типа структуры или объединения: создания экземпляра или подкласса, вызова для негоsizeof()и т. д. Последующие присваивания_fields_приведут к возникновению исключенияAttributeError. Если_fields_не была задана до такого использования, структура или объединение не будет иметь собственных полей, как если бы_fields_была пустой.Подклассы типов структур наследуют поля базового класса, а также поля, заданные в переменной
_fields_подкласса, если она есть.
-
_pack_ -
Необязательное небольшое целое число, позволяющее переопределить выравнивание полей структуры в экземпляре.
Реализовано только для совместимой с MSVC схемы размещения в памяти (см.
_layout_).Значение
_pack_, равное 0, эквивалентно отсутствию этого атрибута. В противном случае значение должно быть положительной степенью двойки. Эффект эквивалентен#pragma pack(N)в C, за исключением того, чтоctypesможет допускать значения n больше тех, которые принимает компилятор._pack_необходимо определить до присваивания_fields_, иначе оно не окажет никакого эффекта.Устарело с версии 3.14, будет удалено в версии 3.19: По историческим причинам, если
_pack_не равно нулю, по умолчанию используется совместимая с MSVC схема размещения. На платформах, отличных от Windows, это значение по умолчанию устарело и, как планируется, в Python 3.19 будет приводить к ошибке. Если такое поведение необходимо, явно задайте для_layout_значение'ms'.
-
_align_ -
Необязательное небольшое целое число, позволяющее увеличить выравнивание структуры при упаковке или распаковке из памяти.
Значение не должно быть отрицательным. Эффект эквивалентен
__attribute__((aligned(N)))в GCC или#pragma align(N)в MSVC, за исключением того, чтоctypesможет допускать значения, которые компилятор отклонил бы._align_может только увеличивать требования к выравниванию структуры. Присваивание значения 0 или 1 не оказывает никакого эффекта.Использовать значения, не являющиеся степенями двойки, не рекомендуется: это может привести к неожиданному поведению.
_align_необходимо определить до присваивания_fields_, иначе оно не окажет никакого эффекта.Добавлено в версии 3.13.
-
_layout_ -
Необязательная строка с названием схемы размещения структуры или объединения. В настоящее время можно задать следующие значения:
-
"ms": схема размещения, используемая компилятором Microsoft (MSVC). В GCC и Clang эту схему можно выбрать с помощью__attribute__((ms_struct)). -
"gcc-sysv": схема размещения, используемая GCC с моделью данных System V или «подобной SysV», применяемой в Linux и macOS. При использовании этой схемы_pack_должен быть не задан или равен нулю.
Если значение
ctypesне задано явно, будет использоваться значение по умолчанию, соответствующее соглашениям платформы. Это значение по умолчанию может измениться в будущих выпусках Python (например, если новая платформа получит официальную поддержку или будет обнаружено различие между похожими платформами). В настоящее время используются следующие значения по умолчанию:- В Windows:
"ms" - Если задано
_pack_:"ms". (Это значение устарело; см. документацию_pack_.) - В остальных случаях:
"gcc-sysv"
_layout_необходимо определить до присваивания_fields_, иначе оно не окажет никакого эффекта.Добавлено в версии 3.14.
-
-
_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.CField(*args, **kw) -
Дескриптор полей
StructureиUnion. Например:>>> class Color(Structure): ... _fields_ = ( ... ('red', c_uint8), ... ('green', c_uint8), ... ('blue', c_uint8), ... ('intense', c_bool, 1), ... ('blinking', c_bool, 1), ... ) ... >>> Color.red <ctypes.CField 'red' type=c_ubyte, ofs=0, size=1> >>> Color.green.type <class 'ctypes.c_ubyte'> >>> Color.blue.byte_offset 2 >>> Color.intense <ctypes.CField 'intense' type=c_bool, ofs=3, bit_size=1, bit_offset=0> >>> Color.blinking.bit_offset 1Все атрибуты доступны только для чтения.
Объекты
CFieldсоздаются с помощью_fields_; не создавайте экземпляры класса напрямую.Добавлено в версии 3.14: Ранее у дескрипторов были только атрибуты
offsetиsize, а также строковое представление, доступное для чтения; классCFieldнельзя было использовать напрямую.-
name -
Имя поля в виде строки.
-
type -
Тип поля в виде класса ctypes.
-
offset -
byte_offset -
Смещение поля в байтах.
Для битовых полей это смещение базовой выровненной по байтам единицы хранения; см.
bit_offset.
-
byte_size -
Размер поля в байтах.
Для битовых полей это размер базовой единицы хранения. Обычно он совпадает с размером типа битового поля.
-
size -
Для не битовых полей эквивалентно
byte_size.Для битовых полей содержит упакованное в биты значение, сохранённое для обратной совместимости и объединяющее
bit_sizeиbit_offset. Рекомендуется использовать отдельные атрибуты.
-
is_bitfield -
True, если это битовое поле.
-
bit_offset -
bit_size -
Расположение битового поля в его единице хранения, то есть в
byte_sizeбайтах памяти, начиная со смещенияbyte_offset.Чтобы получить значение поля, прочитайте единицу хранения как целое число, выполните сдвиг влево на
bit_offsetи возьмитеbit_sizeмладших значащих битов.Для не битовых полей
bit_offsetравно нулю, аbit_sizeравноbyte_size * 8.
-
is_anonymous -
True, если поле является анонимным, то есть содержит вложенные подполя, которые должны объединяться с содержащей их структурой или объединением.
-
Массивы и указатели
-
class ctypes.Array(*args) -
Абстрактный базовый класс для массивов.
Рекомендуемый способ создания конкретных типов массивов — умножение любого типа данных
ctypesна неотрицательное целое число. Также можно создать подкласс этого типа и определить переменные класса_length_и_type_. Элементы массива можно читать и записывать с помощью стандартного доступа по индексу и срезу; результат чтения среза не является объектомArray.Массивы являются обобщёнными по типу своих элементов.
-
_length_ -
Положительное целое число, задающее количество элементов массива. Выход за допустимые границы индекса приводит к возникновению исключения
IndexError. Возвращается функциейlen().
-
_type_ -
Задаёт тип каждого элемента массива.
Конструкторы подклассов массивов принимают позиционные аргументы, используемые для последовательной инициализации элементов.
-
-
ctypes.ARRAY(type, length) -
Создаёт массив. Эквивалентно
type * length, где type — тип данныхctypes, а length — целое число.Мягко объявлено устаревшим с версии 3.14: Рекомендуется использовать умножение.
-
class ctypes._Pointer -
Приватный абстрактный базовый класс для указателей.
Конкретные типы указателей создаются вызовом
POINTER()с типом, на который будет указывать указатель; это автоматически выполняется функциейpointer().Если указатель указывает на массив, его элементы можно читать и записывать с помощью стандартного доступа по индексу и срезу. Объекты-указатели не имеют размера, поэтому вызов
len()приведёт к возникновению исключенияTypeError. Отрицательные индексы будут читать память перед указателем (как в C), а индексы за допустимыми границами, вероятно, приведут к сбою из-за нарушения доступа (если повезёт).-
_type_ -
Задаёт тип, на который указывает указатель.
-
contents -
Возвращает объект, на который указывает указатель. Присваивание этому атрибуту изменяет указатель так, чтобы он указывал на присвоенный объект.
-
Исключения
-
exception ctypes.ArgumentError -
Это исключение возникает, когда при вызове внешней функции не удаётся преобразовать один из переданных аргументов.
-
exception ctypes.COMError(hresult, text, details) -
Это исключение возникает, если вызов метода COM завершается с ошибкой.
-
hresult -
Целочисленное значение, представляющее код ошибки.
-
text -
Сообщение об ошибке.
-
details -
Кортеж из 5 элементов
(descr, source, helpfile, helpcontext, progid).descr — текстовое описание. source — зависящий от языка
ProgIDкласса или приложения, вызвавшего ошибку. helpfile — путь к файлу справки. helpcontext — идентификатор раздела справки. progid —ProgIDинтерфейса, определившего ошибку.
Доступность: Windows
Добавлено в версии 3.14.
-
© 2001 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.14/library/ctypes.html