ctypes
— Python библиотека внешних функций¶
ctypes
Python библиотека внешних функций. Она
предоставляет C-совместимые типы данных и позволяет вызывать функции из DLL или
разделяемых библиотек. Она может быть использована для оборачивания этой библиотеки в
чистый Python.
Учебник по ctypes¶
Примечание. Примеры кода в данном учебном пособии используют doctest
,
чтобы убедиться в их работоспособности. Поскольку некоторые образцы
кода ведут себя по-разному в Linux, Windows или Mac OS X, они содержат
директивы 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
>>>
Обратите внимание, что win32 системные dll, такие как kernel32
и
user32
, часто экспортируют ANSI, а также юникод версии функции.
Юникод версия экспортируется с добавлением к имени W
, а версия ANSI
экспортируется с добавлением к имени A
. У функции win32 GetModuleHandle
,
которая возвращает обработчика модуля для данного имени модуля, есть следующий прототип
C и макрос - используемый, чтобы предоставить одного из них как GetModuleHandle
в
зависимости от того, определен ли юникод или нет:
/* ANSI версия */
HMODULE GetModuleHandleA(LPCSTR lpModuleName);
/* UNICODE версия */
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 *
или: c:type:wchar_t *). Python целые числа передаются как тип
платформы по умолчанию C int
, их значение маскируется для
соответствия типу C.
Прежде чем переходить к вызову функций с другими типами параметров, мы должны
узнать больше о ctypes
типах данных.
Фундаментальные типы данных¶
ctypes
определяет ряд примитивных C-совместимых типов данных:
тип ctypes | тип C | тип Python |
---|---|---|
c_bool |
_Bool |
bool (1) |
c_char |
char |
1-символьный байтовый объект |
c_wchar |
wchar_t |
1-символьная строка |
c_byte |
char |
int |
c_ubyte |
unsigned char |
int |
c_short |
short |
int |
c_ushort |
unsigned short |
int |
c_int |
int |
int |
c_uint |
unsigned int |
int |
c_long |
long |
int |
c_ulong |
unsigned long |
int |
c_longlong |
__int64 or long long |
int |
c_ulonglong |
unsigned __int64 or
unsigned long long |
int |
c_size_t |
size_t |
int |
c_ssize_t |
ssize_t or
Py_ssize_t |
int |
c_float |
float |
float |
c_double |
double |
float |
c_longdouble |
long double |
float |
c_char_p |
char * (NUL terminated) |
объект байтов или None |
c_wchar_p |
wchar_t * (NUL terminated) |
string или None |
c_void_p |
void * |
int или None |
- Конструктор принимает любой объект с истинным значением.
Все эти типы могут быть созданы путем вызова их с помощью дополнительного инициализатора правильного типа и значения:
>>> c_int()
c_long(0)
>>> c_wchar_p("Hello, World")
c_wchar_p(140018365411392)
>>> c_ushort(-3)
c_ushort(65533)
>>>
Поскольку эти типы являются изменяемыми, их значение также могут быть изменены впоследствии:
>>> i = c_int(42)
>>> print(i)
c_long(42)
>>> print(i.value)
42
>>> i.value = -99
>>> print(i.value)
-99
>>>
Назначение нового значение для сущности типов указателей c_char_p
,
c_wchar_p
и c_void_p
изменяет участок в памяти, на которые они указывают,
не содержат блока памяти (конечно, нет, потому что байтовые объекты Python
являются неизменяемыми):
>>> s = "Hello, World"
>>> c_s = c_wchar_p(s)
>>> print(c_s)
c_wchar_p(139966785747344)
>>> print(c_s.value)
Hello World
>>> c_s.value = "Hi, there"
>>> print(c_s) # место в памяти изменилось
c_wchar_p(139966783348904)
>>> print(c_s.value)
Hi, there
>>> print(s) # первый объект остается неизменным
Hello, World
>>>
Однако следует быть осторожным, чтобы не передавать их функциям, ожидающим
указатели в изменяемой памяти. Если вам нужны изменяемые блоки памяти, ctypes
содержит функцию create_string_buffer()
, которая создает их различными способами. К текущему
содержимому блока памяти можно обращаться (или изменять) с помощью свойства
raw
; если вы хотите получить доступ к нему как к строке оканчивающейся на NUL,
используйте свойтсво value
:
>>> from ctypes import *
>>> p = create_string_buffer(3) # создание 3-байтового буфера, инициализированного NUL байтами
>>> print(sizeof(p), repr(p.raw))
3 b'\x00\x00\x00'
>>> p = create_string_buffer(b"Hello") # создание буфера, содержащего строку окончивающуюся NUL
>>> print(sizeof(p), repr(p.raw))
6 b'Hello\x00'
>>> print(repr(p.value))
b'Hello'
>>> p = create_string_buffer(b"Hello", 10) # создание 10 байт буфера
>>> 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, за исключением целых чисел,
строк и объектов байтов, должны быть упакованы в соответствующий тип 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, переданный к вызову функции, он должен
сделать typecheck или независимо от того, что необходимо, чтобы удостовериться,
что этот объект приемлем, и затем возвратите сам объект, его признак
_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 указатель на строку
>>> strchr(b"abcdef", ord("d"))
b'def'
>>> print(strchr(b"abcdef", ord("x")))
None
>>>
Чтобы избежать вышеперечисленных вызовов ord("x")
, можно задать атрибут
argtypes
, и второй аргумент будет преобразован из одного объекта символ
Python байтов в символ C:
>>> strchr.restype = c_char_p
>>> strchr.argtypes = [c_char_p, c_char]
>>> strchr(b"abcdef", b"d")
'def'
>>> strchr(b"abcdef", b"def")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ArgumentError: argument 2: exceptions.TypeError: one character string expected
>>> print(strchr(b"abcdef", b"x"))
None
>>> strchr(b"abcdef", b"d")
'def'
>>>
Можно также использовать вызываемый объект Python (например, функцию или
класс) в качестве атрибута restype
, если внешняя функция возвращает
целое число. Вызываемый будет вызван с помощью функции integer
функция 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))
Поле дескриптора может быть извлечено из class, они полезны для отладки, так как могут предоставить полезную информацию:
>>> print(POINT.x)
<Field type=c_long, ofs=0, size=4>
>>> print(POINT.y)
<Field type=c_long, ofs=4, size=4>
>>>
Предупреждение
ctypes
не поддерживает передачу объединения или структуры с битовыми
полями в функции по значение. Хотя это может работать на 32-разрядных x86,
библиотека не гарантирует работу в общем случае. Объединения и структуры с
битовыми полями всегда должны передаваться функциям по указателю.
Выравнивание структуры/объединения и порядок байтов¶
По умолчанию поля Structure и Union выравниваются таким же образом, как это
делает компилятор C. Возможно переопределить это поведение, определив _pack_
класс атрибут в определении подкласс. Значение должно быть
положительным целым числом и указывать максимальное выравнивание для полей. Это
то, что #pragma pack(n)
также делает в MSVC.
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()
действительно больше, чем просто создает случаи
указателя, она должна создать указатель types сначала. Это выполняется с
помощью функции 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; /* прямая декларация */
struct cell {
char *name;
struct cell *next;
};
Простой перевод на ctypes код был бы таким, но он не работает:
>>> class cell(Structure):
... _fields_ = [("name", c_char_p),
... ("next", POINTER(cell))]
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in cell
NameError: name 'cell' is not defined
>>>
потому что новый class cell
недоступен в самой class инструкции. В
ctypes
можно определить cell
класс и установить _fields_
атрибут позже, после инструкции class:
>>> from ctypes import *
>>> class cell(Structure):
... pass
...
>>> cell._fields_ = [("name", c_char_p),
... ("next", POINTER(cell))]
>>>
Проверим это. Создадим две сущности cell
и позволяем им указывать друг на
друга и в завершении, проследовать за цепочкой указателей несколько раз:
>>> c1 = cell()
>>> c1.name = "foo"
>>> c2 = cell()
>>> c2.name = "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()
, которая используется для сортировки элементов с
помощью колбэк функции.:c:func: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.
Цитирование документов для этого значения:
Этот указатель инициализирован, чтобы указать на массив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
является pointer
к массиву записей struct_frozen
, мы
можем выполнять итерацию над ним, но мы просто должны убедиться, что наш цикл
заканчивается, потому что указатели не имеют размера. Рано или поздно это,
вероятно, потерпело бы крах с нарушением доступа или что бы то ни было, таким
образом, лучше прервать цикл, когда мы передаём на вход NULL
:
>>> for item in table:
... if item.name is None:
... break
... print(item.name.decode("ascii"), item.size)
...
_frozen_importlib 31764
_frozen_importlib_external 41499
__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
>>> # теперь поменяем местами две точки
>>> rc.a, rc.b = rc.b, rc.a
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
3 4 3 4
>>>
Мы, ожидали, что последний инструкция напечатает 3 4 1 2
. Что
случилось? Выполним по шагам вышеуказанную строку rc.a, rc.b = rc.b, rc.a
:
>>> temp0, temp1 = rc.b, rc.a
>>> rc.a = temp0
>>> rc.b = temp1
>>>
Обратите внимание, что temp0
и temp1
являются объектами, по-прежнему
использующими внутренний буфер указанного выше объекта rc
. Таким
образом, выполнение rc.a = temp0
копирует содержимое буфера temp0
в буфер
rc
. Это, в свою очередь, меняет содержание temp1
. Таким образом,
последнее назначение rc.b = temp1
, не содержит ожидаемого эффекта.
Имейте в виду, что получение подобъектов из Structure, Unions и Arrays не копирует подобъект, вместо этого он получает объект-оболочку, обращающийся к базовому буферу корневого объекта.
Другой пример, который может вести себя иначе, чем ожидалось:
>>> s = c_char_p()
>>> s.value = b"abc def ghi"
>>> s.value
b'abc def ghi'
>>> s.value is s.value
False
>>>
Примечание
Объектов, экземпляры которых созданы из c_char_p
, могут содержать только
значения в байтах или целых чисел.
Почему он печатает False
? ctypes сущности - это объекты, содержащие блок
памяти и некоторые дескриптор’ы, обращающиеся к содержимому памяти. Сохранение
объекта Python в блоке памяти не сохраняет сам объект, вместо этого
сохраняется содержимое
объекта. При повторном доступе к содержимому, каждый раз будет
создаваться новый объект 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
преобразует строку, переданный как аргумент в объект байтов, используя 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 callable.
prototype
(func_spec[, paramflags])Возвращает внешнюю функцию, экспортированную разделяемой библиотекой. func_spec должен быть кортежем из двух элементов
(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)
>>>
Функции с выходными параметрами автоматически возвращают значение выходного параметра, если есть один, или кортеж, содержащий значения выходного параметра, если их несколько, то функция GetCreateRect возвращает экземпляр RECT, когда вызывается.
Выходные параметры могут быть объединены с протоколом errcheck
для дальнейшей
обработки выходных данных и проверки ошибок. Функция win32 GetWindowRect
api
возвращает 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.
Raises an auditing event
ctypes.addressof
with argumentobj
.
-
ctypes.
alignment
(obj_or_type)¶ Возвращает требования к выравниванию типа ctypes. obj_or_type должен быть типом ctypes или сущностью.
-
ctypes.
byref
(obj[, offset])¶ Возвращает облегченный указатель на obj, который должен быть сущностью типа ctypes. offset по умолчанию равен нулю и должен быть целым числом, которое будет добавлено к внутреннему указателю значения.
byref(obj, offset)
соответствует этому коду C:(((char *)&obj) + offset)
Возвращаемый объект может быть использован только как параметр вызова внешней функции. Ведет себя подобно
pointer(obj)
, но строительство идет намного быстрее.
-
ctypes.
cast
(obj, type)¶ Эта функция подобна оператору cast в C. Она возвращает новую сущность type, которая указывает на тот же блок памяти как obj. type должен быть типом указателя, а obj должен быть объектом, который может быть интерпретирован как указатель.
-
ctypes.
create_string_buffer
(init_or_size, size=None)¶ Эта функция создает изменяемый символьный буфер. Возвращаемый объект является массивом ctypes
c_char
.init_or_size должен быть целым числом, которое определяет размер массива или объект байтов, который будет использован для инициализации элементов массива.
Если байтовый объект указан в качестве первого аргумента, буфер становится на один элемент больше, чем его длина, так что последним элементом в массиве является завершающий символ NUL. Целое число может быть передано в качестве второго аргумента, позволяющего указать размер массива, если длина байтов не должна быть используема.
Raises an auditing event
ctypes.create_string_buffer
with argumentsinit
,size
.
-
ctypes.
create_unicode_buffer
(init_or_size, size=None)¶ Эта функция создает изменяемый юникод символьный буфер. Возвращаемый объект является массивом ctypes
c_wchar
.init_or_size должен быть целым числом, которое определяет размер массива или строка, которая будет использоваться для инициализации элементов массива.
Если строка указана в качестве первого аргумента, буфер становится на один элемент больше, чем длина строки, так что последним элементом в массиве является завершающий сивол NUL. Целое число может быть передано как второй аргумент, который позволяет определять размер массива, если не должна использоваться длина строки.
Raises an auditing event
ctypes.create_unicode_buffer
with argumentsinit
,size
.
-
ctypes.
DllCanUnloadNow
()¶ Только Windows: эта функция является хуком, который позволяет реализовать внутрипроцессные COM серверы с ctypes. Он вызывается из функции DllCanUnloadNow, которую экспортирует _ctypes расширение dll.
-
ctypes.
DllGetClassObject
()¶ Только Windows: эта функция является хуком, который позволяет реализовать внутрипроцессные COM серверы с ctypes. Он вызывается из функции CallGetClassObject, которую экспортирует библиотека расширения
_ctypes
.
-
ctypes.util.
find_library
(name)¶ Попытаться найти библиотеку и вернуть pathname. name - имя библиотеки без префикса типа
lib
, суффикса типа.so
,.dylib
или номера версии (это форма используемый для опции posix линкера-l
). Если не удается найти библиотеку, возвращает значениеNone
.Точная функциональность зависит от системы.
-
ctypes.util.
find_msvcrt
()¶ Только Windows: возвращает имя файла библиотеки времени выполнения VC используемой Python и модулями расширений. Если имя библиотеки не может быть определено, возвращается значение
None
.Если требуется освободить память, например, выделенную внутренним модулем с вызовом
free(void *)
, важно использовать функцию в той же библиотеки, которая выделила память.
-
ctypes.
FormatError
([code])¶ Только Windows: возвращает текстовое описание кода ошибки code. Если не указан код ошибки, используется последий код ошибки используемый при вызове API функции Windows GetLastError.
-
ctypes.
GetLastError
()¶ Только Windows: возвращает последний код ошибки, установленный Windows в вызывающем потоке. Эта функция вызывает функцию GetLastError() Windows напрямую, она не возвращает ctypes-приватную копию кода ошибки.
-
ctypes.
get_errno
()¶ Возвращает текущее значение ctypes-приватной копии системной переменной
errno
в вызываемом потоке.Raises an auditing event
ctypes.get_errno
with no arguments.
-
ctypes.
get_last_error
()¶ Только Windows: возвращает текущ значение ctypes-приватной копии системной переменной
LastError
в вызываемом потоке.Raises an auditing event
ctypes.get_last_error
with no arguments.
-
ctypes.
memmove
(dst, src, count)¶ То же, что и стандартная функция библиотеки C memmove: копирование count байт из src в dst. dst и src должны быть целыми числами или ctypes сущностями, которые могут быть преобразованы в указатели.
-
ctypes.
memset
(dst, c, count)¶ То же, что и стандартная функция библиотеки C memset: заполняет блок памяти по адресу dst count байтами значением c. dst должно быть целым числом, указывающим адрес или ctypes сущностью.
-
ctypes.
POINTER
(type)¶ Функция фабрика создающая и возвращающая новый тип указателя ctypes. Типы указателей кэшируются и повторно используются внутри системы, поэтому повторный вызов этой функции является дешевым. type должен быть типом ctypes.
-
ctypes.
pointer
(obj)¶ Эта функция создает новый экземпляр указателя, указывающий на obj. Возвращаемый объект содержит тип
POINTER(type(obj))
.Примечание: если вы просто хотите передать указатель на объект на вызов чужой функции, вы должны использовать
byref(obj)
, который гораздо быстрее.
-
ctypes.
resize
(obj, size)¶ Эта функция изменяет размер внутреннего буфера памяти obj, который должен быть сущностью типа ctypes. Невозможно сделать буфер меньше, чем собственный размер объектов типа указанного в параметре
sizeof(type(obj))
, но можно увеличить буфер.
-
ctypes.
set_errno
(value)¶ Установить текущее значение ctypes-приватной копии системной переменной
errno
в вызываемом потоке в value и возвратить предыдущее значение.Raises an auditing event
ctypes.set_errno
with argumenterrno
.
-
ctypes.
set_last_error
(value)¶ Только Windows: установить текущий значение ctypes-приватной копии системной переменной
LastError
в вызываемом потоке в value и возвратить предыдущий значение.Raises an auditing event
ctypes.set_last_error
with argumenterror
.
-
ctypes.
sizeof
(obj_or_type)¶ Возвращает размер в байтах типа ctypes или сущности буфера памяти. Выполняет то же как и оператор C
sizeof
.
-
ctypes.
string_at
(address, size=-1)¶ Эта функция возвращает C строку, начиная с адреса памяти address как байтовый объект. Если указан размер, он используется как размер, в противном случае предполагается, что строка заканчивается нулем.
Raises an auditing event
ctypes.string_at
with argumentsaddress
,size
.
-
ctypes.
WinError
(code=None, descr=None)¶ Только Windows: эта функция, вероятно, наихудшая в ctypes. Создается сущность OSEror. Если code не указан, вызывается
GetLastError
для определения кода ошибки. Если параметр descr не указан, вызывается методFormatError()
для получения текстового описания ошибки.Изменено в версии 3.3: Создана используемая сущность
WindowsError
.
-
ctypes.
wstring_at
(address, size=-1)¶ Эта функция возвращает широкую символьную строку, начиная с адреса памяти address в виде строки. Если определен size – он используется как количество символов строки, иначе строка, как предполагается, заканчивается нулем.
Raises an auditing event
ctypes.wstring_at
with argumentsaddress
,size
.
Типы данных¶
-
class
ctypes.
_CData
¶ Это не публичный класс, являющийся общим базовым классом всех ctypes типов данных. Среди прочего, все типы ctypes сущности содержат блок памяти, который содержит C-совместимые данные; адрес блока памяти возвращается функцией модуля хелпера
addressof()
. Другая переменная сущности выставляется как_objects
; она содержит другие объекты Python, которые необходимо сохранить в случае, если блок памяти содержит указатели.Общий методы типов данных ctypes, это все методы класса (чтобы быть точным, это методы метакласса):
-
from_buffer
(source[, offset])¶ Метод возвращает ctypes сущность, который совместно использует буфер объекта source. Объект source должен поддерживать интерфейс буфера, доступный для записи. Необязательный параметр offset задает смещение в исходном буфере в байтах; значение по умолчанию равно нулю. Если исходный буфер не достаточно большой, поднимается
ValueError
.Raises an auditing event
ctypes.cdata/buffer
with argumentspointer
,size
,offset
.
-
from_buffer_copy
(source[, offset])¶ Метод создает сущность ctypes, копируя буфер из буфера объекта source, который должен быть читаемым. Необязательный параметр offset задает смещение в исходном буфере в байтах; значение по умолчанию равно нулю. Если исходный буфер не достаточно большой, поднимается
ValueError
.Raises an auditing event
ctypes.cdata/buffer
with argumentspointer
,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, которых нет и не содержат указатели, теперь могут быть pickled.Сущности содержат один атрибут:
-
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
. Конструктор принимает дополнительный инициализатор float.
-
class
ctypes.
c_longdouble
¶ Представляет тип данных C
long double
. Конструктор принимает дополнительный инициализатор float. На платформах, гдеsizeof(long double) == sizeof(double)
это алиасc_double
.
-
class
ctypes.
c_float
¶ Представляет тип данных C
float
. Конструктор принимает дополнительный инициализатор float.
-
class
ctypes.
c_int
¶ Представляет тип данных C
signed int
. Конструктор принимает необязательный инициализатор целого числа; проверка переполнения не выполняется. На платформах, гдеsizeof(int) == sizeof(long)
это алиасc_long
.
-
class
ctypes.
c_int64
¶ Представляет 64-битный
signed int
тип данных C. Обычно алиас для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_uint64
¶ Представляет 64-битный
unsigned int
тип данных C. Обычно алиас для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
(более точно: c:type:_Bool от C99). Его значение может бытьTrue
илиFalse
, и конструктор принимает любой объект, имеющий истинное значение.
-
class
ctypes.
HRESULT
¶ Только для Windows: представляет значение
HRESULT
, которое содержит информацию об успехе или ошибке для вызова функции или метода.
-
class
ctypes.
py_object
¶ Представляет тип данных C
PyObject *
. Вызов этого без аргумента создает указательNULL
PyObject *
.
Модуль ctypes.wintypes
предоставляет некоторые другие Windows специфические типы
данных например HWND
:, c:type:WPARAM или DWORD
. Также определены
некоторые полезные структуры, например MSG
или RECT
.
Типы структурированных данных¶
-
class
ctypes.
Union
(*args, **kw)¶ Абстрактный базовый класс для объединений в родном порядке байтов.
-
class
ctypes.
BigEndianStructure
(*args, **kw)¶ Абстрактный базовый класс для структур в порядке байтов big endian.
-
class
ctypes.
LittleEndianStructure
(*args, **kw)¶ Абстрактный базовый класс для структур в порядке байтов little endian.
Структуры с неродным порядком байтов не могут содержать поля типа указателя или любые другие типы данных, содержащие поля типа указателя.
-
class
ctypes.
Structure
(*args, **kw)¶ Абстрактный базовый класс для структур в родном порядке байтов.
конкретные структуры и типы объединений должны создаваться подклассом одного из этих типов и, по крайней мере, определять переменную класса
_fields_
.ctypes
создаст дескриптор, которые позволяют читать и писать поля прямым доступом к атрибуту. Их перечень.-
_fields_
¶ Последовательность, определяющая поля структуры. Элементы должны быть 2-кортежами или 3-кортежами. Первый элемент - имя поля, второй элемент - тип поля; он может быть любым типом данных ctypes.
Для целочисленных полей, таких как
c_int
, может быть задан третий необязательный элемент. Он должен быть небольшим положительным целым числом, определяющее битовую ширину поля.Имена полей должны быть уникальными в рамках одной структуры или объединения. Он не выбирается, при повторении имен можно получить доступ только к одному полю.
Возможно определить переменную
_fields_
класса после class инструкции, которая определяет подкласс Structure, это позволяет создавать типы данных что прямо или косвенно ссылаются сами на себя:class List(Structure): pass List._fields_ = [("pnext", POINTER(List)), ... ]
Однако, переменная класса
_fields_
должна, быть определена до того, как тип вперые используется (создаётся экземпляр,sizeof()
вызывается на нем, и так далее). Более поздние присвоение переменной класса_fields_
будут поднимать AttributeError.Возможно определить под-подклассов типов структуры, они наследуют поля основного класса плюс
_fields_
, определенные в подподклассе, если таковые имеются.
-
_pack_
¶ Необязательное малое целое число, позволяющее переопределять выравнивание полей структуры в сущности.
_pack_
уже должен быть определен при присвоении_fields_
, в противном случае он не будет иметь эффекта.
-
_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
¶ Возвращает объект, на который указывает указатель. Присвоение этому атрибуту изменяет указатель, чтобы указать на назначенный объект.
-