ctypes — Библиотека внешних функций для Python
ctypes — это библиотека внешних функций для Python. Она предоставляет совместимые с C типы данных и позволяет вызывать функции в DLL или динамических библиотеках. Она может использоваться для оборачивания этих библиотек в чистом Python.
Учебник по использованию ctypes
Примечание: примеры кода в этом учебнике используют doctest, чтобы убедиться, что они работают. Поскольку некоторые примеры кода ведут себя по-разному под Linux, Windows или Mac OS X, они содержат директивы doctest в комментариях.
Примечание: некоторые примеры кода ссылаются на тип c_int из ctypes. На платформах, где sizeof(long) == sizeof(int) он является псевдонимом для c_long. Поэтому вы не должны удивляться, если будет выведено c_long, вместо ожидаемого c_int — они на самом деле представляют один и тот же тип.
Загрузка динамических библиотек
ctypes экспортирует объекты cdll, а под Windows — также windll и oledll для загрузки динамических библиотек.
Библиотеки загружаются через доступ к ним как к атрибутам этих объектов. cdll загружает библиотеки, которые экспортируют функции с использованием стандартной cdecl конвенции вызова, в то время как windll загружает библиотеки, вызывающие функции с stdcall конвенцией вызова. oledll также использует stdcall конвенцию вызова и предполагает, что функции возвращают код ошибки Windows HRESULT. Код ошибки используется для автоматического поднятия исключения OSError, когда вызов функции завершается неудачно.
Изменено в версии 3.3: Ошибки Windows раньше поднимали исключение WindowsError, которое теперь является псевдонимом для OSError.
Ниже приведены примеры для Windows. Обратите внимание, что msvcrt — это стандартная библиотека MS C, содержащая большинство стандартных функций C, и использующая конвенцию вызова cdecl:
>>> from ctypes import * >>> print(windll.kernel32) <WinDLL 'kernel32', handle ... at ...> >>> print(cdll.msvcrt) <CDLL 'msvcrt', handle ... at ...> >>> libc = cdll.msvcrt >>>
Windows автоматически добавляет стандартное .dll расширение файла.
Примечание
Доступ к стандартной библиотеке C через cdll.msvcrt может использовать устаревшую версию библиотеки, которая может быть несовместима с той, что используется Python. В возможности, используйте встроенные функции Python, или импортируйте и используйте модуль msvcrt.
В Linux необходимо указывать имя файла включая расширение для загрузки библиотеки, поэтому доступ по атрибутам для загрузки библиотек невозможен. Следует использовать метод LoadLibrary() загрузчиков dll, или загружать библиотеку, создав экземпляр CDLL, вызвав конструктор:
>>> cdll.LoadLibrary("libc.so.6")
<CDLL 'libc.so.6', handle ... at ...>
>>> libc = CDLL("libc.so.6")
>>> libc
<CDLL 'libc.so.6', handle ... at ...>
>>>
Доступ к функциям из загруженных DLL
Функции доступны как атрибуты объектов dll:
>>> from ctypes import *
>>> libc.printf
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.GetModuleHandleA)
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.MyOwnFunction)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "ctypes.py", line 239, in __getattr__
func = _StdcallFuncPtr(name, self)
AttributeError: function 'MyOwnFunction' not found
>>>
Обратите внимание, что системные dll win32, такие как kernel32 и user32 часто экспортируют ANSI и UNICODE версии функций. Версия UNICODE экспортируется с добавленным W к имени, а ANSI — с добавленным A к имени. Функция win32 GetModuleHandle , которая возвращает дескриптор модуля для заданного имени модуля, имеет следующий прототип C, и макрос используется для экспорта одного из них как GetModuleHandle в зависимости от того, определен UNICODE или нет:
/* ANSI version */ HMODULE GetModuleHandleA(LPCSTR lpModuleName); /* UNICODE version */ HMODULE GetModuleHandleW(LPCWSTR lpModuleName);
windll не пытается автоматически выбрать одну из них, вам необходимо явно указать GetModuleHandleA или GetModuleHandleW и затем вызвать её соответственно с байтовыми или строковыми объектами.
Иногда DLL экспортируют функции с именами, которые не являются допустимыми идентификаторами Python, например "??2@YAPAXI@Z". В этом случае вам нужно использовать getattr() для получения функции:
>>> getattr(cdll.msvcrt, "??2@YAPAXI@Z") <_FuncPtr object at 0x...> >>>
В Windows некоторые DLL экспортируют функции не по имени, а по порядковому номеру. К этим функциям можно получить доступ, индексируя объект dll порядковым номером:
>>> cdll.kernel32[1]
<_FuncPtr object at 0x...>
>>> cdll.kernel32[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "ctypes.py", line 310, in __getitem__
func = _StdcallFuncPtr(name, self)
AttributeError: function ordinal 0 not found
>>>
Вызов функций
Вы можете вызывать эти функции как любые другие вызываемые объекты Python. Этот пример использует функцию time() , которая возвращает системное время в секундах с момента эпохи Unix, и функцию GetModuleHandleA() , которая возвращает дескриптор модуля win32.
В этом примере обе функции вызываются с указателем NULL (должен использоваться None в качестве указателя NULL):
>>> print(libc.time(None)) 1150640792 >>> print(hex(windll.kernel32.GetModuleHandleA(None))) 0x1d000000 >>>
ValueError возникает, когда вы вызываете функцию с stdcall конвенцией вызова с cdecl конвенцией вызова, или наоборот:
>>> cdll.kernel32.GetModuleHandleA(None) Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: Procedure probably called with not enough arguments (4 bytes missing) >>> >>> windll.msvcrt.printf(b"spam") Traceback (most recent call last): File "<stdin>", line 1, in <module> ValueError: Procedure probably called with too many arguments (4 bytes in excess) >>>
Чтобы узнать правильную конвенцию вызова, необходимо обратиться к файлу заголовков C или документации для функции, которую вы хотите вызвать.
В Windows ctypes использует обработку исключений win32 для предотвращения сбоев из-за ошибок защиты при вызове функций с недопустимыми значениями аргументов:
>>> windll.kernel32.GetModuleHandleA(32) Traceback (most recent call last): File "<stdin>", line 1, in <module> OSError: exception: access violation reading 0x00000020 >>>
Однако, существует достаточно способов вызвать сбой Python с помощью ctypes, так что будьте осторожны. Модуль faulthandler может быть полезен при отладке сбоев (например, из-за ошибок сегментации, вызванных ошибочными вызовами функций библиотеки C).
None, целые числа, байтовые объекты и (строки) являются единственными встроенными объектами Python, которые могут быть напрямую использованы в качестве параметров в этих вызовах функций. None передается как указатель C NULL, байтовые объекты и строки передаются как указатели на блок памяти, который содержит их данные (char * или wchar_t *). Целые числа Python передаются как платформенный тип C int, их значения маскируются, чтобы вписаться в тип C.
Прежде чем перейти к вызову функций с другими типами параметров, мы должны узнать больше о типах данных ctypes.
Основные типы данных
ctypes определяет ряд примитивных типов данных, совместимых с C:
Тип ctypes | Тип C | Тип Python |
|---|---|---|
| bool (1) | |
| байтовый объект длиной в 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: exceptions.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, чтобы разрешить использование экземпляров ваших собственных классов в качестве аргументов функции. ctypes ищет атрибут _as_parameter_ и использует его как аргумент функции. Конечно, это должно быть целое число, строка или байты:
>>> class Bottles: ... def __init__(self, number): ... self._as_parameter_ = number ... >>> bottles = Bottles(42) >>> printf(b"%d bottles of beer\n", bottles) 42 bottles of beer 19 >>>
Если вы не хотите хранить данные экземпляра в переменной экземпляра _as_parameter_, вы можете определить свойство property, которое делает атрибут доступным по запросу.
Указание требуемых типов аргументов (прототипы функций)
Можно указать требуемые типы аргументов функций, экспортируемых из DLL, установив атрибут argtypes.
argtypes должна быть последовательностью типов данных C (функция printf вероятно, здесь не является хорошим примером, так как она принимает переменное количество и разные типы параметров в зависимости от строки формата, с другой стороны, это довольно удобно для экспериментов с этой функцией):
>>> printf.argtypes = [c_char_p, c_char_p, c_int, c_double] >>> printf(b"String '%s', Int %d, Double %f\n", b"Hi", 10, 2.2) String 'Hi', Int 10, Double 2.200000 37 >>>
Указание формата защищает от несовместимых типов аргументов (как и прототип функции C) и пытается преобразовать аргументы в допустимые типы:
>>> printf(b"%d %d %d", 1, 2, 3) Traceback (most recent call last): File "<stdin>", line 1, in <module> ArgumentError: argument 2: exceptions.TypeError: wrong type >>> printf(b"%s %d %f\n", b"X", 2, 3) X 2 3.000000 13 >>>
Если вы определили свои собственные классы, которые передаёте в вызовы функций, вам нужно реализовать метод класса from_param() для возможности их использования в последовательности argtypes. Метод класса from_param() получает переданный в вызов функции объект Python, должен выполнить проверку типа или то, что необходимо, чтобы убедиться, что этот объект приемлем, а затем вернуть сам объект, его атрибут _as_parameter_ или то, что вы хотите передать в качестве аргумента функции C в данном случае. Результат снова должен быть целым числом, строкой, байтами, экземпляром ctypes или объектом с атрибутом _as_parameter_.
Типы возвращаемых значений
По умолчанию предполагается, что функции возвращают тип C int. Другие типы возвращаемых значений можно указать, установив атрибут restype объекта функции.
Вот более продвинутый пример, в котором используется функция strchr, которая ожидает указатель на строку и символ и возвращает указатель на строку:
>>> strchr = libc.strchr
>>> strchr(b"abcdef", ord("d"))
8059983
>>> strchr.restype = c_char_p # c_char_p 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: exceptions.TypeError: one character string expected >>> print(strchr(b"abcdef", b"x")) None >>> strchr(b"abcdef", b"d") 'def' >>>
Вы также можете использовать вызываемый объект Python (например, функцию или класс) в качестве атрибута restype, если внешняя функция возвращает целое число. Вызываемый объект будет вызван с целым числом, возвращенным C-функцией, и результат этого вызова будет использован в качестве результата вызова вашей функции. Это полезно для проверки значений возврата ошибок и автоматического повышения исключения:
>>> GetModuleHandle = windll.kernel32.GetModuleHandleA
>>> def ValidHandle(value):
... if value == 0:
... raise WinError()
... return value
...
>>>
>>> GetModuleHandle.restype = ValidHandle
>>> GetModuleHandle(None)
486539264
>>> GetModuleHandle("something silly")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in ValidHandle
OSError: [Errno 126] The specified module could not be found.
>>>
WinError — это функция, которая вызовет API Windows FormatMessage() для получения строкового представления кода ошибки и возвратит исключение. WinError принимает необязательный параметр кода ошибки; если он не используется, он вызывает GetLastError() для его получения.
Обратите внимание, что существует гораздо более мощный механизм проверки ошибок через атрибут errcheck; подробности см. в справочном руководстве.
Передача указателей (или: передача параметров по ссылке)
Иногда функция C API ожидает указатель на тип данных в качестве параметра, вероятно, для записи в соответствующее местоположение или если данные слишком велики для передачи по значению. Это также известно как передача параметров по ссылке.
ctypes экспортирует функцию byref(), которая используется для передачи параметров по ссылке. Тот же эффект можно получить с помощью функции pointer(), хотя pointer() выполняет гораздо больше работы, так как создает настоящий объект указателя, поэтому использование byref() быстрее, если вам не нужен объект указателя в самом Python:
>>> i = c_int() >>> f = c_float() >>> s = create_string_buffer(b'\000' * 32) >>> print(i.value, f.value, repr(s.value)) 0 0.0 b'' >>> libc.sscanf(b"1 3.14 Hello", b"%d %f %s", ... byref(i), byref(f), s) 3 >>> print(i.value, f.value, repr(s.value)) 1 3.1400001049 b'Hello' >>>
Структуры и объединения
Структуры и объединения должны быть производными от базовых классов Structure и Union, определённых в модуле ctypes. Каждый подкласс должен определить атрибут _fields_ . _fields_ должен быть списком 2-х кортежей, содержащих имя поля и тип поля.
Тип поля должен быть типом ctypes, например, c_int, или любым другим производным типом ctypes: структура, объединение, массив, указатель.
Вот простой пример структуры POINT, которая содержит два целых числа с именами x и y, а также показывает, как инициализировать структуру в конструкторе:
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = [("x", c_int),
... ("y", c_int)]
...
>>> point = POINT(10, 20)
>>> print(point.x, point.y)
10 20
>>> point = POINT(y=5)
>>> print(point.x, point.y)
0 5
>>> POINT(1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: too many initializers
>>>
Однако вы можете создать и более сложные структуры. Структура сама может содержать другие структуры, используя структуру как тип поля.
Вот структура RECT, которая содержит два POINT с именами upperleft и lowerright:
>>> class RECT(Structure):
... _fields_ = [("upperleft", POINT),
... ("lowerright", POINT)]
...
>>> rc = RECT(point)
>>> print(rc.upperleft.x, rc.upperleft.y)
0 5
>>> print(rc.lowerright.x, rc.lowerright.y)
0 0
>>>
Вложенные структуры также можно инициализировать в конструкторе несколькими способами:
>>> r = RECT(POINT(1, 2), POINT(3, 4)) >>> r = RECT((1, 2), (3, 4))
Поля дескрипторов можно получить из класса, они полезны для отладки, так как могут предоставить полезную информацию:
>>> print(POINT.x) <Field type=c_long, ofs=0, size=4> >>> print(POINT.y) <Field type=c_long, ofs=4, size=4> >>>
Предупреждение
ctypes не поддерживает передачу объединений или структур с битами-полями функциям по значению. Хотя это может работать на 32-разрядном x86, это не гарантируется библиотекой для общего случая. Объединения и структуры с битами-полями всегда должны передаваться функциям по указателю.
Выравнивание и порядок байтов структур/объединений
По умолчанию поля структуры и объединения выравниваются так же, как это делает компилятор 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 — это предопределенный символ, предоставляющий доступ к API Python C:
>>> opt_flag = c_int.in_dll(pythonapi, "Py_OptimizeFlag") >>> print(opt_flag) c_long(0) >>>
Если бы интерпретатор был запущен с -O, пример напечатал бы c_long(1), или c_long(2) , если был указан -OO.
Расширенный пример, который также демонстрирует использование указателей, обращается к указателю PyImport_FrozenModules, экспортированному Python.
Цитата из документации для этого значения:
Этот указатель инициализируется так, чтобы указывать на массив записей struct _frozen, завершаемый записью, члены которой все NULL или ноль. При импорте замороженного модуля поиск ведется в этой таблице. Внешние коды могут использовать хитрости с этим, чтобы предоставить динамически созданный набор замороженных модулей.
Поэтому манипулирование этим указателем может даже оказаться полезным. Чтобы ограничить размер примера, мы покажем только то, как эту таблицу можно прочитать с помощью ctypes:
>>> from ctypes import *
>>>
>>> class struct_frozen(Structure):
... _fields_ = [("name", c_char_p),
... ("code", POINTER(c_ubyte)),
... ("size", c_int)]
...
>>>
Мы определили тип данных struct _frozen, поэтому мы можем получить указатель на таблицу:
>>> FrozenTable = POINTER(struct_frozen) >>> table = FrozenTable.in_dll(pythonapi, "PyImport_FrozenModules") >>>
Так как table является указателем на массив записей struct_frozen, мы можем перебирать его, но мы должны убедиться, что наш цикл завершается, так как у указателей нет размера. Рано или поздно это, вероятно, вызовет нарушение доступа или что-то подобное, поэтому лучше выйти из цикла, когда мы достигнем записи NULL:
>>> for item in table:
... if item.name is None:
... break
... print(item.name.decode("ascii"), item.size)
...
_frozen_importlib 31764
_frozen_importlib_external 41499
__hello__ 161
__phello__ -161
__phello__.spam 161
>>>
Тот факт, что стандартный Python имеет замороженный модуль и замороченный пакет (указанный отрицательным членом size) не широко известен, он используется только для тестирования. Попробуйте его с import __hello__ например.
Удивительные моменты
В ctypes есть некоторые особенности, где вы можете ожидать чего-то другого, чем то, что происходит на самом деле.
Рассмотрим следующий пример:
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = ("x", c_int), ("y", c_int)
...
>>> class RECT(Structure):
... _fields_ = ("a", POINT), ("b", POINT)
...
>>> p1 = POINT(1, 2)
>>> p2 = POINT(3, 4)
>>> rc = RECT(p1, p2)
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
1 2 3 4
>>> # 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, за исключением Windows CE, гдеWINFUNCTYPE()аналогичноCFUNCTYPE(). Функция освободит 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, используемой Python и модулями расширения. Если имя библиотеки определить нельзя, возвращает
None.Если требуется освободить память, например, выделенную модулем расширения с помощью вызова
free(void *), важно, чтобы вы использовали функцию в той же библиотеке, которая выделила память.
-
ctypes.FormatError([code]) -
Только Windows: Возвращает текстовое описание кода ошибки code. Если код ошибки не указан, используется последний код ошибки, полученный при вызове функции Windows GetLastError.
-
ctypes.GetLastError() -
Только Windows: Возвращает последний код ошибки, установленный Windows в потоке вызова. Эта функция вызывает функцию Windows
GetLastError()напрямую, она не возвращает копии кода ошибки, хранящейся в частном пространстве ctypes.
-
ctypes.get_errno() -
Возвращает текущее значение внутренней копии системной переменной
errnoв потоке вызова.Вызывает событие аудита
ctypes.get_errnoбез аргументов.
-
ctypes.get_last_error() -
Только Windows: возвращает текущее значение внутренней копии системной переменной
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) -
Устанавливает текущее значение внутренней копии системной переменной
errnoв потоке вызова на value и возвращает предыдущее значение.Вызывает событие аудита
ctypes.set_errnoс аргументомerrno.
-
ctypes.set_last_error(value) -
Только Windows: устанавливает текущее значение внутренней копии системной переменной
LastErrorв потоке вызова на value и возвращает предыдущее значение.Вызывает событие аудита
ctypes.set_last_errorс аргументомerror.
-
ctypes.sizeof(obj_or_type) -
Возвращает размер буфера памяти типа или экземпляра ctypes в байтах. Действует так же, как оператор C
sizeof.
-
ctypes.string_at(address, size=-1) -
Эта функция возвращает строку C, начинающуюся по адресу памяти address, в виде объекта bytes. Если size указан, он используется как размер, иначе строка предполагается нуль-завершаемой.
Вызывает событие аудита
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 -
Этот атрибут содержит фактическое значение экземпляра. Для целых и указательских типов это целое число, для символьных типов — объект bytes или строка из одного символа, для указателей на символы — объект Python bytes или строка.
Когда атрибут
valueизвлекается из экземпляра ctypes, обычно каждый раз возвращается новый объект. Модульctypesне реализует возврат оригинального объекта, всегда создается новый объект. То же самое верно для всех других экземпляров объектов ctypes.
-
Основные типы данных, возвращаемые в результате вызова внешней функции или, например, при получении элементов структуры или массивов, прозрачно преобразуются в родные типы Python. Другими словами, если внешняя функция имеет тип restype типа c_char_p, вы всегда получите объект Python bytes, а не экземпляр c_char_p.
Подклассы основных типов данных не наследуют это поведение. Итак, если внешняя функция restype является подклассом c_void_p, вы получите экземпляр этого подкласса из вызова функции. Конечно, вы можете получить значение указателя, обратившись к атрибуту value.
Вот основные типы данных ctypes:
-
class ctypes.c_byte -
Представляет тип данных C
signed char, и интерпретирует значение как малое целое число. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_char -
Представляет тип данных C
char, и интерпретирует значение как один символ. Конструктор принимает необязательный строковый инициализатор, длина строки должна составлять ровно один символ.
-
class ctypes.c_char_p -
Представляет тип данных C
char *при указании на нуль-терминированную строку. Для общего указателя на символ, который также может указывать на двоичные данные, необходимо использоватьPOINTER(c_char). Конструктор принимает целочисленный адрес или объект bytes.
-
class ctypes.c_double -
Представляет тип данных C
double. Конструктор принимает необязательный инициализатор с плавающей точкой.
-
class ctypes.c_longdouble -
Представляет тип данных C
long double. Конструктор принимает необязательный инициализатор с плавающей точкой. На платформах, гдеsizeof(long double) == sizeof(double), это псевдоним дляc_double.
-
class ctypes.c_float -
Представляет тип данных C
float. Конструктор принимает необязательный инициализатор с плавающей точкой.
-
class ctypes.c_int -
Представляет тип данных C
signed int. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется. На платформах, гдеsizeof(int) == sizeof(long), это псевдоним дляc_long.
-
class ctypes.c_int8 -
Представляет 8-битный тип данных C
signed int. Обычно псевдоним дляc_byte.
-
class ctypes.c_int16 -
Представляет 16-битный тип данных C
signed int. Обычно псевдоним дляc_short.
-
class ctypes.c_int32 -
Представляет 32-битный тип данных C
signed int. Обычно псевдоним дляc_int.
-
class ctypes.c_int64 -
Представляет 64-битный тип данных C
signed int. Обычно псевдоним дляc_longlong.
-
class ctypes.c_long -
Представляет тип данных C
signed long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_longlong -
Представляет тип данных C
signed long long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_short -
Представляет тип данных C
signed short. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_size_t -
Представляет тип данных C
size_t.
-
class ctypes.c_ssize_t -
Представляет тип данных C
ssize_t.Введено в версии 3.2.
-
class ctypes.c_ubyte -
Представляет тип данных C
unsigned char, интерпретирует значение как малое целое число. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_uint -
Представляет тип данных C
unsigned int. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется. На платформах, гдеsizeof(int) == sizeof(long), это псевдоним дляc_ulong.
-
class ctypes.c_uint8 -
Представляет 8-битный тип данных C
unsigned int. Обычно псевдоним дляc_ubyte.
-
class ctypes.c_uint16 -
Представляет 16-битный тип данных C
unsigned int. Обычно псевдоним дляc_ushort.
-
class ctypes.c_uint32 -
Представляет 32-битный тип данных C
unsigned int. Обычно псевдоним дляc_uint.
-
class ctypes.c_uint64 -
Представляет 64-битный тип данных C
unsigned int. Обычно псевдоним дляc_ulonglong.
-
class ctypes.c_ulong -
Представляет тип данных C
unsigned long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_ulonglong -
Представляет тип данных C
unsigned long long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_ushort -
Представляет тип данных C
unsigned short. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
-
class ctypes.c_void_p -
Представляет тип C
void *. Значение представлено в виде целого числа. Конструктор принимает необязательный целочисленный инициализатор.
-
class ctypes.c_wchar -
Представляет тип данных C
wchar_t, и интерпретирует значение как строку 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 *. Вызов без аргументов создает указательNULLнаPyObject *.
Модуль 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–2022 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.8/library/ctypes.html