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 — они на самом деле одного типа.

Доступ к функциям из загруженных 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
  1. Конструктор принимает любой объект с истинным значением.

Все эти типы могут быть созданы путем вызова их с помощью дополнительного инициализатора правильного типа и значения:

>>> 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

Поиск разделяемых библиотек

При программировании на компилируемом языке доступ к разделяемым библиотекам осуществляется при компиляции/линковке программы и при запуске программы.

Цель функции find_library() состоит в том, чтобы определить местонахождение библиотеки, в некотором роде подобной тому, что делают компилятор или загрузчик во время выполнения (на платформах с несколькими версиями общей библиотеки, должна быть загружена самая новая), в то время как ctypes загрузчики библиотеки действуют когда программа работает и вызов загрузчика во время непосредственного выполнения.

Модуль ctypes.util предоставляет функцию, которая может помочь определить загружаемую библиотеку.

ctypes.util.find_library(name)

Попытаться найти библиотеку и вернуть её путь. name - имя библиотеки без префикса типа lib, суффикса типа .so, .dylib или номера версии (это форма используется для опции posix линкера -l). Если не удается найти библиотеку, возвращает значение None.

Точная функциональность зависит от системы.

В Linux find_library() пытается запустить внешние программы (/sbin/ldconfig, gcc, objdump и ld), чтобы найти файл библиотеки. Возвращает имя файла библиотеки.

Изменено в версии 3.6: В Linux значение переменной среды LD_LIBRARY_PATH используется при поиске библиотек, если библиотека не может быть найдена каким-либо другим способом.

Далее несколько примеров:

>>> from ctypes.util import find_library
>>> find_library("m")
'libm.so.6'
>>> find_library("c")
'libc.so.6'
>>> find_library("bz2")
'libbz2.so.1.0'
>>>

На OS X, find_library() пытается проверить несколько предопределенных схем именования и путей, чтобы определить местонахождение библиотеки, и возвращает полное имя пути в случае успеха:

>>> from ctypes.util import find_library
>>> find_library("c")
'/usr/lib/libc.dylib'
>>> find_library("m")
'/usr/lib/libm.dylib'
>>> find_library("bz2")
'/usr/lib/libbz2.dylib'
>>> find_library("AGL")
'/System/Library/Frameworks/AGL.framework/AGL'
>>>

В Windows find_library() выполняет поиск по системному пути поиска и возвращает полный путь. Но так как нет предопределенной схемы именования, вызов подобного find_library("c") завершится неудачей и возвращает значение None.

Если обернуть разделяемую библиотеку с ctypes, это возможно лучше определения названия разделяемой библиотеки во время разработки и жёстко запрограммировать её в модуль обертки, вместо того, чтобы использовать find_library() для поиска местонахождения библиотеки во времени выполнения.

Загрузка общих библиотек

Существует несколько способов загрузки общих библиотек в процесс Python. Одним из способов является создание экземпляра одного из следующих классов:

class ctypes.CDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=0)

Сущности этого класса представляют загруженные общие библиотеки. Функции в этих библиотеках используют стандартное соглашение о вызове на языке C, и предполагается, что они возвращают int.

В Windows создание экземпляра CDLL может завершиться ошибкой, даже если имя DLL существует. Если зависимая DLL загруженной DLL не найдена, возникает ошибка OSError с сообщением «[WinError 126] Указанный модуль не может быть найден». Это сообщение об ошибке не содержит имя отсутствующей DLL, поскольку Windows API не возвращает эту информацию, что затрудняет диагностику этой ошибки. Чтобы устранить эту ошибку и определить, какая DLL не найдена, вам необходимо найти список зависимых DLL и определить, какая из них не найдена, с помощью средств отладки и трассировки Windows.

См.также

Инструмент от Microsoft DUMPBIN — Программа для поиска зависимостей DLL.

class ctypes.OleDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=0)

Только Windows: сущности из этого класса представляют загруженные общие библиотеки, функции в этих библиотеках используют соглашение о вызове stdcall, и предполагается, что возвращают определенные в Windows HRESULT код. Значения HRESULT содержат информацию, указывающую, завершился ли вызов функции провалом или успехом, а также дополнительные ошибки кода. Если возвращение, значение сигнализирует о неудаче, автоматически поднимается OSError.

Изменено в версии 3.3: Используется для поднятия исключения WindowsError.

class ctypes.WinDLL(name, mode=DEFAULT_MODE, handle=None, use_errno=False, use_last_error=False, winmode=0)

Только Windows: сущности этого класса представляют загруженные разделяемые библиотеки, функции в этих библиотеках используют соглашение о вызовах stdcall и предполагается, что по умолчанию возвращают int.

В Windows CE используется только стандартное соглашение о вызове, для удобства WinDLL и OleDLL также используют стандартное соглашение о вызове.

