ctypes — Библиотека внешних функций для Python
Исходный код: Lib/ctypes
ctypes — это библиотека внешних функций для Python. Она предоставляет совместимые с C типы данных и позволяет вызывать функции из DLL или динамических библиотек. Она может использоваться для обертывания этих библиотек в чистом Python.
Учебник по ctypes
Примечание: Примеры кода в этом руководстве используют doctest, чтобы убедиться, что они действительно работают. Поскольку некоторые примеры кода ведут себя по-разному под Linux, Windows или macOS, они содержат директивы doctest в комментариях.
Примечание: Некоторые примеры кода ссылаются на тип ctypes c_int. На платформах, где sizeof(long) == sizeof(int) он является псевдонимом для c_long. Поэтому вы не должны путаться, если c_long выводится, если ожидается c_int — на самом деле они являются одним и тем же типом.
Загрузка динамических библиотек
ctypes экспортирует объекты cdll, а в Windows также windll и oledll для загрузки динамических библиотек.
Библиотеки загружаются путем доступа к ним как к атрибутам этих объектов. cdll загружает библиотеки, которые экспортируют функции, используя стандартную cdecl конвенцию вызова, в то время как windll библиотеки вызывают функции, используя stdcall конвенцию вызова. oledll также использует stdcall конвенцию вызова и предполагает, что функции возвращают код ошибки Windows HRESULT. Код ошибки используется для автоматического повышения исключения OSError при неудачном вызове функции.
Изменено в версии 3.3: Ошибки Windows раньше поднимали WindowsError, который теперь является псевдонимом для OSError.
Вот несколько примеров для Windows. Обратите внимание, что msvcrt — это стандартная библиотека MS C, содержащая большинство стандартных функций C, и использующая конвенцию вызова cdecl:
>>> from ctypes import * >>> print(windll.kernel32) <WinDLL 'kernel32', handle ... at ...> >>> print(cdll.msvcrt) <CDLL 'msvcrt', handle ... at ...> >>> libc = cdll.msvcrt >>>
Windows автоматически добавляет обычный .dll суффикс файла.
Примечание
Доступ к стандартной библиотеке C через cdll.msvcrt будет использовать устаревшую версию библиотеки, которая может быть несовместима с той, которая используется Python. В тех случаях, где возможно, используйте собственные функции Python, или же импортируйте и используйте модуль msvcrt.
В Linux необходимо указать имя файла включая расширение для загрузки библиотеки, поэтому доступ к атрибутам для загрузки библиотек невозможен. Должно использоваться метод LoadLibrary() загрузчиков dll, или вы должны загрузить библиотеку, создав экземпляр CDLL, вызвав конструктор:
>>> cdll.LoadLibrary("libc.so.6")
<CDLL 'libc.so.6', handle ... at ...>
>>> libc = CDLL("libc.so.6")
>>> libc
<CDLL 'libc.so.6', handle ... at ...>
>>>
Доступ к функциям из загруженных DLL
Функции доступны как атрибуты объектов dll:
>>> from ctypes import *
>>> libc.printf
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.GetModuleHandleA)
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.MyOwnFunction)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "ctypes.py", line 239, in __getattr__
func = _StdcallFuncPtr(name, self)
AttributeError: function 'MyOwnFunction' not found
>>>
Обратите внимание, что системные dll win32, такие как kernel32 и user32 часто экспортируют ANSI и UNICODE версии функции. Версия UNICODE экспортируется с добавлением W к имени, а версия ANSI экспортируется с добавлением A к имени. Функция win32 GetModuleHandle , которая возвращает дескриптор модуля для заданного имени модуля, имеет следующий прототип C, и макрос используется для экспорта одного из них как GetModuleHandle в зависимости от того, определен UNICODE или нет:
/* ANSI version */ HMODULE GetModuleHandleA(LPCSTR lpModuleName); /* UNICODE version */ HMODULE GetModuleHandleW(LPCWSTR lpModuleName);
windll не пытается выбрать один из них магически, вы должны получить доступ к необходимой версии, явно указав GetModuleHandleA или GetModuleHandleW и затем вызвать её с объектами 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. Этот пример использует функцию time() , которая возвращает системное время в секундах с момента эпохи Unix, и функцию GetModuleHandleA() , которая возвращает дескриптор модуля win32.
Этот пример вызывает обе функции с указателем NULL (None должен использоваться в качестве указателя NULL):
>>> print(libc.time(None)) 1150640792 >>> print(hex(windll.kernel32.GetModuleHandleA(None))) 0x1d000000 >>>
ValueError возникает, когда вы вызываете функцию с stdcall конвенцией вызова с cdecl конвенцией вызова, или наоборот:
>>> cdll.kernel32.GetModuleHandleA(None) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: Procedure probably called with not enough arguments (4 bytes missing) >>> >>> windll.msvcrt.printf(b"spam") Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: Procedure probably called with too many arguments (4 bytes in excess) >>>
Чтобы узнать правильную конвенцию вызова, вам нужно посмотреть в заголовочный файл C или документацию для функции, которую вы хотите вызвать.
В Windows ctypes использует обработку структурированных исключений win32 для предотвращения сбоев из-за общих ошибок защиты при вызове функций с недопустимыми значениями аргументов:
>>> windll.kernel32.GetModuleHandleA(32) Traceback (most recent call last): File "<stdin>", line 1, in <module> OSError: exception: access violation reading 0x00000020 >>>
Однако существуют способы сбоя Python с использованием ctypes, поэтому следует проявлять осторожность. Модуль faulthandler может быть полезен для отладки сбоев (например, из-за ошибок сегментации, вызванных ошибочными вызовами библиотек C).
None, целые числа, объекты bytes и (unicode) строки — единственные родные объекты Python, которые могут непосредственно использоваться в качестве параметров в этих вызовах функций. None передаётся как указатель C NULL, объекты bytes и строки передаются как указатели на блок памяти, содержащий их данные (char* или wchar_t*). Целые числа Python передаются как тип C int платформы по умолчанию, их значение маскируется для соответствия типу C.
Прежде чем перейти к вызову функций с другими типами параметров, мы должны узнать больше о типах данных ctypes.
Основные типы данных
ctypes определяет ряд примитивных типов данных, совместимых с C:
Тип ctypes | Тип C | Тип Python |
|---|---|---|
| bool (1) | |
| объект bytes длиной 1 символ | |
| строка длиной 1 символ | |
| int | |
| int | |
| int | |
| int | |
| int | |
| int | |
| int | |
| int | |
| int | |
| int | |
| int | |
| int | |
| float | |
| float | |
| float | |
| объект bytes или | |
| строка или | |
| int или |
- Конструктор принимает любой объект с истинным значением.
Все эти типы можно создать, вызвав их с необязательным инициализатором соответствующего типа и значения:
>>> c_int()
c_long(0)
>>> c_wchar_p("Hello, World")
c_wchar_p(140018365411392)
>>> c_ushort(-3)
c_ushort(65533)
>>>
Поскольку эти типы изменяемы, их значение также можно изменить позже:
>>> i = c_int(42) >>> print(i) c_long(42) >>> print(i.value) 42 >>> i.value = -99 >>> print(i.value) -99 >>>
Присвоение нового значения экземплярам типов указателей c_char_p, c_wchar_p и c_void_p изменяет местоположение в памяти, на которое они указывают, а не содержимое блока памяти (разумеется, так как объекты Python bytes неизменяемы):
>>> s = "Hello, World" >>> c_s = c_wchar_p(s) >>> print(c_s) c_wchar_p(139966785747344) >>> print(c_s.value) Hello World >>> c_s.value = "Hi, there" >>> print(c_s) # the memory location has changed c_wchar_p(139966783348904) >>> print(c_s.value) Hi, there >>> print(s) # first object is unchanged Hello, World >>>
Однако следует быть осторожным, чтобы не передавать их функциям, ожидающим указателей на изменяемую память. Если вам нужны изменяемые блоки памяти, в ctypes есть функция create_string_buffer(), которая создаёт их различными способами. Текущее содержимое блока памяти можно получить (или изменить) с помощью свойства raw; если вы хотите получить его как строку с завершением нулём, используйте свойство value.
>>> from ctypes import * >>> p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes >>> print(sizeof(p), repr(p.raw)) 3 b'\x00\x00\x00' >>> p = create_string_buffer(b"Hello") # create a buffer containing a NUL terminated string >>> print(sizeof(p), repr(p.raw)) 6 b'Hello\x00' >>> print(repr(p.value)) b'Hello' >>> p = create_string_buffer(b"Hello", 10) # create a 10 byte buffer >>> print(sizeof(p), repr(p.raw)) 10 b'Hello\x00\x00\x00\x00\x00' >>> p.value = b"Hi" >>> print(sizeof(p), repr(p.raw)) 10 b'Hi\x00lo\x00\x00\x00\x00\x00' >>>
Функция create_string_buffer() заменяет функцию c_buffer() (которая всё ещё доступна как псевдоним), а также функцию c_string() из более ранних выпусков ctypes. Для создания изменяемого блока памяти, содержащего символы Юникода типа C wchar_t, используйте функцию create_unicode_buffer().
Вызов функций (продолжение)
Обратите внимание, что printf выводит в реальный канал стандартного вывода, а не в sys.stdout, поэтому эти примеры будут работать только в командной строке, а не изнутри IDLE или PythonWin:
>>> printf = libc.printf >>> printf(b"Hello, %s\n", b"World!") Hello, World! 14 >>> printf(b"Hello, %S\n", "World!") Hello, World! 14 >>> printf(b"%d bottles of beer\n", 42) 42 bottles of beer 19 >>> printf(b"%f bottles of beer\n", 42.5) Traceback (most recent call last): File "<stdin>", line 1, in <module> ArgumentError: argument 2: 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 Platforms, соглашение о вызове для функций с переменным числом аргументов отличается от соглашения о вызове обычных функций.
На этих платформах необходимо указать атрибут argtypes для обычных, не имеющих переменного числа аргументов, аргументов функции:
libc.printf.argtypes = [ctypes.c_char_p]
Поскольку указание атрибута ограничивает переносимость, рекомендуется всегда указывать argtypes для всех функций с переменным числом аргументов.
Вызов функций с пользовательскими типами данных
Вы также можете настроить преобразование аргументов ctypes, чтобы разрешить использование экземпляров ваших собственных классов в качестве аргументов функции. ctypes ищет атрибут _as_parameter_, и использует его в качестве аргумента функции. Конечно, это должно быть целое число, строка или объект bytes:
>>> class Bottles: ... def __init__(self, number): ... self._as_parameter_ = number ... >>> bottles = Bottles(42) >>> printf(b"%d bottles of beer\n", bottles) 42 bottles of beer 19 >>>
Если вы не хотите хранить данные экземпляра в переменной экземпляра _as_parameter_, вы можете определить свойство property, которое делает атрибут доступным по запросу.
Указание требуемых типов аргументов (прототипы функций)
Можно указать требуемые типы аргументов функций, экспортируемых из DLL, установив атрибут argtypes.
argtypes должно быть последовательностью типов данных C (функция printf вероятно, не является хорошим примером здесь, потому что она принимает переменное количество аргументов разных типов в зависимости от строки формата, но с другой стороны, это довольно удобно для экспериментов с этой функцией):
>>> printf.argtypes = [c_char_p, c_char_p, c_int, c_double] >>> printf(b"String '%s', Int %d, Double %f\n", b"Hi", 10, 2.2) String 'Hi', Int 10, Double 2.200000 37 >>>
Указание формата защищает от несовместимых типов аргументов (как и прототип функции C) и пытается преобразовать аргументы в допустимые типы:
>>> printf(b"%d %d %d", 1, 2, 3) Traceback (most recent call last): File "<stdin>", line 1, in <module> ArgumentError: argument 2: TypeError: wrong type >>> printf(b"%s %d %f\n", b"X", 2, 3) X 2 3.000000 13 >>>
Если вы определили свои собственные классы, которые вы передаёте в вызовы функций, вам необходимо реализовать метод класса from_param() для того, чтобы использовать их в последовательности argtypes. Метод класса from_param() получает переданный в вызов функции объект Python, он должен выполнить проверку типа или выполнить любые необходимые действия, чтобы убедиться, что этот объект приемлем, а затем вернуть сам объект, его атрибут _as_parameter_ или всё, что вы хотите передать как аргумент функции C в данном случае. Результат должен быть целым числом, строкой, объектом bytes, экземпляром ctypes или объектом со свойством _as_parameter_.
Типы возвращаемых значений
По умолчанию предполагается, что функции возвращают тип C int. Другие типы возвращаемых значений можно указать, установив атрибут restype объекта функции.
Вот более сложный пример, он использует функцию strchr, которая ожидает указатель на строку и символ и возвращает указатель на строку:
>>> strchr = libc.strchr
>>> strchr(b"abcdef", ord("d"))
8059983
>>> strchr.restype = c_char_p # c_char_p is a pointer to a string
>>> strchr(b"abcdef", ord("d"))
b'def'
>>> print(strchr(b"abcdef", ord("x")))
None
>>>
Если вы хотите избежать вызовов ord("x") выше, вы можете установить атрибут argtypes, и второй аргумент будет преобразован из объекта Python типа bytes, содержащего один символ, в C-символ:
>>> strchr.restype = c_char_p >>> strchr.argtypes = [c_char_p, c_char] >>> strchr(b"abcdef", b"d") 'def' >>> strchr(b"abcdef", b"def") Traceback (most recent call last): File "<stdin>", line 1, in <module> ArgumentError: argument 2: TypeError: one character string expected >>> print(strchr(b"abcdef", b"x")) None >>> strchr(b"abcdef", b"d") 'def' >>>
Вы также можете использовать вызываемый объект Python (например, функцию или класс) в качестве атрибута restype, если внешняя функция возвращает целое число. Вызываемый объект будет вызван с целым числом, возвращённым C-функцией, и результат этого вызова будет использован как результат вызова вашей функции. Это полезно для проверки значений возврата ошибок и автоматического повышения исключения:
>>> GetModuleHandle = windll.kernel32.GetModuleHandleA
>>> def ValidHandle(value):
... if value == 0:
... raise WinError()
... return value
...
>>>
>>> GetModuleHandle.restype = ValidHandle
>>> GetModuleHandle(None)
486539264
>>> GetModuleHandle("something silly")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in ValidHandle
OSError: [Errno 126] The specified module could not be found.
>>>
WinError — это функция, которая вызывает Windows-API FormatMessage() для получения строкового представления кода ошибки и возвращает исключение. WinError принимает необязательный параметр кода ошибки; если он не указан, он вызывает GetLastError() для его получения.
Обратите внимание, что механизм проверки ошибок значительно мощнее через атрибут errcheck; подробности см. в справочном руководстве.
Передача указателей (или: передача параметров по ссылке)
Иногда C-функция API ожидает указатель на тип данных в качестве параметра, вероятно, для записи в соответствующее местоположение, или если данные слишком велики для передачи по значению. Это также известно как передача параметров по ссылке.
ctypes экспортирует функцию byref(), которая используется для передачи параметров по ссылке. Тот же эффект можно получить с помощью функции pointer(), хотя pointer() делает гораздо больше работы, так как она строит реальный объект-указатель, поэтому быстрее использовать byref(), если вам не нужен объект указателя сам по себе в Python:
>>> i = c_int() >>> f = c_float() >>> s = create_string_buffer(b'\000' * 32) >>> print(i.value, f.value, repr(s.value)) 0 0.0 b'' >>> libc.sscanf(b"1 3.14 Hello", b"%d %f %s", ... byref(i), byref(f), s) 3 >>> print(i.value, f.value, repr(s.value)) 1 3.1400001049 b'Hello' >>>
Структуры и объединения
Структуры и объединения должны быть производными от базовых классов Structure и Union, которые определены в модуле ctypes. Каждый подкласс должен определить атрибут _fields_. _fields_ должен быть списком 2-х кортежей, содержащим имя поля и тип поля.
Тип поля должен быть типом ctypes, например c_int, или любым другим производным типом ctypes: структура, объединение, массив, указатель.
Вот простой пример структуры POINT, которая содержит два целых числа с именами x и y, а также демонстрирует, как инициализировать структуру в конструкторе:
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = [("x", c_int),
... ("y", c_int)]
...
>>> point = POINT(10, 20)
>>> print(point.x, point.y)
10 20
>>> point = POINT(y=5)
>>> print(point.x, point.y)
0 5
>>> POINT(1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: too many initializers
>>>
Однако вы можете создавать гораздо более сложные структуры. Структура может сама содержать другие структуры, используя структуру как тип поля.
Вот структура RECT, которая содержит два POINT с именами upperleft и lowerright:
>>> class RECT(Structure):
... _fields_ = [("upperleft", POINT),
... ("lowerright", POINT)]
...
>>> rc = RECT(point)
>>> print(rc.upperleft.x, rc.upperleft.y)
0 5
>>> print(rc.lowerright.x, rc.lowerright.y)
0 0
>>>
Вложенные структуры также могут быть инициализированы в конструкторе несколькими способами:
>>> r = RECT(POINT(1, 2), POINT(3, 4)) >>> r = RECT((1, 2), (3, 4))
Поля дескрипторы можно получить из класса; они полезны для отладки, так как могут предоставлять полезную информацию:
>>> print(POINT.x) <Field type=c_long, ofs=0, size=4> >>> print(POINT.y) <Field type=c_long, ofs=4, size=4> >>>
Предупреждение
ctypes не поддерживает передачу объединений или структур с битами-полями в функции по значению. Хотя это может работать на 32-битном x86, это не гарантируется библиотекой в общем случае. Объединения и структуры с битами-полями всегда должны передаваться в функции по указателю.
Выравнивание и порядок байтов структур/объединений
По умолчанию поля структур и объединений выравниваются так же, как это делает C-компилятор. Можно переопределить это поведение, указав атрибут _pack_ в определении подкласса. Он должен быть положительным целым числом и определяет максимальное выравнивание для полей. Это также делает #pragma pack(n) в MSVC.
ctypes использует родной порядок байтов для структур и объединений. Для создания структур с неродным порядком байтов вы можете использовать один из базовых классов BigEndianStructure, LittleEndianStructure, BigEndianUnion, и LittleEndianUnion. Эти классы не могут содержать поля указателей.
Битовые поля в структурах и объединениях
Можно создавать структуры и объединения, содержащие битовые поля. Битовые поля возможны только для целочисленных полей; ширина бита указывается как третий элемент в кортежах _fields_:
>>> class Int(Structure):
... _fields_ = [("first_16", c_int, 16),
... ("second_16", c_int, 16)]
...
>>> print(Int.first_16)
<Field type=c_long, ofs=0:0, bits=16>
>>> print(Int.second_16)
<Field type=c_long, ofs=0:16, bits=16>
>>>
Массивы
Массивы являются последовательностями, содержащими фиксированное количество экземпляров одного и того же типа.
Рекомендуемый способ создания типов массивов — умножить тип данных на положительное целое число:
TenPointsArrayType = POINT * 10
Вот пример несколько искусственного типа данных — структуры, содержащей 4 POINT среди прочего:
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = ("x", c_int), ("y", c_int)
...
>>> class MyStruct(Structure):
... _fields_ = [("a", c_int),
... ("b", c_float),
... ("point_array", POINT * 4)]
>>>
>>> print(len(MyStruct().point_array))
4
>>>
Экземпляры создаются обычным способом — вызовом класса:
arr = TenPointsArrayType()
for pt in arr:
print(pt.x, pt.y)
Вышеприведённый код напечатает серию 0 0 строк, так как содержимое массива инициализируется нулями.
Можно также указать инициализаторы правильного типа:
>>> from ctypes import * >>> TenIntegers = c_int * 10 >>> ii = TenIntegers(1, 2, 3, 4, 5, 6, 7, 8, 9, 10) >>> print(ii) <c_long_Array_10 object at 0x...> >>> for i in ii: print(i, end=" ") ... 1 2 3 4 5 6 7 8 9 10 >>>
Указатели
Экземпляры указателей создаются вызовом функции pointer() для типа ctypes:
>>> from ctypes import * >>> i = c_int(42) >>> pi = pointer(i) >>>
Экземпляры указателей имеют атрибут contents, который возвращает объект, на который указывает указатель, объект i выше:
>>> pi.contents c_long(42) >>>
Обратите внимание, что ctypes не имеет OOR (возврат исходного объекта), каждый раз, когда вы получаете атрибут, он строит новый, эквивалентный объект:
>>> pi.contents is i False >>> pi.contents is pi.contents False >>>
Присвоение другого экземпляра c_int атрибуту содержимого указателя заставило бы указатель указывать на местоположение памяти, где это хранится:
>>> i = c_int(99) >>> pi.contents = i >>> pi.contents c_long(99) >>>
Экземпляры указателей также можно индексировать целыми числами:
>>> pi[0] 99 >>>
Присвоение целочисленного индекса изменяет значение, на которое указывает указатель:
>>> print(i) c_long(99) >>> pi[0] = 22 >>> print(i) c_long(22) >>>
Можно также использовать индексы, отличные от 0, но вы должны понимать, что делаете, точно так же, как в C: вы можете получить доступ к произвольным расположениям памяти или изменить их. Как правило, вы используете эту функцию только в том случае, если получаете указатель от C-функции и знаете, что указатель фактически указывает на массив, а не на один элемент.
За кулисами функция pointer() делает больше, чем просто создаёт экземпляры указателей; ей сначала нужно создать типы указателей. Это делается с помощью функции POINTER(), которая принимает любой тип ctypes и возвращает новый тип:
>>> PI = POINTER(c_int) >>> PI <class 'ctypes.LP_c_long'> >>> PI(42) Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: expected c_long instead of int >>> PI(c_int(42)) <ctypes.LP_c_long object at 0x...> >>>
Вызов типа указателя без аргумента создаёт NULL указатель. NULL указатели имеют значение False булевого типа:
>>> null_ptr = POINTER(c_int)() >>> print(bool(null_ptr)) False >>>
ctypes проверяет NULL при разыменовании указателей (но разыменование недействительных, не-NULL указателей приведёт к сбою Python):
>>> null_ptr[0]
Traceback (most recent call last):
....
ValueError: NULL pointer access
>>>
>>> null_ptr[0] = 1234
Traceback (most recent call last):
....
ValueError: NULL pointer access
>>>
Преобразования типов
Обычно ctypes выполняет строгий контроль типов. Это означает, что если у вас POINTER(c_int) в списке argtypes функции или в качестве типа поля-члена в определении структуры, принимаются только экземпляры ровно того же типа. Существуют некоторые исключения из этого правила, когда ctypes принимает другие объекты. Например, вы можете передавать совместимые массивы вместо типов указателей. Итак, для POINTER(c_int), ctypes принимает массив c_int:
>>> class Bar(Structure):
... _fields_ = [("count", c_int), ("values", POINTER(c_int))]
...
>>> bar = Bar()
>>> bar.values = (c_int * 3)(1, 2, 3)
>>> bar.count = 3
>>> for i in range(bar.count):
... print(bar.values[i])
...
1
2
3
>>>
Кроме того, если аргумент функции явно объявлен как тип указателя (например, POINTER(c_int)) в argtypes, объект указанного типа (c_int в данном случае) может быть передан в функцию. ctypes автоматически применит необходимое byref() преобразование в этом случае.
Чтобы установить поле типа POINTER в NULL, можно присвоить None:
>>> bar.values = None >>>
Иногда у вас есть экземпляры несовместимых типов. В C можно привести один тип к другому типу. ctypes предоставляет функцию cast(), которую можно использовать аналогичным образом. Структура Bar , определённая выше, принимает указатели на POINTER(c_int) или массивы c_int для своего поля values, но не экземпляры других типов:
>>> bar.values = (c_byte * 4)() Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: incompatible types, c_byte_Array_4 instance instead of LP_c_long instance >>>
Для таких случаев удобна функция cast().
Функция cast() может быть использована для приведения экземпляра ctypes к указателю на другой тип данных ctypes. cast() принимает два параметра: объект ctypes, который может быть преобразован в указатель какого-либо типа, и тип указателя ctypes. Она возвращает экземпляр второго аргумента, который ссылается на тот же блок памяти, что и первый аргумент:
>>> a = (c_byte * 4)() >>> cast(a, POINTER(c_int)) <ctypes.LP_c_long object at ...> >>>
Таким образом, cast() может использоваться для присвоения полю values структуры Bar:
>>> bar = Bar() >>> bar.values = cast((c_byte * 4)(), POINTER(c_int)) >>> print(bar.values[0]) 0 >>>
Неполные типы
Неполные типы — это структуры, объединения или массивы, члены которых ещё не указаны. В C они задаются с помощью предварительных объявлений, которые определяются позже:
struct cell; /* forward declaration */
struct cell {
char *name;
struct cell *next;
};
Прямое преобразование в код ctypes будет таким, но оно не работает:
>>> class cell(Structure):
... _fields_ = [("name", c_char_p),
... ("next", POINTER(cell))]
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in cell
NameError: name 'cell' is not defined
>>>
потому что новый class cell недоступен в самом операторе класса. В ctypes мы можем определить класс cell и установить атрибут _fields_ позже, после оператора класса:
>>> from ctypes import *
>>> class cell(Structure):
... pass
...
>>> cell._fields_ = [("name", c_char_p),
... ("next", POINTER(cell))]
>>>
Давайте попробуем. Мы создаём два экземпляра cell, и они указывают друг на друга, а затем следуем цепочке указателей несколько раз:
>>> c1 = cell() >>> c1.name = b"foo" >>> c2 = cell() >>> c2.name = b"bar" >>> c1.next = pointer(c2) >>> c2.next = pointer(c1) >>> p = c1 >>> for i in range(8): ... print(p.name, end=" ") ... p = p.next[0] ... foo bar foo bar foo bar foo bar >>>
Функции обратного вызова
ctypes позволяет создавать указатели на функции, вызываемые из C, из вызываемых функций Python. Иногда их называют функциями обратного вызова.
Сначала необходимо создать класс для функции обратного вызова. Класс знает соглашение о вызове, тип возвращаемого значения и количество и типы аргументов, которые получит эта функция.
Функция-фабрика CFUNCTYPE() создаёт типы для функций обратного вызова, используя соглашение о вызове cdecl. В Windows функция-фабрика WINFUNCTYPE() создаёт типы для функций обратного вызова, используя соглашение о вызове stdcall.
Обе эти функции-фабрики вызываются с типом результата в качестве первого аргумента и типами ожидаемых аргументов функций обратного вызова в качестве оставшихся аргументов.
Я приведу здесь пример, который использует стандартную функцию C-библиотеки qsort(), которая используется для сортировки элементов с помощью функции обратного вызова. qsort() будет использоваться для сортировки массива целых чисел:
>>> IntArray5 = c_int * 5 >>> ia = IntArray5(5, 1, 7, 33, 99) >>> qsort = libc.qsort >>> qsort.restype = None >>>
qsort() должна вызываться с указателем на данные для сортировки, количеством элементов в массиве данных, размером одного элемента и указателем на функцию сравнения — функцию обратного вызова. Затем функция обратного вызова будет вызываться с двумя указателями на элементы, и она должна возвращать целое отрицательное число, если первый элемент меньше второго, ноль, если они равны, и целое положительное число в противном случае.
Итак, наша функция обратного вызова получает указатели на целые числа и должна возвращать целое число. Сначала мы создаём type для функции обратного вызова:
>>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int)) >>>
Для начала вот простая функция обратного вызова, которая показывает значения, которые она получает:
>>> def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return 0
...
>>> cmp_func = CMPFUNC(py_cmp_func)
>>>
Результат:
>>> qsort(ia, len(ia), sizeof(c_int), cmp_func) py_cmp_func 5 1 py_cmp_func 33 99 py_cmp_func 7 33 py_cmp_func 5 7 py_cmp_func 1 7 >>>
Теперь мы можем фактически сравнить два элемента и вернуть полезный результат:
>>> def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return a[0] - b[0]
...
>>>
>>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func))
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>
Как мы можем легко проверить, наш массив теперь отсортирован:
>>> for i in ia: print(i, end=" ") ... 1 5 7 33 99 >>>
Функции-фабрики могут использоваться как фабрики декораторов, поэтому мы можем записать:
>>> @CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
... def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return a[0] - b[0]
...
>>> qsort(ia, len(ia), sizeof(c_int), py_cmp_func)
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>
Примечание
Убедитесь, что вы сохраняете ссылки на объекты CFUNCTYPE() до тех пор, пока они используются из кода C. ctypes этого не делает, и если вы этого не сделаете, они могут быть удалены сборщиком мусора, что приведёт к ошибке вашей программы при вызове функции обратного вызова.
Также обратите внимание, что если функция обратного вызова вызывается в потоке, созданном вне управления Python (например, внешним кодом, вызывающим функцию обратного вызова), ctypes создаёт новый виртуальный поток Python при каждом вызове. Это поведение верно для большинства целей, но это означает, что значения, сохранённые с помощью threading.local, не сохранятся при разных вызовах обратного вызова, даже если эти вызовы выполняются из одного и того же потока C.
Доступ к значениям, экспортированным из dll
Некоторые общие библиотеки не только экспортируют функции, но и экспортируют переменные. Пример в самой библиотеке Python — это Py_OptimizeFlag, целое число, установленное в 0, 1 или 2 в зависимости от флага -O или -OO, заданных при запуске.
ctypes может получить доступ к таким значениям с помощью методов класса in_dll() типа. pythonapi — это предопределённый символ, обеспечивающий доступ к C-API Python:
>>> opt_flag = c_int.in_dll(pythonapi, "Py_OptimizeFlag") >>> print(opt_flag) c_long(0) >>>
Если интерпретатор был бы запущен с -O, пример выводил бы c_long(1), или c_long(2) если был бы указан -OO.
Расширенный пример, который также демонстрирует использование указателей, получает доступ к указателю PyImport_FrozenModules, экспортированному Python.
Цитата из документации по этому значению:
Этот указатель инициализируется для указания на массив записей _frozen, завершённый записью, члены которой все равны NULL или нулю. При импорте замороженного модуля он ищется в этой таблице. Код сторонних разработчиков мог бы использовать эту возможность для предоставления динамически созданного набора замороженных модулей.
Таким образом, работа с этим указателем может оказаться полезной. Чтобы ограничить размер примера, мы показываем только то, как эту таблицу можно прочитать с помощью ctypes:
>>> from ctypes import *
>>>
>>> class struct_frozen(Structure):
... _fields_ = [("name", c_char_p),
... ("code", POINTER(c_ubyte)),
... ("size", c_int)]
...
>>>
Мы определили тип данных _frozen, поэтому мы можем получить указатель на таблицу:
>>> FrozenTable = POINTER(struct_frozen) >>> table = FrozenTable.in_dll(pythonapi, "PyImport_FrozenModules") >>>
Поскольку table является указателем на массив записей struct_frozen, мы можем перебирать его, но нам просто нужно убедиться, что наш цикл завершается, потому что у указателей нет размера. Рано или поздно это, вероятно, приведёт к ошибке доступа или чему-то подобному, поэтому лучше выйти из цикла, когда мы встретим запись NULL:
>>> for item in table:
... if item.name is None:
... break
... print(item.name.decode("ascii"), item.size)
...
_frozen_importlib 31764
_frozen_importlib_external 41499
__hello__ 161
__phello__ -161
__phello__.spam 161
>>>
Тот факт, что стандартный Python имеет замороженный модуль и замороченный пакет (указанный отрицательным членом size ), не широко известен; он используется только для тестирования. Попробуйте с import __hello__ , например.
Неожиданные моменты
В ctypes есть некоторые нюансы, где вы можете ожидать чего-то другого, чем то, что фактически происходит.
Рассмотрим следующий пример:
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = ("x", c_int), ("y", c_int)
...
>>> class RECT(Structure):
... _fields_ = ("a", POINT), ("b", POINT)
...
>>> p1 = POINT(1, 2)
>>> p2 = POINT(3, 4)
>>> rc = RECT(p1, p2)
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
1 2 3 4
>>> # now swap the two points
>>> rc.a, rc.b = rc.b, rc.a
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
3 4 3 4
>>>
Хм. Мы, безусловно, ожидали, что последняя инструкция выведет 3 4 1 2. Что случилось? Вот шаги выполнения строки rc.a, rc.b = rc.b, rc.a выше:
>>> temp0, temp1 = rc.b, rc.a >>> rc.a = temp0 >>> rc.b = temp1 >>>
Обратите внимание, что temp0 и temp1 — это объекты, которые по-прежнему используют внутренний буфер объекта rc выше. Поэтому выполнение rc.a = temp0 копирует содержимое буфера temp0 в буфер rc. Это, в свою очередь, изменяет содержимое temp1. Таким образом, последняя присваивающая операция rc.b = temp1, не имеет ожидаемого эффекта.
Помните, что извлечение подобъектов из структур, объединений и массивов не копирует подобъект, вместо этого он извлекает оберточный объект, имеющий доступ к базовому буферу родительского объекта.
Другой пример, который может вести себя иначе, чем ожидается, это:
>>> s = c_char_p() >>> s.value = b"abc def ghi" >>> s.value b'abc def ghi' >>> s.value is s.value False >>>
Примечание
Объекты, созданные из c_char_p, могут иметь своё значение, установленное только в байты или целые числа.
Почему он выводит False? Экземпляры ctypes — это объекты, содержащие блок памяти плюс некоторые дескрипторы, обеспечивающие доступ к содержимому памяти. Сохранение объекта Python в блоке памяти не сохраняет сам объект, вместо этого сохраняется contents объекта. Каждый раз при доступе к содержимому создаётся новый объект Python!
Типы данных переменной длины
ctypes предоставляет некоторую поддержку переменных массивов и структур.
Функция resize() может использоваться для изменения размера буфера памяти существующего объекта ctypes. Функция принимает объект в качестве первого аргумента и запрашиваемый размер в байтах в качестве второго. Блок памяти не может быть меньше естественного блока памяти, определенного типом объекта; если это пытаются сделать, возникает исключение ValueError:
>>> short_array = (c_short * 4)()
>>> print(sizeof(short_array))
8
>>> resize(short_array, 4)
Traceback (most recent call last):
...
ValueError: minimum size is 8
>>> resize(short_array, 32)
>>> sizeof(short_array)
32
>>> sizeof(type(short_array))
8
>>>
Это неплохо, но как получить доступ к дополнительным элементам, содержащимся в этом массиве? Поскольку тип всё ещё знает только о 4 элементах, при попытке доступа к другим элементам возникают ошибки:
>>> short_array[:]
[0, 0, 0, 0]
>>> short_array[7]
Traceback (most recent call last):
...
IndexError: invalid index
>>>
Другой способ использования типов данных переменной длины с ctypes заключается в использовании динамической природы Python и (пере)определении типа данных после того, как требуемый размер уже известен, в каждом конкретном случае.
Справочник по ctypes
Внешние функции
Как объяснялось в предыдущем разделе, к внешним функциям можно получить доступ как к атрибутам загруженных динамических библиотек. Объекты функций, созданные таким образом, по умолчанию принимают любое количество аргументов, принимают любые экземпляры данных ctypes в качестве аргументов и возвращают тип результата по умолчанию, указанный загрузчиком библиотеки. Они являются экземплярами частного класса:
-
class ctypes._FuncPtr -
Базовый класс для вызываемых C внешних функций.
Экземпляры внешних функций также являются совместимыми с C типами данных; они представляют указатели на C функции.
Это поведение можно настроить, назначив значения специальным атрибутам объекта внешней функции.
-
restype -
Назначьте тип ctypes для указания типа результата внешней функции. Используйте
Noneдляvoid, функции, не возвращающей ничего.Возможна привязка вызываемого объекта Python, который не является типом ctypes. В этом случае функция предполагается, что возвращает целое число C
int, и вызываемый объект будет вызван с этим целым числом, что позволит дополнительно обрабатывать результат или проверять ошибки. Использование этого метода устарело. Для более гибкой обработки результата или проверки ошибок используйте тип данных ctypes в качествеrestypeи назначьте вызываемый объект атрибутуerrcheck.
-
argtypes -
Назначьте кортеж типов ctypes для указания типов аргументов, которые принимает функция. Функции, использующие соглашение о вызове
stdcall, могут вызываться только с тем же количеством аргументов, что и длина этого кортежа; функции, использующие соглашение о вызове C, принимают дополнительные, не указанные аргументы.При вызове внешней функции каждый фактический аргумент передается методу
from_param()элементов в кортежеargtypes. Этот метод позволяет адаптировать фактический аргумент к объекту, который принимает внешняя функция. Например, элементc_char_pв кортежеargtypesпреобразует строку, переданную в качестве аргумента, в объект bytes с использованием правил преобразования ctypes.Новое: теперь можно помещать в argtypes элементы, которые не являются типами ctypes, но каждый элемент должен иметь метод
from_param(), который возвращает значение, используемое в качестве аргумента (целое число, строка, экземпляр ctypes). Это позволяет определять адаптеры, которые могут адаптировать пользовательские объекты в качестве параметров функций.
-
errcheck -
Назначьте Python-функцию или другой вызываемый объект этому атрибуту. Вызываемый объект будет вызван с тремя или более аргументами:
-
callable(result, func, arguments) -
result — то, что возвращает внешняя функция, как указано атрибутом
restype.func — сам объект внешней функции. Это позволяет повторно использовать тот же вызываемый объект для проверки или дальнейшей обработки результатов нескольких функций.
arguments — кортеж, содержащий параметры, изначально переданные в вызов функции. Это позволяет специализировать поведение в зависимости от используемых аргументов.
Объект, возвращаемый этой функцией, будет возвращен вызовом внешней функции, но он также может проверять значение результата и генерировать исключение, если вызов внешней функции завершился неудачно.
-
-
-
exception ctypes.ArgumentError -
Это исключение генерируется, когда вызов внешней функции не может преобразовать один из переданных аргументов.
В Windows, когда вызов внешней функции генерирует системное исключение (например, из-за нарушения доступа), оно будет перехвачено и заменено соответствующим исключением Python. Кроме того, будет возбуждено событие аудита ctypes.seh_exception с аргументом code, что позволит крючку аудита заменить исключение своим собственным.
Некоторые способы вызова внешних функций могут вызвать событие аудита ctypes.call_function с аргументами function pointer и arguments.
Прототипы функций
Внешние функции также можно создавать, создавая экземпляры прототипов функций. Прототипы функций аналогичны прототипам функций в C; они описывают функцию (тип возвращаемого значения, типы аргументов, соглашение о вызове), не определяя реализацию. Функции-фабрики должны вызываться с желаемым типом результата и типами аргументов функции и могут использоваться как фабрики декораторов, а также применяться к функциям через синтаксис @wrapper. См. Функции обратного вызова для примеров.
-
ctypes.CFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False) -
Возвращаемый прототип функции создает функции, которые используют стандартное соглашение о вызове C. Функция высвободит GIL во время вызова. Если use_errno установлено в значение true, внутренняя копия ctypes системной переменной
errnoменяется на реальное значениеerrnoдо и после вызова; use_last_error делает то же самое для кода ошибки Windows.
-
ctypes.WINFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False) -
Только Windows: Возвращаемый прототип функции создает функции, которые используют соглашение о вызове
stdcall. Функция высвободит GIL во время вызова. use_errno и use_last_error имеют то же значение, что и выше.
-
ctypes.PYFUNCTYPE(restype, *argtypes) -
Возвращаемый прототип функции создает функции, которые используют соглашение о вызове Python. Функция не высвободит GIL во время вызова.
Прототипы функций, созданные этими функциями-фабриками, можно создать различными способами, в зависимости от типа и количества параметров в вызове:
-
prototype(address) -
Возвращает внешнюю функцию по указанному адресу, который должен быть целым числом.
-
prototype(callable) -
Создает вызываемую функцией C (функцию обратного вызова) из Python-вызываемого объекта.
-
prototype(func_spec[, paramflags]) -
Возвращает внешнюю функцию, экспортированную из библиотеки общего использования. func_spec должен быть 2-кортежем
(name_or_ordinal, library). Первый элемент — это имя экспортированной функции в виде строки или порядковый номер экспортированной функции в виде целого числа. Второй элемент — экземпляр библиотеки общего использования.
-
prototype(vtbl_index, name[, paramflags[, iid]]) -
Возвращает внешнюю функцию, которая вызовет COM-метод. vtbl_index — это индекс в таблице виртуальных функций, малое неотрицательное целое число. name — имя COM-метода. iid — необязательный указатель на идентификатор интерфейса, используемый в расширенном сообщении об ошибках.
COM-методы используют специальное соглашение о вызове: они требуют указателя на COM-интерфейс в качестве первого аргумента, помимо параметров, указанных в кортеже
argtypes.
Необязательный параметр paramflags создает оболочки внешних функций с гораздо большим функционалом, чем описанные выше функции.
paramflags должен быть кортежем той же длины, что и argtypes.
Каждый элемент этого кортежа содержит дополнительную информацию об аргументе, он должен быть кортежем, содержащим один, два или три элемента.
Первый элемент — целое число, содержащее комбинацию флагов направления для параметра:
- 1
-
Указывает входной параметр функции.
- 2
-
Параметр вывода. Внешняя функция заполняет значение.
- 4
-
Входной параметр, который по умолчанию равен целому числу ноль.
Необязательный второй элемент — имя параметра в виде строки. Если он указан, внешняя функция может быть вызвана с именованными параметрами.
Необязательный третий элемент — значение по умолчанию для этого параметра.
В этом примере показано, как обернуть функцию Windows MessageBoxW так, чтобы она поддерживала параметры по умолчанию и именованные аргументы. C-объявление из заголовочного файла windows такое:
WINUSERAPI int WINAPI
MessageBoxW(
HWND hWnd,
LPCWSTR lpText,
LPCWSTR lpCaption,
UINT uType);
Вот обертка с помощью ctypes:
>>> from ctypes import c_int, WINFUNCTYPE, windll
>>> from ctypes.wintypes import HWND, LPCWSTR, UINT
>>> prototype = WINFUNCTYPE(c_int, HWND, LPCWSTR, LPCWSTR, UINT)
>>> paramflags = (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", "Hello from ctypes"), (1, "flags", 0)
>>> MessageBox = prototype(("MessageBoxW", windll.user32), paramflags)
Теперь внешнюю функцию MessageBox можно вызвать следующими способами:
>>> MessageBox() >>> MessageBox(text="Spam, spam, spam") >>> MessageBox(flags=2, text="foo bar")
Второй пример демонстрирует параметры вывода. Функция win32 GetWindowRect извлекает размеры указанного окна, копируя их в структуру RECT , которую должен предоставить вызывающий. Вот C-объявление:
WINUSERAPI BOOL WINAPI
GetWindowRect(
HWND hWnd,
LPRECT lpRect);
Вот обертка с помощью ctypes:
>>> from ctypes import POINTER, WINFUNCTYPE, windll, WinError
>>> from ctypes.wintypes import BOOL, HWND, RECT
>>> prototype = WINFUNCTYPE(BOOL, HWND, POINTER(RECT))
>>> paramflags = (1, "hwnd"), (2, "lprect")
>>> GetWindowRect = prototype(("GetWindowRect", windll.user32), paramflags)
>>>
Функции с параметрами вывода автоматически возвращают значение параметра вывода, если он один, или кортеж, содержащий значения параметров вывода, если их несколько, поэтому функция GetWindowRect теперь возвращает экземпляр RECT при вызове.
Параметры вывода можно комбинировать с протоколом errcheck для дальнейшей обработки вывода и проверки ошибок. Функция win32 GetWindowRect возвращает BOOL для сигнализации об успехе или ошибке, поэтому эта функция может выполнить проверку ошибок и поднять исключение при неудаче вызова API:
>>> def errcheck(result, func, args): ... if not result: ... raise WinError() ... return args ... >>> GetWindowRect.errcheck = errcheck >>>
Если функция errcheck возвращает полученный кортеж аргументов без изменений, ctypes продолжает обычную обработку параметров вывода. Если вы хотите вернуть кортеж координат окна вместо экземпляра RECT, вы можете извлечь поля в функции и вернуть их вместо этого, обычная обработка больше не будет выполняться:
>>> def errcheck(result, func, args): ... if not result: ... raise WinError() ... rc = args[1] ... return rc.left, rc.top, rc.bottom, rc.right ... >>> GetWindowRect.errcheck = errcheck >>>
Функции для работы с типами
-
ctypes.addressof(obj) -
Возвращает адрес буфера памяти в виде целого числа. obj должен быть экземпляром типа ctypes.
Вызывает событие аудита аудита
ctypes.addressofсо значением аргументаobj.
-
ctypes.alignment(obj_or_type) -
Возвращает требования к выравниванию для типа ctypes. obj_or_type должен быть типом или экземпляром ctypes.
-
ctypes.byref(obj[, offset]) -
Возвращает лёгкий указатель на obj, который должен быть экземпляром типа ctypes. offset по умолчанию равен нулю и должен быть целым числом, которое будет добавлено к внутреннему значению указателя.
byref(obj, offset)соответствует следующему коду C:(((char *)&obj) + offset)
Возвращаемый объект может использоваться только как параметр вызова внешней функции. Он ведет себя подобно
pointer(obj), но создание происходит намного быстрее.
-
ctypes.cast(obj, type) -
Эта функция аналогична оператору приведения типов в C. Она возвращает новый экземпляр type, который указывает на тот же блок памяти, что и obj. type должен быть указателем, а obj — объектом, который можно интерпретировать как указатель.
-
ctypes.create_string_buffer(init_or_size, size=None) -
Эта функция создаёт изменяемый буфер символов. Возвращаемый объект — массив ctypes типа
c_char.init_or_size должно быть целым числом, определяющим размер массива, или объектом bytes, который будет использован для инициализации элементов массива.
Если в качестве первого аргумента указан объект bytes, буфер увеличивается на один элемент, чтобы последний элемент массива был символом NUL. В качестве второго аргумента можно передать целое число, позволяющее указать размер массива, если длина объекта bytes не должна использоваться.
Вызывает событие аудита аудита
ctypes.create_string_bufferс аргументамиinit,size.
-
ctypes.create_unicode_buffer(init_or_size, size=None) -
Эта функция создаёт изменяемый буфер символов Юникода. Возвращаемый объект — массив ctypes типа
c_wchar.init_or_size должно быть целым числом, определяющим размер массива, или строкой, которая будет использоваться для инициализации элементов массива.
Если в качестве первого аргумента указана строка, буфер увеличивается на один элемент, чтобы последний элемент массива был символом NUL. В качестве второго аргумента можно передать целое число, позволяющее указать размер массива, если длина строки не должна использоваться.
Вызывает событие аудита аудита
ctypes.create_unicode_bufferс аргументамиinit,size.
-
ctypes.DllCanUnloadNow() -
Только для Windows: Эта функция — обработчик, позволяющий реализовать COM-серверы в процессе с помощью ctypes. Она вызывается функцией DllCanUnloadNow, которую экспортирует dll расширения _ctypes.
-
ctypes.DllGetClassObject() -
Только для Windows: Эта функция — обработчик, позволяющий реализовать COM-серверы в процессе с помощью ctypes. Она вызывается функцией DllGetClassObject, которую экспортирует dll расширения
_ctypes.
-
ctypes.util.find_library(name) -
Попытаться найти библиотеку и вернуть путь к ней. name — имя библиотеки без каких-либо префиксов, например,
lib, суффиксов, например,.so,.dylib, или номера версии (такой вид используется для параметра POSIX-линкера-l). Если библиотека не найдена, возвращаетсяNone.Точное поведение зависит от системы.
-
ctypes.util.find_msvcrt() -
Только для Windows: возвращает имя файла библиотеки VC runtime, используемой Python и модулями расширения. Если имя библиотеки определить невозможно, возвращается
None.Если вам нужно освободить память, например, выделенную модулем расширения с помощью вызова
free(void *), важно, чтобы вы использовали функцию из той же библиотеки, которая выделила память.
-
ctypes.FormatError([code]) -
Только для Windows: Возвращает текстовое описание кода ошибки code. Если код ошибки не указан, используется последний код ошибки, полученный вызовом функции Windows GetLastError.
-
ctypes.GetLastError() -
Только для Windows: Возвращает последний код ошибки, установленный Windows в потоке вызова. Эта функция вызывает функцию Windows
GetLastError()напрямую, она не возвращает копию кода ошибки, хранящуюся в библиотеке ctypes.
-
ctypes.get_errno() -
Возвращает текущее значение копии переменной системы ctypes
errnoв потоке вызова.Вызывает событие аудита
ctypes.get_errnoбез аргументов.
-
ctypes.get_last_error() -
Только для Windows: возвращает текущее значение копии переменной системы ctypes
LastErrorв потоке вызова.Вызывает событие аудита
ctypes.get_last_errorбез аргументов.
-
ctypes.memmove(dst, src, count) -
Аналогична стандартной C-функции memmove: копирует count байтов из src в dst. dst и src должны быть целыми числами или экземплярами ctypes, которые можно преобразовать в указатели.
-
ctypes.memset(dst, c, count) -
Аналогична стандартной C-функции memset: заполняет блок памяти по адресу dst значением 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) -
Только для Windows: устанавливает текущее значение копии переменной системы ctypes
LastErrorв потоке вызова на value и возвращает предыдущее значение.Вызывает событие аудита
ctypes.set_last_errorс аргументомerror.
-
ctypes.sizeof(obj_or_type) -
Возвращает размер буфера памяти типа ctypes или экземпляра в байтах. Делает то же, что и оператор C
sizeof.
-
ctypes.string_at(address, size=- 1) -
Эта функция возвращает строку C, начинающуюся с адреса памяти address, в виде объекта bytes. Если указан размер, он используется в качестве размера, иначе строка предполагается завершающейся нулём.
Вызывает событие аудита
ctypes.string_atс аргументамиaddress,size.
-
ctypes.WinError(code=None, descr=None) -
Только для Windows: эта функция, вероятно, имеет самое плохое название в ctypes. Она создаёт экземпляр OSError. Если code не указан, вызывается
GetLastErrorдля определения кода ошибки. Если descr не указан, вызываетсяFormatError()для получения текстового описания ошибки.Изменено в версии 3.3: Создавался экземпляр
WindowsError.
-
ctypes.wstring_at(address, size=- 1) -
Эта функция возвращает строку широких символов, начинающуюся с адреса памяти address, в виде строки. Если указан size, он используется как количество символов строки, в противном случае строка предполагается нуль-терминированной.
Вызывает событие аудита аудита
ctypes.wstring_atс аргументамиaddress,size.
Типы данных
-
class ctypes._CData -
Этот непубличный класс является общим базовым классом всех типов данных ctypes. Среди прочего, все экземпляры типов ctypes содержат блок памяти, который хранит данные, совместимые с C; адрес блока памяти возвращается функцией-помощником
addressof(). Другая переменная экземпляра экспортируется как_objects; она содержит другие объекты Python, которые необходимо поддерживать активными в случае, если блок памяти содержит указатели.Общие методы типов данных ctypes, все это методы класса (точнее, методы метакласса метакласса):
-
from_buffer(source[, offset]) -
Этот метод возвращает экземпляр ctypes, который разделяет буфер объекта source. Объект source должен поддерживать интерфейс записи в буфер. Необязательный параметр offset указывает смещение в буфере источника в байтах; по умолчанию он равен нулю. Если буфер источника недостаточно велик, возникает ошибка
ValueError.Вызывает событие аудита аудита
ctypes.cdata/bufferс аргументамиpointer,size,offset.
-
from_buffer_copy(source[, offset]) -
Этот метод создаёт экземпляр ctypes, копируя буфер из буфера объекта source, который должен быть читаемым. Необязательный параметр offset указывает смещение в буфере источника в байтах; по умолчанию он равен нулю. Если буфер источника недостаточно велик, возникает ошибка
ValueError.Вызывает событие аудита аудита
ctypes.cdata/bufferс аргументамиpointer,size,offset.
-
from_address(address) -
Этот метод возвращает экземпляр типа ctypes, используя память, указанную параметром address, который должен быть целым числом.
Этот метод и другие методы, косвенно вызывающие этот метод, вызывают событие аудита аудита
ctypes.cdataс аргументомaddress.
-
from_param(obj) -
Этот метод адаптирует obj к типу ctypes. Он вызывается с фактическим объектом, используемым в вызове внешней функции, когда тип присутствует в кортеже
argtypesвнешней функции; он должен вернуть объект, который может использоваться в качестве параметра вызова функции.Все типы данных ctypes имеют по умолчанию реализацию этого метода класса, которая обычно возвращает obj, если это экземпляр типа. Некоторые типы также принимают другие объекты.
-
in_dll(library, name) -
Этот метод возвращает экземпляр типа ctypes, экспортированный из динамической библиотеки. name — имя символа, экспортирующего данные, library — загруженная динамическая библиотека.
Общие переменные экземпляров типов данных ctypes:
-
_b_base_ -
Иногда экземпляры данных ctypes не владеют блоком памяти, который они содержат, вместо этого они используют часть блока памяти базового объекта. Член только для чтения
_b_base_— это корневой объект ctypes, который владеет блоком памяти.
-
_b_needsfree_ -
Эта переменная только для чтения равна true, если экземпляр данных ctypes сам выделил блок памяти, в противном случае — false.
-
_objects -
Этот член либо
None, либо словарь, содержащий объекты Python, которые необходимо поддерживать активными, чтобы содержимое блока памяти оставалось действительным. Этот объект предоставляется только для отладки; никогда не изменяйте содержимое этого словаря.
-
Основные типы данных
-
class ctypes._SimpleCData -
Этот непубличный класс является базовым классом всех основных типов данных ctypes. Он упоминается здесь, потому что содержит общие атрибуты основных типов данных ctypes.
_SimpleCDataявляется подклассом_CData, поэтому наследует их методы и атрибуты. Типы данных ctypes, которые не являются и не содержат указателей, теперь можно сериализовать.Экземпляры имеют один атрибут:
-
value -
Этот атрибут содержит фактическое значение экземпляра. Для целочисленных и указательских типов это целое число, для символьных типов — объект байтов или строка, содержащая один символ, для указателей на символы — объект байтов или строка Python.
При получении атрибута
valueиз экземпляра ctypes обычно каждый раз возвращается новый объект.ctypesне реализует возврат исходного объекта, всегда создается новый объект. То же самое справедливо для всех других экземпляров объектов ctypes.
-
Основные типы данных, возвращаемые в качестве результатов вызова внешних функций или, например, при получении членов структур или элементов массивов, прозрачно преобразуются в базовые типы Python. Другими словами, если внешняя функция имеет тип restype типа c_char_p, вы всегда получите объект байтов Python, а не экземпляр c_char_p.
Подклассы основных типов данных не наследуют это поведение. Таким образом, если внешняя функция restype является подклассом c_void_p, вы получите экземпляр этого подкласса из вызова функции. Конечно, вы можете получить значение указателя, обратившись к атрибуту value.
Вот основные типы данных ctypes:
-
class ctypes.c_byte -
Представляет тип данных C
signed char, и интерпретирует значение как малое целое число. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_char -
Представляет тип данных C
char, и интерпретирует значение как один символ. Конструктор принимает необязательный строковый инициализатор; длина строки должна быть ровно один символ.
-
class ctypes.c_char_p -
Представляет тип данных C
char*, когда он указывает на нуль-терминированную строку. Для общего указателя на символ, который также может указывать на двоичные данные, необходимо использоватьPOINTER(c_char). Конструктор принимает целочисленный адрес или объект байтов.
-
class ctypes.c_double -
Представляет тип данных C
double. Конструктор принимает необязательный инициализатор с плавающей запятой.
-
class ctypes.c_longdouble -
Представляет тип данных C
long double. Конструктор принимает необязательный инициализатор с плавающей запятой. На платформах, гдеsizeof(long double) == sizeof(double), он является алиасомc_double.
-
class ctypes.c_float -
Представляет тип данных C
float. Конструктор принимает необязательный инициализатор с плавающей запятой.
-
class ctypes.c_int -
Представляет тип данных C
signed int. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется. На платформах, гдеsizeof(int) == sizeof(long), он является алиасомc_long.
-
class ctypes.c_int8 -
Представляет тип данных C 8-битный
signed int. Обычно является алиасомc_byte.
-
class ctypes.c_int16 -
Представляет тип данных C 16-битный
signed int. Обычно является алиасомc_short.
-
class ctypes.c_int32 -
Представляет тип данных C 32-битный
signed int. Обычно является алиасомc_int.
-
class ctypes.c_int64 -
Представляет тип данных C 64-битный
signed int. Обычно является алиасомc_longlong.
-
class ctypes.c_long -
Представляет тип данных C
signed long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_longlong -
Представляет тип данных C
signed long long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_short -
Представляет тип данных C
signed short. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_size_t -
Представляет тип данных C
size_t.
-
class ctypes.c_ssize_t -
Представляет тип данных C
ssize_t.Введено в версии 3.2.
-
class ctypes.c_ubyte -
Представляет тип данных C
unsigned char, интерпретирует значение как малое целое число. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_uint -
Представляет тип данных C
unsigned int. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется. На платформах, гдеsizeof(int) == sizeof(long), он является алиасомc_ulong.
-
class ctypes.c_uint8 -
Представляет тип данных C 8-битный
unsigned int. Обычно является алиасомc_ubyte.
-
class ctypes.c_uint16 -
Представляет тип данных C 16-битный
unsigned int. Обычно является алиасомc_ushort.
-
class ctypes.c_uint32 -
Представляет тип данных C 32-битный
unsigned int. Обычно является алиасомc_uint.
-
class ctypes.c_uint64 -
Представляет тип данных C 64-битный
unsigned int. Обычно является алиасомc_ulonglong.
-
class ctypes.c_ulong -
Представляет тип данных C
unsigned long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_ulonglong -
Представляет тип данных C
unsigned long long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_ushort -
Представляет тип данных C
unsigned short. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_void_p -
Представляет тип данных C
void*. Значение представлено в виде целого числа. Конструктор принимает необязательный целочисленный инициализатор.
-
class ctypes.c_wchar -
Представляет тип данных C
wchar_t, и интерпретирует значение как строку Unicode, содержащую один символ. Конструктор принимает необязательный строковый инициализатор; длина строки должна быть ровно один символ.
-
class ctypes.c_wchar_p -
Представляет тип данных C
wchar_t*, который должен быть указателем на нуль-терминированную строку широких символов. Конструктор принимает целочисленный адрес или строку.
-
class ctypes.c_bool -
Представляет тип данных C
bool(точнее,_Boolиз C99). Его значение может бытьTrueилиFalse, и конструктор принимает любой объект, имеющий истинное значение.
-
class ctypes.HRESULT -
Только Windows: Представляет значение
HRESULT, которое содержит информацию об успехе или ошибке для вызова функции или метода.
-
class ctypes.py_object -
Представляет тип данных C
PyObject*. Вызов без аргумента создает указательNULLPyObject*.
Модуль ctypes.wintypes предоставляет еще некоторые другие типы данных, специфичные для Windows, например HWND, WPARAM, или DWORD. Также определены некоторые полезные структуры, такие как MSG или RECT.
Типы структурированных данных
-
class ctypes.Union(*args, **kw) -
Абстрактный базовый класс для объединений в родном порядке байтов.
-
class ctypes.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_должна быть определена до первого использования типа (создан экземпляр, вызванsizeof()и т. д.). Поздние присваивания переменной класса_fields_вызовут исключение AttributeError.Возможна дефиниция подподклассов типов структур, они наследуют поля базового класса плюс
_fields_, определённые в подподклассе, если таковые имеются.
-
_pack_ -
Необязательное небольшое целое число, которое позволяет переопределить выравнивание полей структуры в экземпляре.
_pack_должно быть уже определено при присваивании_fields_, иначе это не повлияет.
-
_anonymous_ -
Необязательная последовательность, которая перечисляет имена безымянных (анонимных) полей.
_anonymous_должно быть определено до присваивания_fields_, иначе это не повлияет.Поля, перечисленные в этой переменной, должны быть полями типа структуры или объединения.
ctypesсоздаст дескрипторы в типе структуры, которые позволяют напрямую обращаться к вложенным полям без необходимости создания поля структуры или объединения.Вот пример типа (Windows):
class _U(Union): _fields_ = [("lptdesc", POINTER(TYPEDESC)), ("lpadesc", POINTER(ARRAYDESC)), ("hreftype", HREFTYPE)] class TYPEDESC(Structure): _anonymous_ = ("u",) _fields_ = [("u", _U), ("vt", VARTYPE)]Структура
TYPEDESCописывает тип данных COM, полеvtуказывает, какое из полей объединения является действительным. Поскольку полеuопределено как анонимное поле, теперь можно напрямую обращаться к членам экземпляра TYPEDESC.td.lptdescиtd.u.lptdescэквивалентны, но первое быстрее, так как не требует создания временного экземпляра объединения:td = TYPEDESC() td.vt = VT_PTR td.lptdesc = POINTER(some_type) td.u.lptdesc = POINTER(some_type)
Возможна дефиниция подподклассов структур, они наследуют поля базового класса. Если в определении подкласса есть отдельная переменная
_fields_, поля, указанные в ней, добавляются к полям базового класса.Конструкторы структур и объединений принимают позиционные и именованные аргументы. Позиционные аргументы используются для инициализации полей членов в том же порядке, что и в
_fields_. Именованные аргументы в конструкторе интерпретируются как присваивания атрибутов, поэтому они будут инициализировать_fields_с тем же именем или создавать новые атрибуты для имен, отсутствующих в_fields_. -
Массивы и указатели
-
class ctypes.Array(*args) -
Абстрактный базовый класс для массивов.
Рекомендуемый способ создания конкретных типов массивов — умножение любого типа данных
ctypesна неотрицательное целое число. В качестве альтернативы можно создать подкласс этого типа и определить переменные класса_length_и_type_. Элементы массива можно читать и записывать с помощью стандартных индексных и срезовых обращений; при чтении срезов полученный объект не является объектомArray.-
_length_ -
Положительное целое число, определяющее количество элементов в массиве. Индексы вне диапазона приводят к
IndexError. Возвращается функциейlen().
-
_type_ -
Определяет тип каждого элемента в массиве.
Конструкторы подклассов массивов принимают позиционные аргументы, используемые для инициализации элементов в порядке.
-
-
class ctypes._Pointer -
Приватный, абстрактный базовый класс для указателей.
Конкретные типы указателей создаются с помощью вызова
POINTER()с типом, на который будет указывать указатель; это делается автоматически функциейpointer().Если указатель указывает на массив, его элементы можно читать и записывать с помощью стандартных индексных и срезовых обращений. Объекты указателей не имеют размера, поэтому
len()вызоветTypeError. Отрицательные индексы будут считывать данные из памяти перед указателем (как в C), а индексы вне диапазона, вероятно, приведут к ошибке доступа (если вам повезет).-
_type_ -
Определяет тип, на который указывает указатель.
-
contents -
Возвращает объект, на который указывает указатель. Присваивание этому атрибуту изменяет указатель на указание на присвоенный объект.
-
© 2001–2023 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.10/library/ctypes.html