Spec-Zone.ru › Python 3.13

ctypes — Библиотека внешних функций для Python

Исходный код: Lib/ctypes

ctypes — это библиотека внешних функций для Python. Она предоставляет C-совместимые типы данных и позволяет вызывать функции в DLL или разделяемых библиотеках. Её можно использовать для создания обёрток таких библиотек на чистом Python.

Учебное руководство по ctypes

Примечание. В примерах кода этого руководства используется doctest для проверки их работоспособности. Поскольку некоторые примеры кода ведут себя по-разному в Linux, Windows или macOS, они содержат директивы doctest в комментариях.

Примечание. В некоторых примерах кода используется тип ctypes c_int. На платформах, где sizeof(long) == sizeof(int), он является псевдонимом для c_long. Поэтому не удивляйтесь, если вместо ожидаемого c_int выводится c_long — на самом деле это один и тот же тип.

Загрузка библиотек динамической компоновки

ctypes экспортирует объекты cdll, а в Windows — windll и oledll, для загрузки библиотек динамической компоновки.

Загрузка библиотек осуществляется путём обращения к ним как к атрибутам этих объектов. cdll загружает библиотеки, экспортирующие функции со стандартным соглашением о вызовах cdecl, а windll — библиотеки с соглашением о вызовах stdcall. oledll также использует соглашение о вызовах stdcall и предполагает, что функции возвращают код ошибки Windows HRESULT. Код ошибки используется для автоматического вызова исключения OSError в случае сбоя вызова функции.

Изменено в версии 3.3: Ошибки Windows ранее вызывали исключение WindowsError, которое теперь является псевдонимом для OSError.

Вот несколько примеров для Windows. Обратите внимание, что msvcrt — это стандартная библиотека C от Microsoft, содержащая большинство стандартных функций C, и она использует соглашение о вызовах cdecl:

>>> from ctypes import *
>>> print(windll.kernel32)
<WinDLL 'kernel32', handle ... at ...>
>>> print(cdll.msvcrt)
<CDLL 'msvcrt', handle ... at ...>
>>> libc = cdll.msvcrt
>>>

Windows автоматически добавляет обычное расширение файла .dll.

Примечание

Доступ к стандартной библиотеке C через cdll.msvcrt приведет к использованию устаревшей версии библиотеки, которая может быть несовместима с версией, используемой Python. По возможности используйте встроенную функциональность Python или импортируйте и используйте модуль msvcrt.

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

>>> libc.printf
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.GetModuleHandleA)
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.MyOwnFunction)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "ctypes.py", line 239, in __getattr__
    func = _StdcallFuncPtr(name, self)
AttributeError: function 'MyOwnFunction' not found
>>>

Обратите внимание, что системные DLL win32, такие как kernel32 и user32, часто экспортируют как ANSI-, так и UNICODE-версии функции. Версия UNICODE экспортируется с добавлением суффикса W к имени, а версия ANSI — с суффиксом A. Функция win32 GetModuleHandle, возвращающая дескриптор модуля по заданному имени модуля, имеет следующий C-прототип, а макрос используется для предоставления одной из них под именем GetModuleHandle в зависимости от того, определен ли макрос UNICODE:

/* ANSI version */
HMODULE GetModuleHandleA(LPCSTR lpModuleName);
/* UNICODE version */
HMODULE GetModuleHandleW(LPCWSTR lpModuleName);

windll не пытается автоматически выбрать одну из них: вам необходимо обращаться к нужной версии, явно указывая GetModuleHandleA или GetModuleHandleW, а затем вызывать её с объектами bytes или string соответственно.

Иногда DLL экспортируют функции с именами, которые не являются допустимыми идентификаторами Python, например "??2@YAPAXI@Z". В этом случае для получения функции необходимо использовать getattr():

>>> getattr(cdll.msvcrt, "??2@YAPAXI@Z")
<_FuncPtr object at 0x...>
>>>

В Windows некоторые DLL экспортируют функции не по имени, а по порядковому номеру (ординалу). Доступ к таким функциям можно получить путем индексирования объекта DLL порядковым номером:

>>> cdll.kernel32[1]
<_FuncPtr object at 0x...>
>>> cdll.kernel32[0]
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "ctypes.py", line 310, in __getitem__
    func = _StdcallFuncPtr(name, self)
AttributeError: function ordinal 0 not found
>>>

Вызов функций

Эти функции можно вызывать как любой другой вызываемый объект Python. В этом примере используется функция rand(), которая не принимает аргументов и возвращает псевдослучайное целое число:

>>> print(libc.rand())
1804289383

В Windows можно вызвать функцию GetModuleHandleA(), которая возвращает дескриптор модуля win32 (передавая None в качестве единственного аргумента для вызова с указателем NULL):

>>> print(hex(windll.kernel32.GetModuleHandleA(None)))
0x1d000000
>>>

ValueError возникает при вызове функции stdcall с соглашением о вызовах cdecl или наоборот:

>>> cdll.kernel32.GetModuleHandleA(None)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with not enough arguments (4 bytes missing)
>>>

>>> windll.msvcrt.printf(b"spam")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with too many arguments (4 bytes in excess)
>>>

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

В Windows ctypes использует структурированную обработку исключений win32 для предотвращения аварийного завершения работы из-за ошибок общей защиты при вызове функций с недопустимыми значениями аргументов:

>>> windll.kernel32.GetModuleHandleA(32)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
OSError: exception: access violation reading 0x00000020
>>>

Тем не менее, существует достаточно способов вызвать сбой Python с помощью ctypes, поэтому в любом случае следует соблюдать осторожность. Модуль faulthandler может быть полезен при отладке аварийных завершений (например, сбоев сегментации, вызванных ошибочными вызовами библиотек C).

None, целые числа, объекты bytes и (юникод-)строки — единственные встроенные объекты Python, которые можно напрямую использовать в качестве параметров в этих вызовах функций. None передается как указатель C NULL, объекты bytes и строки передаются как указатель на блок памяти, содержащий их данные (char* или wchar_t*). Целые числа Python передаются как используемый платформой по умолчанию тип C int, их значение маскируется в соответствии с размером типа C.

Прежде чем перейти к вызову функций с другими типами параметров, необходимо подробнее изучить типы данных ctypes.

Фундаментальные типы данных

ctypes определяет ряд примитивных C-совместимых типов данных:

Тип ctypes

Тип C

Тип Python

c_bool

_Bool

bool (1)

c_char

char

1-символьный объект bytes

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_int8

int8_t

int

c_int16

int16_t

int

c_int32

int32_t

int

c_int64

int64_t

int

c_uint

unsigned int

int

c_uint8

uint8_t

int

c_uint16

uint16_t

int

c_uint32

uint32_t

int

c_uint64

uint64_t

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_time_t

time_t

int

c_float

float

float

c_double

double

float

c_longdouble

long double

float

c_char_p

char* (NUL terminated)

объект bytes или None

c_wchar_p

wchar_t* (NUL terminated)

строка или 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)              # the memory location has changed
c_wchar_p(139966783348904)
>>> print(c_s.value)
Hi, there
>>> print(s)                # first object is unchanged
Hello, World
>>>