Python глобальная блокировка интерпретатора освобождается перед вызовом любой из экспортируемой этими библиотеками функцией, и повторно захватывается впоследствии.

class ctypes.PyDLL(name, mode=DEFAULT_MODE, handle=None)

Сущности этого класса ведут себя как CDLL экземпляры, за исключением того, что Python GIL не освобождается во время вызова функции, и после выполнения функции Python проверяет флаг ошибки. Если флаг ошибки установлен, поднимается Python исключение.

Таким образом, это полезно только для непосредственного вызова функций API Python C.

Все эти классы можно инстанцировать, вызвав их хотя бы с одним аргументом - pathname разделяемой библиотеки. Если у вас есть существующий обработчик в уже загруженной общей библиотеке, она может быть передана как именованный handle параметр, иначе функции основной платформы dlopen или LoadLibrary - используются, чтобы загрузить библиотеку в процесс и получить её обработчик.

Параметр mode используется для определения того, как библиотека загружается. Для получения дополнительной информации ознакомьтесь с man страницей dlopen(3). В Windows mode игнорируется. В системах posix всегда добавляется RTLD_NOW и не конфигурируется.

Параметр use_errno, когда установлен в true, включает ctypes механизм, который позволяет получать доступ к системному коду ошибки errno безопасным способом. ctypes ведет копию локального потока системной переменная errno; если вызвать внешнюю функцию, созданную с помощью use_errno=True, то errno значение перед вызовом функции заменяется частной копией ctypes, то же самое происходит сразу после вызова функции.

Функция ctypes.get_errno() возвращает значение ctypes частной копии и функция ctypes.set_errno(), изменяет ctypes частную копию нового значения и возвращает прежнее значение.

Параметр use_last_error, если установлен в значение true, включает тот же механизм для код ошибок Windows, которым управляют API функции GetLastError() и SetLastError() Windows; ctypes.get_last_error() и ctypes.set_last_error() используются для запроса и изменения частной копии ctypes ошибок windows кода.

Параметр winmode, используется на Windows, чтобы определить, как библиотека загружена (так как mode игнорируется). Он принимает любое значение, который действителен для Win32 API LoadLibraryEx параметра флагов. Если не указано, то по умолчанию используются флаги, которые приводят к наиболее безопасной загрузке DLL, чтобы избежать таких проблем, как похищение DLL. Передача полного пути к DLL является наиболее безопасным способом обеспечения правильной загрузки библиотеки и зависимостей.

Изменено в версии 3.8: Добавлен параметр winmode.

ctypes.RTLD_GLOBAL

Флаг, используемый в параметре mode. На платформах, где этот флаг недоступен, он определяется как целочисленный ноль.

ctypes.RTLD_LOCAL

Флаг, используемый в параметре mode. На платформах, где он недоступен, это то же самое, что и RTLD_GLOBAL.

ctypes.DEFAULT_MODE

Режим по умолчанию, который используется для загрузки разделяемой библиотеки. В OSX 10.3 это RTLD_GLOBAL, в противном случае оно совпадает с RTLD_LOCAL.

Сущности этих классов не содержат публичных методов. Функции, экспортируемые разделяемой библиотекой, могут быть доступны как атрибуты или по индексу. Обратите внимание, что при доступе к функции через атрибут, результат кэшируется и, следовательно, многократный доступ к нему возвращает один и тот же объект. С другой стороны, доступ к нему через индекс каждый раз возвращает новый объект:

>>> from ctypes import CDLL
>>> libc = CDLL("libc.so.6")  # На Linux
>>> libc.time == libc.time
True
>>> libc['time'] == libc['time']
False

Доступны следующие публичные атрибуты, их имя начинается с подчеркивания, чтобы не конфликтовать с экспортируемыми именами функций:

PyDLL._handle

Системный дескриптор используемый для доступа к библиотеке.

PyDLL._name

Имя библиотеки переданной в конструкторе.

Общие библиотеки также можно загрузить с помощью одного из префабричных объектов, являющихся сущностями класса LibraryLoader, либо путем вызова метода LoadLibrary(), либо путем извлечения библиотеки в качестве атрибут сущности загрузчика.

class ctypes.LibraryLoader(dlltype)

Класс, загружающий общие библиотеки. dlltype должен быть одним из типов CDLL, PyDLL, WinDLL или OleDLL.

__getattr__() содержит особое поведение: он позволяет загружать общую библиотеку, обращаясь к ней как к атрибуту сущности загрузчика библиотеки. Результат кэшируется, таким образом, повторный доступ к атрибуту возвращает каждый раз ту же библиотеку .

LoadLibrary(name)

Загрузит общую библиотеку в процесс и возвратит её. Этот метод всегда возвращает новую сущность библиотеки.

Также доступны префабричные библиотеки загрузчиков:

ctypes.cdll

Создает CDLL сущности.

ctypes.windll

Только Windows: создает WinDLL сущности.