Однако следует соблюдать осторожность и не передавать их в функции, ожидающие указатели на изменяемую память. Если вам требуются изменяемые блоки памяти, в ctypes есть функция create_string_buffer(), которая создает их различными способами. Доступ к текущему содержимому блока памяти (или его изменение) можно получить с помощью свойства raw; если вы хотите обращаться к нему как к NUL-терминированной строке, используйте свойство value:

>>> from ctypes import *
>>> p = create_string_buffer(3)            # create a 3 byte buffer, initialized to NUL bytes
>>> print(sizeof(p), repr(p.raw))
3 b'\x00\x00\x00'
>>> p = create_string_buffer(b"Hello")     # create a buffer containing a NUL terminated string
>>> print(sizeof(p), repr(p.raw))
6 b'Hello\x00'
>>> print(repr(p.value))
b'Hello'
>>> p = create_string_buffer(b"Hello", 10) # create a 10 byte buffer
>>> print(sizeof(p), repr(p.raw))
10 b'Hello\x00\x00\x00\x00\x00'
>>> p.value = b"Hi"
>>> print(sizeof(p), repr(p.raw))
10 b'Hi\x00lo\x00\x00\x00\x00\x00'
>>>

Функция create_string_buffer() заменяет старую функцию c_buffer() (которая все еще доступна как псевдоним). Для создания изменяемого блока памяти, содержащего символы Unicode типа C wchar_t, используйте функцию create_unicode_buffer().

Вызов функций, продолжение

Обратите внимание, что printf выводит данные в реальный стандартный канал вывода, а не в sys.stdout, поэтому эти примеры будут работать только в командной строке консоли, но не внутри IDLE или PythonWin:

>>> printf = libc.printf
>>> printf(b"Hello, %s\n", b"World!")
Hello, World!
14
>>> printf(b"Hello, %S\n", "World!")
Hello, World!
14
>>> printf(b"%d bottles of beer\n", 42)
42 bottles of beer
19
>>> printf(b"%f bottles of beer\n", 42.5)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ctypes.ArgumentError: argument 2: TypeError: Don't know how to convert parameter 2
>>>

Как уже упоминалось ранее, все типы Python, кроме целых чисел, строк и объектов bytes, должны быть обёрнуты в соответствующий тип ctypes, чтобы их можно было преобразовать в требуемый тип данных C:

>>> printf(b"An int %d, a double %f\n", 1234, c_double(3.14))
An int 1234, a double 3.140000
31
>>>

Вызов функций с переменным числом аргументов

На многих платформах вызов функций с переменным числом аргументов через ctypes ничем не отличается от вызова функций с фиксированным числом параметров. На некоторых платформах, и в частности на ARM64 для платформ Apple, соглашение о вызовах для функций с переменным числом аргументов отличается от соглашения для обычных функций.

На этих платформах необходимо указывать атрибут argtypes для обычных, невариативных аргументов функции:

libc.printf.argtypes = [ctypes.c_char_p]

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

Вызов функций с собственными типами данных

Вы также можете настроить преобразование аргументов ctypes, чтобы использовать экземпляры собственных классов в качестве аргументов функций. ctypes ищет атрибут _as_parameter_ и использует его в качестве аргумента функции. Значением атрибута должно быть целое число, строка, bytes, экземпляр ctypes или объект с атрибутом _as_parameter_:

>>> class Bottles:
...     def __init__(self, number):
...         self._as_parameter_ = number
...
>>> bottles = Bottles(42)
>>> printf(b"%d bottles of beer\n", bottles)
42 bottles of beer
19
>>>

Если вы не хотите хранить данные экземпляра в переменной экземпляра _as_parameter_, можно определить property, которое делает этот атрибут доступным по запросу.

Указание требуемых типов аргументов (прототипы функций)

Можно указать требуемые типы аргументов функций, экспортируемых из DLL, установив атрибут argtypes.

argtypes должен быть последовательностью типов данных C (функция printf(), вероятно, не лучший пример, поскольку она принимает переменное число и различные типы параметров в зависимости от строки формата, но с другой стороны, это очень удобно для экспериментов с этой возможностью):

>>> printf.argtypes = [c_char_p, c_char_p, c_int, c_double]
>>> printf(b"String '%s', Int %d, Double %f\n", b"Hi", 10, 2.2)
String 'Hi', Int 10, Double 2.200000
37
>>>

Указание формата защищает от несовместимых типов аргументов (так же, как прототип для функции C) и пытается преобразовать аргументы в допустимые типы:

>>> printf(b"%d %d %d", 1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ctypes.ArgumentError: argument 2: TypeError: 'int' object cannot be interpreted as ctypes.c_char_p
>>> printf(b"%s %d %f\n", b"X", 2, 3)
X 2 3.000000
13
>>>

Если вы определили собственные классы, которые передаете в вызовы функций, вам необходимо реализовать для них метод класса from_param(), чтобы их можно было использовать в последовательности argtypes. Метод класса from_param() получает объект Python, переданный в вызов функции, должен выполнить проверку типа или другие необходимые действия, чтобы убедиться, что этот объект допустим, а затем вернуть сам объект, его атрибут _as_parameter_ или то, что вы хотите передать в качестве аргумента функции C в данном случае. Опять же, результат должен быть целым числом, строкой, bytes, экземпляром ctypes или объектом с атрибутом _as_parameter_.

Возвращаемые типы

По умолчанию предполагается, что функции возвращают тип C int. Другие возвращаемые типы можно указать, задав атрибут restype объекта функции.

C-прототип функции time() — time_t time(time_t *). Поскольку тип time_t может отличаться от возвращаемого по умолчанию типа int, следует указать атрибут restype:

>>> libc.time.restype = c_time_t

Типы аргументов можно указать с помощью argtypes:

>>> libc.time.argtypes = (POINTER(c_time_t),)

Чтобы вызвать функцию с указателем NULL в качестве первого аргумента, используйте None:

>>> print(libc.time(None))
1150640792

Вот более сложный пример: в нем используется функция strchr(), которая ожидает указатель на строку и char, а возвращает указатель на строку:

>>> strchr = libc.strchr
>>> strchr(b"abcdef", ord("d"))
8059983
>>> strchr.restype = c_char_p    # c_char_p is a pointer to a string
>>> strchr(b"abcdef", ord("d"))
b'def'
>>> print(strchr(b"abcdef", ord("x")))
None
>>>

Если вы хотите избежать приведенных выше вызовов ord("x"), можно задать атрибут argtypes, и второй аргумент будет преобразован из односимвольного объекта bytes Python в C char:

>>> strchr.restype = c_char_p
>>> strchr.argtypes = [c_char_p, c_char]
>>> strchr(b"abcdef", b"d")
b'def'
>>> strchr(b"abcdef", b"def")
Traceback (most recent call last):
ctypes.ArgumentError: argument 2: TypeError: one character bytes, bytearray or integer expected
>>> print(strchr(b"abcdef", b"x"))
None
>>> strchr(b"abcdef", b"d")
b'def'
>>>

Вы также можете использовать вызываемый объект Python (например, функцию или класс) в качестве значения атрибута restype, если внешняя функция возвращает целое число. Вызываемый объект будет вызван с целым числом, возвращенным функцией C, и результат этого вызова будет использован в качестве результата вызова вашей функции. Это удобно для проверки возвращаемых значений ошибок и автоматического вызова исключения:

>>> GetModuleHandle = windll.kernel32.GetModuleHandleA
>>> def ValidHandle(value):
...     if value == 0:
...         raise WinError()
...     return value
...
>>>
>>> GetModuleHandle.restype = ValidHandle
>>> GetModuleHandle(None)
486539264
>>> GetModuleHandle("something silly")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 3, in ValidHandle
OSError: [Errno 126] The specified module could not be found.
>>>

WinError — это функция, которая вызывает API Windows FormatMessage() для получения строкового представления кода ошибки и возвращает исключение. WinError принимает необязательный параметр кода ошибки; если он не передан, вызывается GetLastError() для его получения.

Обратите внимание, что гораздо более мощный механизм проверки ошибок доступен через атрибут errcheck; подробности см. в справочном руководстве.

Передача указателей (или: передача параметров по ссылке)

Иногда функция C API ожидает указатель на тип данных в качестве параметра, например, для записи в соответствующую область памяти или если данные слишком велики для передачи по значению. Это также известно как передача параметров по ссылке.

ctypes экспортирует функцию byref(), которая используется для передачи параметров по ссылке. Того же эффекта можно достичь с помощью функции pointer(), хотя pointer() выполняет значительно больше работы, поскольку конструирует настоящий объект указателя, поэтому быстрее использовать byref(), если сам объект указателя в Python вам не нужен:

>>> i = c_int()
>>> f = c_float()
>>> s = create_string_buffer(b'\000' * 32)
>>> print(i.value, f.value, repr(s.value))
0 0.0 b''
>>> libc.sscanf(b"1 3.14 Hello", b"%d %f %s",
...             byref(i), byref(f), s)
3
>>> print(i.value, f.value, repr(s.value))
1 3.1400001049 b'Hello'
>>>

Структуры и объединения

Структуры и объединения должны наследоваться от базовых классов Structure и Union, определенных в модуле ctypes. Каждый подкласс должен определять атрибут _fields_. _fields_ должен быть списком кортежей из двух элементов, содержащих имя поля и тип поля.

Тип поля должен быть типом ctypes, таким как c_int, или любым другим производным типом ctypes: структура, объединение, массив, указатель.

Вот простой пример структуры POINT, содержащей два целых числа с именами x и y, который также показывает, как инициализировать структуру в конструкторе:

>>> from ctypes import *
>>> class POINT(Structure):
...     _fields_ = [("x", c_int),
...                 ("y", c_int)]
...
>>> point = POINT(10, 20)
>>> print(point.x, point.y)
10 20
>>> point = POINT(y=5)
>>> print(point.x, point.y)
0 5
>>> POINT(1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: too many initializers
>>>

Однако вы можете создавать гораздо более сложные структуры. Структура может сама содержать другие структуры, если использовать структуру в качестве типа поля.

Вот структура RECT, содержащая две структуры POINT с именами upperleft и lowerright:

>>> class RECT(Structure):
...     _fields_ = [("upperleft", POINT),
...                 ("lowerright", POINT)]
...
>>> rc = RECT(point)
>>> print(rc.upperleft.x, rc.upperleft.y)
0 5
>>> print(rc.lowerright.x, rc.lowerright.y)
0 0
>>>

Вложенные структуры также можно инициализировать в конструкторе несколькими способами:

>>> r = RECT(POINT(1, 2), POINT(3, 4))
>>> r = RECT((1, 2), (3, 4))

Дескрипторы полей можно получить из класса; они полезны при отладке, поскольку могут предоставить полезную информацию:

>>> print(POINT.x)
<Field type=c_long, ofs=0, size=4>
>>> print(POINT.y)
<Field type=c_long, ofs=4, size=4>
>>>

Предупреждение

ctypes не поддерживает передачу объединений или структур с битовыми полями в функции по значению. Хотя это может работать на 32-битной архитектуре x86, библиотека не гарантирует работоспособность в общем случае. Объединения и структуры с битовыми полями всегда следует передавать в функции по указателю.

Выравнивание структур/объединений и порядок байтов

По умолчанию поля Structure и Union выравниваются так же, как это делает компилятор C. Это поведение можно переопределить, указав атрибут класса _pack_ в определении подкласса. Он должен быть положительным целым числом и задает максимальное выравнивание для полей. То же самое делает #pragma pack(n) в MSVC. Также можно задать минимальное выравнивание для упаковки самого подкласса аналогично тому, как работает #pragma align(n) в MSVC. Этого можно достичь, указав атрибут класса _align_ в определении подкласса.

ctypes использует нативный порядок байтов для Structures и Unions. Для создания структур с ненативным порядком байтов можно использовать один из базовых классов: 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 атрибуту contents указателя приведет к тому, что указатель будет указывать на область памяти, где он хранится:

>>> i = c_int(99)
>>> pi.contents = i
>>> pi.contents
c_long(99)
>>>

Экземпляры указателей также можно индексировать целыми числами:

>>> pi[0]
99
>>>

Присваивание по целочисленному индексу изменяет значение, на которое указывает указатель:

>>> print(i)
c_long(99)
>>> pi[0] = 22
>>> print(i)
c_long(22)
>>>

Также можно использовать индексы, отличные от 0, но вы должны понимать, что делаете, точно так же, как в C: вы можете получить доступ или изменить произвольные области памяти. Обычно эта возможность используется только в том случае, если вы получаете указатель из функции C и знаете, что указатель на самом деле указывает на массив, а не на отдельный элемент.

Под капотом функция pointer() делает больше, чем просто создание экземпляров указателей: сначала она должна создать типы указателей. Это делается с помощью функции POINTER(), которая принимает любой тип ctypes и возвращает новый тип:

>>> PI = POINTER(c_int)
>>> PI
<class 'ctypes.LP_c_long'>
>>> PI(42)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: expected c_long instead of int
>>> PI(c_int(42))
<ctypes.LP_c_long object at 0x...>
>>>

Вызов типа указателя без аргументов создает указатель NULL. Указатели NULL имеют логическое значение False:

>>> null_ptr = POINTER(c_int)()
>>> print(bool(null_ptr))
False
>>>

ctypes проверяет на NULL при разыменовании указателей (однако разыменование недействительных указателей, отличных от NULL, приведет к аварийному завершению Python):

>>> null_ptr[0]
Traceback (most recent call last):
    ....
ValueError: NULL pointer access
>>>

>>> null_ptr[0] = 1234
Traceback (most recent call last):
    ....
ValueError: NULL pointer access
>>>

Преобразования типов

Обычно ctypes выполняет строгую проверку типов. Это означает, что если у вас есть POINTER(c_int) в списке argtypes функции или в качестве типа поля в определении структуры, принимаются только экземпляры точно такого же типа. Из этого правила есть некоторые исключения, когда ctypes принимает другие объекты. Например, вместо типов указателей можно передавать совместимые экземпляры массивов. Так, для POINTER(c_int) ctypes принимает массив c_int:

>>> class Bar(Structure):
...     _fields_ = [("count", c_int), ("values", POINTER(c_int))]
...
>>> bar = Bar()
>>> bar.values = (c_int * 3)(1, 2, 3)
>>> bar.count = 3
>>> for i in range(bar.count):
...     print(bar.values[i])
...
1
2
3
>>>

Кроме того, если аргумент функции явно объявлен как тип указателя (например, POINTER(c_int)) в argtypes, функции можно передать объект типа, на который указывает указатель (в данном случае c_int). В этом случае ctypes автоматически применит необходимое преобразование byref().

Чтобы установить поле типа POINTER в NULL, можно присвоить None:

>>> bar.values = None
>>>

Иногда встречаются экземпляры несовместимых типов. В C можно привести один тип к другому типу. ctypes предоставляет функцию cast(), которую можно использовать аналогичным образом. Структура Bar, определенная выше, принимает указатели POINTER(c_int) или массивы c_int для своего поля values, но не экземпляры других типов:

>>> bar.values = (c_byte * 4)()
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
TypeError: incompatible types, c_byte_Array_4 instance instead of LP_c_long instance
>>>

Для таких случаев удобна функция cast().

Функцию cast() можно использовать для приведения экземпляра ctypes к указателю на другой тип данных ctypes. Функция cast() принимает два параметра: объект ctypes, который является указателем какого-либо вида или может быть преобразован в него, и тип указателя ctypes. Она возвращает экземпляр второго аргумента, который ссылается на тот же блок памяти, что и первый аргумент:

>>> a = (c_byte * 4)()
>>> cast(a, POINTER(c_int))
<ctypes.LP_c_long object at ...>
>>>

Таким образом, cast() можно использовать для присваивания полю values структуры Bar:

>>> bar = Bar()
>>> bar.values = cast((c_byte * 4)(), POINTER(c_int))
>>> print(bar.values[0])
0
>>>

Неполные типы

Неполные типы — это структуры, объединения или массивы, члены которых еще не определены. В C они задаются опережающими объявлениями (forward declarations), которые определяются позже:

struct cell; /* forward declaration */

struct cell {
    char *name;
    struct cell *next;
};

Прямой перевод в код ctypes выглядел бы следующим образом, но это не работает:

>>> class cell(Structure):
...     _fields_ = [("name", c_char_p),
...                 ("next", POINTER(cell))]
...
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "<stdin>", line 2, in cell
NameError: name 'cell' is not defined
>>>

поскольку новый class cell недоступен внутри самой инструкции 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 = b"foo"
>>> c2 = cell()
>>> c2.name = b"bar"
>>> c1.next = pointer(c2)
>>> c2.next = pointer(c1)
>>> p = c1
>>> for i in range(8):
...     print(p.name, end=" ")
...     p = p.next[0]
...
foo bar foo bar foo bar foo bar
>>>

Функции обратного вызова

ctypes позволяет создавать вызываемые из C указатели на функции из вызываемых объектов Python. Их иногда называют функциями обратного вызова (callback functions).

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

Фабричная функция CFUNCTYPE() создает типы для функций обратного вызова, использующих соглашение о вызовах cdecl. В Windows фабричная функция WINFUNCTYPE() создает типы для функций обратного вызова, использующих соглашение о вызовах stdcall.

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

Здесь мы приведем пример, использующий функцию qsort() стандартной библиотеки C, которая применяется для сортировки элементов с помощью функции обратного вызова. Функция qsort() будет использоваться для сортировки массива целых чисел:

>>> IntArray5 = c_int * 5
>>> ia = IntArray5(5, 1, 7, 33, 99)
>>> qsort = libc.qsort
>>> qsort.restype = None
>>>

Функция qsort() должна вызываться с указателем на сортируемые данные, количеством элементов в массиве данных, размером одного элемента и указателем на функцию сравнения (callback). Затем функция обратного вызова будет вызываться с двумя указателями на элементы и должна возвращать отрицательное целое число, если первый элемент меньше второго, ноль, если они равны, и положительное целое число в противном случае.

Таким образом, наша функция обратного вызова принимает указатели на целые числа и должна возвращать целое число. Сначала мы создаем type для функции обратного вызова:

>>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
>>>

Для начала вот простой callback, который отображает переданные ему значения:

>>> 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 этого не делает, и если вы этого не сделаете, они могут быть удалены сборщиком мусора, что приведет к аварийному завершению программы при вызове callback-функции.

Также обратите внимание, что если функция обратного вызова вызывается в потоке, созданном вне контроля Python (например, внешним кодом, вызывающим callback), ctypes создает новый фиктивный поток Python при каждом вызове. Такое поведение корректно для большинства целей, но это означает, что значения, сохраненные с помощью threading.local, не сохранятся между различными обратными вызовами, даже если эти вызовы выполняются из одного и того же потока C.

Доступ к значениям, экспортируемым из библиотек DLL

Некоторые разделяемые библиотеки экспортируют не только функции, но и переменные. Примером в самой библиотеке Python является Py_Version — номер версии среды выполнения Python, закодированный в виде одной целочисленной константы.

ctypes может обращаться к таким значениям с помощью методов класса in_dll() типа. pythonapi — это предопределенный символ, предоставляющий доступ к Python C API:

>>> version = ctypes.c_int.in_dll(ctypes.pythonapi, "Py_Version")
>>> print(hex(version.value))
0x30c00a0

Более развернутый пример, также демонстрирующий использование указателей, обращается к указателю PyImport_FrozenModules, экспортируемому Python.

Цитата из документации для этого значения:

Этот указатель инициализируется для указания на массив записей _frozen, оканчивающийся записью, все члены которой равны NULL или нулю. При импорте замороженного модуля (frozen module) поиск происходит в этой таблице. Сторонний код может использовать это для предоставления динамически создаваемой коллекции замороженных модулей.

Поэтому манипулирование этим указателем может даже оказаться полезным. Чтобы ограничить размер примера, покажем только, как эту таблицу можно прочитать с помощью ctypes:

>>> from ctypes import *
>>>
>>> class struct_frozen(Structure):
...     _fields_ = [("name", c_char_p),
...                 ("code", POINTER(c_ubyte)),
...                 ("size", c_int),
...                 ("get_code", POINTER(c_ubyte)),  # Function pointer
...                ]
...
>>>

Мы определили тип данных _frozen, поэтому можем получить указатель на таблицу:

>>> FrozenTable = POINTER(struct_frozen)
>>> table = FrozenTable.in_dll(pythonapi, "_PyImport_FrozenBootstrap")
>>>

Поскольку table является pointer на массив записей struct_frozen, мы можем итерироваться по нему, но нам просто нужно убедиться, что наш цикл завершается, поскольку у указателей нет размера. Рано или поздно программа, вероятно, завершилась бы аварийно с ошибкой нарушения прав доступа (access violation) или подобной, поэтому лучше прервать цикл при достижении записи NULL:

>>> for item in table:
...     if item.name is None:
...         break
...     print(item.name.decode("ascii"), item.size)
...
_frozen_importlib 31764
_frozen_importlib_external 41499
zipimport 12345
>>>

Тот факт, что стандартный Python имеет замороженный модуль и замороженный пакет (обозначается отрицательным значением члена size), малоизвестен; это используется только для тестирования. Попробуйте, например, выполнить import __hello__.

Неожиданности

В ctypes есть некоторые тонкости, где поведение может отличаться от ожидаемого.

Рассмотрим следующий пример:

>>> from ctypes import *
>>> class POINT(Structure):
...     _fields_ = ("x", c_int), ("y", c_int)
...
>>> class RECT(Structure):
...     _fields_ = ("a", POINT), ("b", POINT)
...
>>> p1 = POINT(1, 2)
>>> p2 = POINT(3, 4)
>>> rc = RECT(p1, p2)
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
1 2 3 4
>>> # now swap the two points
>>> rc.a, rc.b = rc.b, rc.a
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
3 4 3 4
>>>

Хм. Мы определенно ожидали, что последняя инструкция выведет 3 4 1 2. Что произошло? Вот шаги выполнения строки rc.a, rc.b = rc.b, rc.a выше:

>>> temp0, temp1 = rc.b, rc.a
>>> rc.a = temp0
>>> rc.b = temp1
>>>

Обратите внимание, что temp0 и temp1 — это объекты, которые все еще используют внутренний буфер объекта rc выше. Поэтому выполнение rc.a = temp0 копирует содержимое буфера temp0 в буфер rc. Это, в свою очередь, изменяет содержимое temp1. Таким образом, последнее присваивание rc.b = temp1 не приводит к ожидаемому результату.

Имейте в виду, что извлечение подобъектов из 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 в блоке памяти не сохраняет сам объект — вместо этого сохраняется contents объекта. Повторный доступ к содержимому каждый раз создает новый объект Python!

Типы данных переменного размера

ctypes обеспечивает некоторую поддержку массивов и структур переменного размера.

Функция resize() может использоваться для изменения размера буфера памяти существующего объекта ctypes. Функция принимает объект в качестве первого аргумента и запрошенный размер в байтах в качестве второго аргумента. Блок памяти нельзя сделать меньше естественного блока памяти, заданного типом объекта; при попытке сделать это возникает исключение ValueError:

>>> short_array = (c_short * 4)()
>>> print(sizeof(short_array))
8
>>> resize(short_array, 4)
Traceback (most recent call last):
    ...
ValueError: minimum size is 8
>>> resize(short_array, 32)
>>> sizeof(short_array)
32
>>> sizeof(type(short_array))
8
>>>

Все это прекрасно, но как получить доступ к дополнительным элементам, содержащимся в этом массиве? Поскольку тип по-прежнему знает только о 4 элементах, при обращении к другим элементам возникают ошибки:

>>> short_array[:]
[0, 0, 0, 0]
>>> short_array[7]
Traceback (most recent call last):
    ...
IndexError: invalid index
>>>

Другой способ использования типов данных переменного размера в ctypes — воспользоваться динамической природой Python и (пере)определять тип данных для каждого отдельного случая, когда требуемый размер уже известен.

Справочник ctypes

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

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

Назначение функции 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'
>>>

В macOS и Android 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=None)

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

В Windows создание экземпляра CDLL может завершиться ошибкой, даже если имя DLL существует. Если зависимая DLL загружаемой DLL не найдена, возникает ошибка OSError с сообщением “[WinError 126] The specified module could not be found”. Это сообщение об ошибке не содержит имени отсутствующей DLL, поскольку Windows API не возвращает эту информацию, что затрудняет диагностику этой ошибки. Чтобы устранить эту ошибку и определить, какая DLL не найдена, необходимо найти список зависимых DLL и определить отсутствующую с помощью инструментов отладки и трассировки Windows.

Изменено в версии 3.12: Параметр name теперь может быть путеподобным объектом.

См. также

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

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

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

Доступность: Windows

Изменено в версии 3.3: Ранее вызывалось исключение WindowsError, которое теперь является псевдонимом OSError.

Изменено в версии 3.12: Параметр name теперь может быть путеподобным объектом.

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

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

Доступность: Windows

Изменено в версии 3.12: Параметр name теперь может быть путеподобным объектом.

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

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

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

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

Изменено в версии 3.12: Параметр name теперь может быть путеподобным объектом.

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

Параметр mode можно использовать для указания способа загрузки библиотеки. Подробности см. на странице руководства 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, управляемого функциями Windows API GetLastError() и SetLastError(); функции ctypes.get_last_error() и ctypes.set_last_error() используются для запроса и изменения закрытой копии кода ошибки Windows в ctypes.

Параметр winmode используется в Windows для указания способа загрузки библиотеки (поскольку mode игнорируется). Он принимает любое значение, допустимое для параметра флагов Win32 API LoadLibraryEx. Если он опущен, по умолчанию используются флаги, обеспечивающие наиболее безопасную загрузку DLL, что позволяет избежать таких проблем, как DLL hijacking. Передача полного пути к 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")  # On 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

Создает экземпляры WinDLL.

Доступность: Windows

ctypes.oledll

Создает экземпляры OleDLL.

Доступность: Windows

ctypes.pydll

Создает экземпляры PyDLL.

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

ctypes.pythonapi

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

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

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

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

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

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

Они являются экземплярами закрытого локального класса _FuncPtr (не экспортируемого в ctypes), который наследуется от закрытого класса _CFuncPtr:

>>> import ctypes
>>> lib = ctypes.CDLL(None)
>>> issubclass(lib._FuncPtr, ctypes._CFuncPtr)
True
>>> lib._FuncPtr is ctypes._CFuncPtr
False
class ctypes._CFuncPtr

Базовый класс для вызываемых внешних функций C.

Экземпляры внешних функций также являются C-совместимыми типами данных; они представляют собой указатели на функции C.

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

restype

Присвойте тип ctypes, чтобы указать тип результата внешней функции. Используйте None для void — функции, ничего не возвращающей.

Можно назначить вызываемый объект Python, не являющийся типом ctypes; в этом случае предполагается, что функция возвращает C int, и вызываемый объект будет вызван с этим целым числом, что позволяет выполнить дальнейшую обработку или проверку ошибок. Использование этого варианта устарело; для более гибкой постобработки или проверки ошибок используйте тип данных ctypes в качестве restype и присвойте вызываемый объект атрибуту errcheck.

argtypes

Присвойте кортеж типов ctypes, чтобы указать типы аргументов, которые принимает функция. Функции, использующие соглашение о вызовах stdcall, могут вызываться только с тем количеством аргументов, которое равно длине этого кортежа; функции, использующие соглашение о вызовах C, также принимают дополнительные, неопределенные аргументы.

При вызове внешней функции каждый фактический аргумент передается классовому методу from_param() элементов в кортеже argtypes; этот метод позволяет адаптировать фактический аргумент к объекту, который принимает внешняя функция. Например, элемент c_char_p в кортеже argtypes преобразует переданную в качестве аргумента строку в объект bytes с использованием правил преобразования ctypes.

Новое: теперь в argtypes можно помещать элементы, не являющиеся типами ctypes, но каждый элемент должен иметь метод from_param(), возвращающий значение, пригодное для использования в качестве аргумента (целое число, строка, экземпляр ctypes). Это позволяет определять адаптеры, способные адаптировать пользовательские объекты в качестве параметров функции.

errcheck

Присвойте этому атрибуту функцию Python или другой вызываемый объект. Вызываемый объект будет вызван с тремя или более аргументами:

callable(result, func, arguments)

result — то, что возвращает внешняя функция, как указано атрибутом restype.

func — сам объект внешней функции, что позволяет повторно использовать один и тот же вызываемый объект для проверки или постобработки результатов нескольких функций.

arguments — кортеж, содержащий параметры, первоначально переданные вызову функции; это позволяет настраивать поведение в зависимости от используемых аргументов.

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

exception ctypes.ArgumentError

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

В Windows, когда вызов внешней функции вызывает системное исключение (например, из-за нарушения прав доступа), оно будет перехвачено и заменено соответствующим исключением Python. Кроме того, будет вызвано событие аудита ctypes.set_exception с аргументом code, что позволяет хуку аудита заменить исключение собственным.

Некоторые способы вызова внешних функций могут инициировать событие аудита ctypes.call_function с аргументами function pointer и arguments.

Прототипы функций

Внешние функции также можно создавать путем инстанцирования прототипов функций. Прототипы функций аналогичны прототипам функций в C; они описывают функцию (тип возвращаемого значения, типы аргументов, соглашение о вызовах) без определения реализации. Фабричные функции должны вызываться с желаемым типом результата и типами аргументов функции, а также могут использоваться в качестве фабрик декораторов и, таким образом, применяться к функциям с помощью синтаксиса @wrapper. Примеры см. в разделе Функции обратного вызова.

ctypes.CFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)