ctypes.oledll

Только Windows: создает OleDLL сущности.

ctypes.pydll

Создает PyDLL сущности.

Для непосредственного доступа к C api Python, доступен готовый к использованию разделяемый Python объект библиотеки:

ctypes.pythonapi

Cущность PyDLL, которая выставляет функции Python C API как атрибуты. Обратите внимание, что все эти функции возвращают C int, что, конечно, не всегда является истиной, поэтому для использования этих функций необходимо назначить правильный restype атрибут.

Загрузка библиотеки через любой из этих объектов вызывает событие аудита ctypes.dlopen с строковым аргументом name, именем используемым для загрузки библиотеки.

Обращение к функции загруженной библиотеки вызывает событие аудита ctypes.dlsym с аргументами library (объект библиотеки) и name (символическое имя как строка или целое число).

В случаях, когда доступен только обработчик библиотеки, а не объект, доступ к функции вызывает событие аудита ctypes.dlsym/handle с аргументами handle (сырой обработчик библиотеки) и name.

Внешние функции

Как пояснялось в предыдущем разделе, внешние функции могут быть доступны как атрибуты загруженных разделяемых библиотек. Объекты функции, созданные таким образом, по умолчанию принимают любое количество аргументов, принимают любые данные 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 argument obj.

ctypes.alignment(obj_or_type)

Возвращает требования к выравниванию типа ctypes. obj_or_type должен быть типом ctypes или сущностью.

ctypes.byref(obj[, offset])

Возвращает облегченный указатель на obj, который должен быть сущностью типа ctypes. offset по умолчанию равен нулю и должен быть целым числом, которое будет добавлено к внутреннему указателю значения.

byref(obj, offset) соответствует этому коду C:

(((char *)&obj) + offset)

Возвращаемый объект может быть использован только как параметр вызова внешней функции. Ведет себя подобно pointer(obj), но строительство идет намного быстрее.

ctypes.cast(obj, type)

Эта функция подобна оператору cast в C. Она возвращает новую сущность type, которая указывает на тот же блок памяти как obj. type должен быть типом указателя, а obj должен быть объектом, который может быть интерпретирован как указатель.

ctypes.create_string_buffer(init_or_size, size=None)

Эта функция создает изменяемый символьный буфер. Возвращаемый объект является массивом ctypes c_char.

init_or_size должен быть целым числом, которое определяет размер массива или объект байтов, который будет использован для инициализации элементов массива.

Если байтовый объект указан в качестве первого аргумента, буфер становится на один элемент больше, чем его длина, так что последним элементом в массиве является завершающий символ NUL. Целое число может быть передано в качестве второго аргумента, позволяющего указать размер массива, если длина байтов не должна быть используема.

Raises an auditing event ctypes.create_string_buffer with arguments init, 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 arguments init, 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 argument errno.

ctypes.set_last_error(value)

Только Windows: установить текущий значение ctypes-приватной копии системной переменной LastError в вызываемом потоке в value и возвратить предыдущий значение.

Raises an auditing event ctypes.set_last_error with argument error.

ctypes.sizeof(obj_or_type)

Возвращает размер в байтах типа ctypes или сущности буфера памяти. Выполняет то же как и оператор C sizeof.

ctypes.string_at(address, size=-1)

Эта функция возвращает C строку, начиная с адреса памяти address как байтовый объект. Если указан размер, он используется как размер, в противном случае предполагается, что строка заканчивается нулем.

Raises an auditing event ctypes.string_at with arguments address, 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 arguments address, 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 arguments pointer, size, offset.

from_buffer_copy(source[, offset])

Метод создает сущность ctypes, копируя буфер из буфера объекта source, который должен быть читаемым. Необязательный параметр offset задает смещение в исходном буфере в байтах; значение по умолчанию равно нулю. Если исходный буфер не достаточно большой, поднимается ValueError.

Raises an auditing event ctypes.cdata/buffer with arguments 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, которых нет и не содержат указатели, теперь могут быть 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_int8

Представляет 8-битный signed int тип данных C. Обычно алиас для c_byte.

class ctypes.c_int16

Представляет 16-битный signed int тип данных C. Обычно алиас для c_short.

class ctypes.c_int32

Представляет 32-битный signed int тип данных C. Обычно алиас для c_int.

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_uint8

Представляет 8-битный unsigned int тип данных C. Обычно алиас для c_ubyte.

class ctypes.c_uint16

Представляет 16-битный unsigned int тип данных C. Обычно алиас для c_ushort.

class ctypes.c_uint32

Представляет 32-битный unsigned int тип данных C. Обычно алиас для c_uint.

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 *. Вызов этого без аргумента создает указатель NULLPyObject *.

Модуль 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

Возвращает объект, на который указывает указатель. Присвоение этому атрибуту изменяет указатель, чтобы указать на назначенный объект.