Возвращенный прототип функции создает функции, использующие стандартное соглашение о вызовах C. Функция освобождает GIL во время вызова. Если параметр use_errno имеет значение true, закрытая копия системной переменной errno в ctypes обменивается с реальным значением errno до и после вызова; use_last_error делает то же самое для кода ошибки Windows.

ctypes.WINFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)

Возвращенный прототип функции создает функции, использующие соглашение о вызовах stdcall. Функция освобождает GIL во время вызова. Параметры use_errno и use_last_error имеют то же значение, что и выше.

Доступность: Windows

ctypes.PYFUNCTYPE(restype, *argtypes)

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

Прототипы функций, созданные этими фабричными функциями, могут быть инстанцированы различными способами, в зависимости от типа и количества параметров в вызове:

prototype(address)

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

prototype(callable)

Создает вызываемую из C функцию (функцию обратного вызова) из вызываемого объекта Python callable.

prototype(func_spec[, paramflags])

Возвращает внешнюю функцию, экспортируемую разделяемой библиотекой. func_spec должен быть 2-кортежем (name_or_ordinal, library). Первый элемент — это имя экспортируемой функции в виде строки или порядковый номер экспортируемой функции в виде небольшого целого числа. Второй элемент — экземпляр разделяемой библиотеки.

prototype(vtbl_index, name[, paramflags[, iid]])

Возвращает внешнюю функцию, которая будет вызывать COM-метод. vtbl_index — это индекс в таблице виртуальных функций, небольшое неотрицательное целое число. name — имя COM-метода. iid — необязательный указатель на идентификатор интерфейса, который используется в расширенном отчете об ошибках.

COM-методы используют специальное соглашение о вызовах: в дополнение к параметрам, указанным в кортеже argtypes, они требуют указатель на COM-интерфейс в качестве первого аргумента.

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

Параметр paramflags должен быть кортежем той же длины, что и argtypes.

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

Первый элемент — это целое число, содержащее комбинацию флагов направления для параметра:

1

Задает входной параметр функции.

2

Выходной параметр. Внешняя функция заполняет значение.

4

Входной параметр со значением по умолчанию равным целому числу ноль.

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

Необязательный третий элемент — это значение по умолчанию для этого параметра.

В следующем примере показано, как обернуть функцию Windows MessageBoxW, чтобы она поддерживала параметры по умолчанию и именованные аргументы. C-объявление из заголовочного файла Windows выглядит следующим образом:

WINUSERAPI int WINAPI
MessageBoxW(
    HWND hWnd,
    LPCWSTR lpText,
    LPCWSTR lpCaption,
    UINT uType);

Вот обертка с помощью ctypes:

>>> from ctypes import c_int, WINFUNCTYPE, windll
>>> from ctypes.wintypes import HWND, LPCWSTR, UINT
>>> prototype = WINFUNCTYPE(c_int, HWND, LPCWSTR, LPCWSTR, UINT)
>>> paramflags = (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", "Hello from ctypes"), (1, "flags", 0)
>>> MessageBox = prototype(("MessageBoxW", windll.user32), paramflags)

Внешнюю функцию MessageBox теперь можно вызывать следующими способами:

>>> MessageBox()
>>> MessageBox(text="Spam, spam, spam")
>>> MessageBox(flags=2, text="foo bar")

Второй пример демонстрирует выходные параметры. Функция win32 GetWindowRect получает размеры указанного окна, копируя их в структуру RECT, которую должен предоставить вызывающий. Вот C-объявление:

WINUSERAPI BOOL WINAPI
GetWindowRect(
     HWND hWnd,
     LPRECT lpRect);

Вот обертка с помощью ctypes:

>>> from ctypes import POINTER, WINFUNCTYPE, windll, WinError
>>> from ctypes.wintypes import BOOL, HWND, RECT
>>> prototype = WINFUNCTYPE(BOOL, HWND, POINTER(RECT))
>>> paramflags = (1, "hwnd"), (2, "lprect")
>>> GetWindowRect = prototype(("GetWindowRect", windll.user32), paramflags)
>>>

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

Выходные параметры можно объединять с протоколом errcheck для выполнения дополнительной обработки вывода и проверки ошибок. Функция win32 API GetWindowRect возвращает BOOL для сообщения об успехе или неудаче, поэтому эта функция может выполнять проверку ошибок и вызывать исключение при сбое вызова API:

>>> def errcheck(result, func, args):
...     if not result:
...         raise WinError()
...     return args
...
>>> GetWindowRect.errcheck = errcheck
>>>

Если функция errcheck возвращает полученный кортеж аргументов без изменений, ctypes продолжает обычную обработку выходных параметров. Если вы хотите вернуть кортеж координат окна вместо экземпляра RECT, вы можете извлечь поля в функции и вернуть их вместо этого; в этом случае обычная обработка больше производиться не будет:

>>> def errcheck(result, func, args):
...     if not result:
...         raise WinError()
...     rc = args[1]
...     return rc.left, rc.top, rc.bottom, rc.right
...
>>> GetWindowRect.errcheck = errcheck
>>>

Вспомогательные функции

ctypes.addressof(obj)

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

Вызывает событие аудита ctypes.addressof с аргументом obj.

ctypes.alignment(obj_or_type)

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

ctypes.byref(obj[, offset])

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

byref(obj, offset) соответствует следующему коду на C:

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

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

ctypes.cast(obj, type)

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

ctypes.create_string_buffer(init, size=None)
ctypes.create_string_buffer(size)

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

Если задан параметр size (и не равен None), он должен быть int. Он определяет размер возвращаемого массива.

Если указан аргумент init, он должен быть объектом типа bytes. Он используется для инициализации элементов массива. Байты, не инициализированные таким образом, обнуляются (NUL).

Если параметр size не задан (или равен None), буфер создается на один элемент больше, чем init, фактически добавляя завершающий NUL-символ.

Если указаны оба аргумента, size не должен быть меньше len(init).

Warning

Если size равен len(init), завершающий NUL-символ не добавляется. Не обрабатывайте такой буфер как C-строку.

Например:

>>> bytes(create_string_buffer(2))
b'\x00\x00'
>>> bytes(create_string_buffer(b'ab'))
b'ab\x00'
>>> bytes(create_string_buffer(b'ab', 2))
b'ab'
>>> bytes(create_string_buffer(b'ab', 4))
b'ab\x00\x00'
>>> bytes(create_string_buffer(b'abcdef', 2))
Traceback (most recent call last):
   ...
ValueError: byte string too long

Вызывает событие аудита ctypes.create_string_buffer с аргументами init, size.

ctypes.create_unicode_buffer(init, size=None)
ctypes.create_unicode_buffer(size)

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

Функция принимает те же аргументы, что и create_string_buffer(), за исключением того, что init должен быть строкой, а size задает количество c_wchar.

Вызывает событие аудита ctypes.create_unicode_buffer с аргументами init, size.

ctypes.DllCanUnloadNow()

Эта функция представляет собой хук, позволяющий реализовать внутрипроцессные COM-серверы с помощью ctypes. Она вызывается из функции DllCanUnloadNow, экспортируемой dll-расширением _ctypes.

Доступность: Windows

ctypes.DllGetClassObject()

Эта функция представляет собой хук, позволяющий реализовать внутрипроцессные COM-серверы с помощью ctypes. Она вызывается из функции DllGetClassObject, экспортируемой dll-расширением _ctypes.

Доступность: Windows

ctypes.util.find_library(name)

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

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

ctypes.util.find_msvcrt()

Возвращает имя файла библиотеки времени выполнения VC, используемой Python и модулями расширения. Если имя библиотеки определить не удается, возвращается None.

Если вам нужно освободить память, выделенную, например, модулем расширения с помощью вызова free(void *), важно использовать функцию из той же библиотеки, которая выделила эту память.

Доступность: Windows

ctypes.FormatError([code])

Возвращает текстовое описание кода ошибки code. Если код ошибки не указан, используется последний код ошибки, полученный вызовом функции Windows API GetLastError.

Доступность: Windows

ctypes.GetLastError()

Возвращает последний код ошибки, установленный Windows в вызывающем потоке. Эта функция напрямую вызывает функцию Windows GetLastError() и не возвращает копию кода ошибки, закрытую для ctypes.

Доступность: Windows

ctypes.get_errno()

Возвращает текущее значение закрытой для ctypes копии системной переменной errno в вызывающем потоке.

Вызывает событие аудита ctypes.get_errno без аргументов.

ctypes.get_last_error()

Возвращает текущее значение закрытой для ctypes копии системной переменной LastError в вызывающем потоке.

Доступность: Windows

Вызывает событие аудита ctypes.get_last_error без аргументов.

ctypes.memmove(dst, src, count)

Аналогична стандартной библиотечной функции C memmove: копирует count байтов из src в dst. dst и src должны быть целыми числами или экземплярами ctypes, которые могут быть преобразованы в указатели.

ctypes.memset(dst, c, count)

Аналогична стандартной библиотечной функции C memset: заполняет блок памяти по адресу dst значением c в количестве count байтов. dst должен быть целым числом, задающим адрес, или экземпляром ctypes.

ctypes.POINTER(type, /)

Создает и возвращает новый тип указателя ctypes. Типы указателей кэшируются и повторно используются внутри системы, поэтому повторный вызов этой функции является недорогим. type должен быть типом ctypes.

ctypes.pointer(obj, /)

Создает новый экземпляр указателя, указывающий на obj. Возвращаемый объект имеет тип POINTER(type(obj)).

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

ctypes.resize(obj, size)

Эта функция изменяет размер внутреннего буфера памяти obj, который должен быть экземпляром типа ctypes. Невозможно сделать буфер меньше, чем собственный размер типа объекта, заданный sizeof(type(obj)), но возможно увеличить буфер.

ctypes.set_errno(value)

Устанавливает текущее значение закрытой для ctypes копии системной переменной errno в вызывающем потоке в value и возвращает предыдущее значение.

Вызывает событие аудита ctypes.set_errno с аргументом errno.

ctypes.set_last_error(value)

Устанавливает текущее значение закрытой для ctypes копии системной переменной LastError в вызывающем потоке в value и возвращает предыдущее значение.

Доступность: Windows

Вызывает событие аудита ctypes.set_last_error с аргументом error.

ctypes.sizeof(obj_or_type)

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

ctypes.string_at(ptr, size=-1)

Возвращает байтовую строку по адресу void *ptr. Если указан параметр size, он используется в качестве размера, в противном случае предполагается, что строка завершается нулем.

Вызывает событие аудита ctypes.string_at с аргументами ptr, size.

ctypes.WinError(code=None, descr=None)

Эта функция, вероятно, имеет самое неудачное название в ctypes. Она создает экземпляр OSError. Если code не указан, вызывается GetLastError для определения кода ошибки. Если descr не указан, вызывается FormatError() для получения текстового описания ошибки.

Доступность: Windows

Изменено в версии 3.3: Раньше создавался экземпляр WindowsError, который теперь является псевдонимом для OSError.

ctypes.wstring_at(ptr, size=-1)

Возвращает строку расширенных символов по адресу void *ptr. Если указан параметр size, он используется как количество символов строки, в противном случае предполагается, что строка завершается нулем.

Вызывает событие аудита ctypes.wstring_at с аргументами ptr, size.

Типы данных

class ctypes._CData

Этот закрытый класс является общим базовым классом для всех типов данных ctypes. Помимо прочего, все экземпляры типов ctypes содержат блок памяти, содержащий C-совместимые данные; адрес этого блока памяти возвращается вспомогательной функцией addressof(). Другая переменная экземпляра предоставляется как _objects; она содержит другие объекты Python, которые необходимо сохранять живыми в случае, если блок памяти содержит указатели.

Общие методы типов данных ctypes, все они являются методами класса (точнее, методами метакласса):

from_buffer(source[, offset])

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

Вызывает событие аудита ctypes.cdata/buffer с аргументами pointer, size, offset.

from_buffer_copy(source[, offset])

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

Вызывает событие аудита ctypes.cdata/buffer с аргументами pointer, size, offset.

from_address(address)

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

Этот метод и другие методы, косвенно вызывающие его, вызывают событие аудита ctypes.cdata с аргументом address.

from_param(obj)

Этот метод адаптирует obj к типу ctypes. Он вызывается с фактическим объектом, используемым при вызове внешней функции, когда тип присутствует в кортеже argtypes внешней функции; он должен возвращать объект, который может использоваться в качестве параметра вызова функции.

Все типы данных ctypes имеют реализацию по умолчанию этого метода класса, которая обычно возвращает obj, если он является экземпляром этого типа. Некоторые типы принимают и другие объекты.

in_dll(library, name)

Этот метод возвращает экземпляр типа ctypes, экспортируемый разделяемой библиотекой. name — имя символа, экспортирующего данные, library — загруженная разделяемая библиотека.

Общие переменные экземпляров типов данных ctypes:

_b_base_

Иногда экземпляры данных ctypes не владеют блоком памяти, который они содержат, а вместо этого разделяют часть блока памяти базового объекта. Доступный только для чтения член _b_base_ — это корневой объект ctypes, который владеет блоком памяти.

_b_needsfree_

Эта доступная только для чтения переменная равна true, когда экземпляр данных ctypes сам выделил блок памяти, и false в противном случае.

_objects

Этот член равен либо None, либо словарю, содержащему объекты Python, которые необходимо сохранять живыми, чтобы содержимое блока памяти оставалось действительным. Этот объект предоставляется только для отладки; никогда не изменяйте содержимое этого словаря.

Базовые типы данных

class ctypes._SimpleCData

Этот непубличный класс является базовым классом для всех фундаментальных типов данных ctypes. Он упоминается здесь, поскольку содержит общие атрибуты фундаментальных типов данных ctypes. _SimpleCData является подклассом _CData, поэтому наследует его методы и атрибуты. Типы данных ctypes, которые не являются указателями и не содержат их, теперь поддерживают сериализацию через pickle.

Экземпляры имеют единственный атрибут:

value

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

При извлечении атрибута value из экземпляра ctypes обычно каждый раз возвращается новый объект. Модуль ctypes не возвращает исходный объект, а всегда конструирует новый. То же самое справедливо и для всех остальных экземпляров объектов ctypes.

Фундаментальные типы данных при возврате в качестве результатов вызова внешних функций или, например, при получении полей структур или элементов массивов, прозрачно преобразуются в нативные типы Python. Другими словами, если внешняя функция имеет restype, равный c_char_p, вы всегда получите объект bytes языка Python, а не экземпляр c_char_p.

Подклассы фундаментальных типов данных не наследуют это поведение. Таким образом, если атрибут restype внешней функции является подклассом c_void_p, из вызова функции вы получите экземпляр этого подкласса. Разумеется, получить значение указателя можно, обратившись к атрибуту value.

Ниже перечислены фундаментальные типы данных ctypes:

class ctypes.c_byte

Представляет тип данных C signed char и интерпретирует значение как небольшое целое число. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.

class ctypes.c_char

Представляет тип данных C char и интерпретирует значение как один символ. Конструктор принимает необязательный строковый инициализатор, длина строки должна составлять ровно один символ.

class ctypes.c_char_p

Представляет тип данных C char*, когда он указывает на строку, завершающуюся нулем. Для универсального указателя на символы, который также может указывать на двоичные данные, следует использовать POINTER(c_char). Конструктор принимает целочисленный адрес или объект bytes.

class ctypes.c_double

Представляет тип данных C double. Конструктор принимает необязательный инициализатор с плавающей точкой.

class ctypes.c_longdouble

Представляет тип данных C long double. Конструктор принимает необязательный инициализатор с плавающей точкой. На платформах, где sizeof(long double) == sizeof(double), это псевдоним для c_double.

class ctypes.c_float

Представляет тип данных C float. Конструктор принимает необязательный инициализатор с плавающей точкой.

class ctypes.c_int

Представляет тип данных C signed int. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется. На платформах, где sizeof(int) == sizeof(long), это псевдоним для c_long.

class ctypes.c_int8

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

class ctypes.c_int16

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

class ctypes.c_int32

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

class ctypes.c_int64

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

class ctypes.c_long

Представляет тип данных C signed long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.

class ctypes.c_longlong

Представляет тип данных C signed long long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.

class ctypes.c_short

Представляет тип данных C signed short. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.

class ctypes.c_size_t

Представляет тип данных C size_t.

class ctypes.c_ssize_t

Представляет тип данных C ssize_t.

Добавлено в версии 3.2.

class ctypes.c_time_t

Представляет тип данных C time_t.

Добавлено в версии 3.12.

class ctypes.c_ubyte

Представляет тип данных C unsigned char и интерпретирует значение как небольшое целое число. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.

class ctypes.c_uint

Представляет тип данных C unsigned int. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется. На платформах, где sizeof(int) == sizeof(long), это псевдоним для c_ulong.

class ctypes.c_uint8

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

class ctypes.c_uint16

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

class ctypes.c_uint32

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

class ctypes.c_uint64

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

class ctypes.c_ulong

Представляет тип данных C unsigned long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.

class ctypes.c_ulonglong

Представляет тип данных C unsigned long long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.

class ctypes.c_ushort

Представляет тип данных C unsigned short. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.

class ctypes.c_void_p

Представляет тип C void*. Значение представляется как целое число. Конструктор принимает необязательный целочисленный инициализатор.

class ctypes.c_wchar

Представляет тип данных C wchar_t и интерпретирует значение как односимвольную строку юникода. Конструктор принимает необязательный строковый инициализатор, длина строки должна составлять ровно один символ.

class ctypes.c_wchar_p

Представляет тип данных C wchar_t*, который должен быть указателем на завершающуюся нулем строку расширенных символов (wide character string). Конструктор принимает целочисленный адрес или строку.

class ctypes.c_bool

Представляет тип данных C bool (точнее, _Bool из C99). Его значением может быть True или False, а конструктор принимает любой объект, имеющий логическое значение (truth value).

class ctypes.HRESULT

Представляет значение HRESULT, которое содержит информацию об успехе или ошибке вызова функции или метода.

Доступность: Windows

class ctypes.py_object

Представляет тип данных C PyObject*. Вызов этого типа без аргументов создает указатель NULL PyObject*.

Модуль ctypes.wintypes предоставляет множество других специфичных для Windows типов данных, например HWND, WPARAM или DWORD. Также определены некоторые полезные структуры, такие как MSG или RECT.

Структурированные типы данных

class ctypes.Union(*args, **kw)

Абстрактный базовый класс для объединений с нативным порядком байтов.

class ctypes.BigEndianUnion(*args, **kw)

Абстрактный базовый класс для объединений с порядком байтов от старшего к младшему (big endian).

Добавлено в версии 3.11.

class ctypes.LittleEndianUnion(*args, **kw)

Абстрактный базовый класс для объединений с порядком байтов от младшего к старшему (little endian).

Добавлено в версии 3.11.

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_, в противном случае он не возымеет эффекта. Установка этого атрибута в 0 аналогична его отсутствию.

_align_

Необязательное небольшое целое число, позволяющее переопределить выравнивание структуры при упаковке в память или распаковке из памяти. Установка этого атрибута в 0 аналогична его отсутствию.

Добавлено в версии 3.13.

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

Задает тип каждого элемента в массиве.

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

ctypes.ARRAY(type, length)

Создает массив. Эквивалентно выражению type * length, где type — тип данных ctypes, а length — целое число.

Эта функция мягко устарела (soft deprecated) в пользу умножения. Планов по ее удалению нет.

class ctypes._Pointer

Приватный абстрактный базовый класс для указателей.

Конкретные типы указателей создаются путем вызова функции POINTER() с типом, на который будет ссылаться указатель; функция pointer() делает это автоматически.

Если указатель указывает на массив, его элементы можно читать и записывать с помощью стандартных операций индексации и срезов. Объекты-указатели не имеют размера, поэтому len() вызовет исключение TypeError. Отрицательные индексы будут читать из памяти перед указателем (как в C), а индексы вне диапазона, вероятно, приведут к аварийному завершению из-за нарушения прав доступа (если вам повезет).

_type_

Задает тип, на который указывает указатель.

contents

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

© 2001–2025 Python Software Foundation
Licensed under the PSF License.
https://docs.python.org/3.13/library/ctypes.html

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API