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_long будет выводиться вместо ожидаемого c_int — на самом деле они представляют один и тот же тип.

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

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

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

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

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

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

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

Примечание

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

В Linux необходимо указать имя файла включая расширение для загрузки библиотеки, поэтому доступ к атрибутам для загрузки библиотек невозможен. Должно использоваться либо метод LoadLibrary() загрузчиков dll, либо библиотека должна быть загружена путём создания экземпляра CDLL, вызывая конструктор:

>>> cdll.LoadLibrary("libc.so.6")  
<CDLL 'libc.so.6', handle ... at ...>
>>> libc = CDLL("libc.so.6")       
>>> libc                           
<CDLL 'libc.so.6', handle ... at ...>
>>>

Доступ к функциям из загруженных DLL

Функции доступны как атрибуты объектов dll:

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

целое число

c_ubyte

unsigned char

целое число

c_short

short

целое число

c_ushort

unsigned short

целое число

c_int

int

целое число

c_uint

unsigned int

целое число

c_long

long

целое число

c_ulong

unsigned long

целое число

c_longlong

__int64 или long long

целое число

c_ulonglong

unsigned __int64 или unsigned long long

целое число

c_size_t

size_t

целое число

c_ssize_t

ssize_t или Py_ssize_t

целое число

c_time_t

time_t

целое число

c_float

float

вещественное число

c_double

double

вещественное число

c_longdouble

long double

вещественное число

c_char_p

char* (завершается нулём)

объект bytes или None

c_wchar_p

wchar_t* (завершается нулём)

строка или None

c_void_p

void*

целое число или 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 bytes неизменяемы):

>>> s = "Hello, World"
>>> c_s = c_wchar_p(s)
>>> print(c_s)
c_wchar_p(139966785747344)
>>> print(c_s.value)
Hello World
>>> c_s.value = "Hi, there"
>>> print(c_s)              # the memory location has changed
c_wchar_p(139966783348904)
>>> print(c_s.value)
Hi, there
>>> print(s)                # first object is unchanged
Hello, World
>>>

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

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

Функция create_string_buffer() заменяет старую функцию c_buffer() (которая всё ещё доступна как псевдоним). Для создания изменяемого блока памяти, содержащего символы Юникод типа C 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 Platforms, соглашение о вызове для функций с переменным числом аргументов отличается от соглашения для обычных функций.

На этих платформах необходимо указать атрибут 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 в этом случае. Результат должен быть целым числом, строкой, байтовым объектом, экземпляром 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(), которая ожидает указатель на строку и символ и возвращает указатель на строку:

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

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

Массивы

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

Рекомендуемый способ создания типов массивов — умножение типа данных на положительное целое число:

TenPointsArrayType = POINT * 10

Вот пример несколько искусственного типа данных — структуры, содержащей 4 POINT среди других элементов:

>>> from ctypes import *
>>> class POINT(Structure):
...     _fields_ = ("x", c_int), ("y", c_int)
...
>>> class MyStruct(Structure):
...     _fields_ = [("a", c_int),
...                 ("b", c_float),
...                 ("point_array", POINT * 4)]
>>>
>>> print(len(MyStruct().point_array))
4
>>>

Экземпляры создаются обычным способом, вызывая класс:

arr = TenPointsArrayType()
for pt in arr:
    print(pt.x, pt.y)

Вышеприведенный код выводит серию 0 0 строк, так как содержимое массива инициализируется нулями.

Также можно указать инициализаторы правильного типа:

>>> from ctypes import *
>>> TenIntegers = c_int * 10
>>> ii = TenIntegers(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
>>> print(ii)
<c_long_Array_10 object at 0x...>
>>> for i in ii: print(i, end=" ")
...
1 2 3 4 5 6 7 8 9 10
>>>

Указатели

Экземпляры указателей создаются путем вызова функции pointer() для типа ctypes:

>>> from ctypes import *
>>> i = c_int(42)
>>> pi = pointer(i)
>>>

Экземпляры указателей имеют атрибут contents, который возвращает объект, на который указывает указатель, — объект i выше:

>>> pi.contents
c_long(42)
>>>

Обратите внимание, что ctypes не имеет OOR (возвращение исходного объекта), он каждый раз создает новый эквивалентный объект при получении атрибута:

>>> pi.contents is i
False
>>> pi.contents is pi.contents
False
>>>

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

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

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

>>> pi[0]
99
>>>

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

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

Также можно использовать индексы, отличные от 0, но вы должны понимать, что делаете, как и в языке C: Вы можете получить доступ к произвольным местам памяти или изменить их. Как правило, вы используете эту функцию только тогда, когда получаете указатель от функции C, и вы точно знаете, что указатель на самом деле указывает на массив, а не на один элемент.

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

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

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

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

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

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

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

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

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

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

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

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

>>> bar.values = None
>>>

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

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

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

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

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

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

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

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

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

struct cell; /* forward declaration */

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

Прямое преобразование в код ctypes выглядело бы так, но оно не работает:

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

потому что новый class cell недоступен в самом операторе класса. В ctypes мы можем определить класс cell и установить атрибут _fields_ позже, после оператора класса:

>>> from ctypes import *
>>> class cell(Structure):
...     pass
...
>>> cell._fields_ = [("name", c_char_p),
...                  ("next", POINTER(cell))]
>>>

Давайте попробуем. Мы создаем два экземпляра cell и делаем их взаимно связанными указателями, а затем несколько раз следуем цепочке указателей:

>>> c1 = cell()
>>> c1.name = b"foo"
>>> c2 = cell()
>>> c2.name = b"bar"
>>> c1.next = pointer(c2)
>>> c2.next = pointer(c1)
>>> p = c1
>>> for i in range(8):
...     print(p.name, end=" ")
...     p = p.next[0]
...
foo bar foo bar foo bar foo bar
>>>

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

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

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

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

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

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

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

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

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

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

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

>>> def py_cmp_func(a, b):
...     print("py_cmp_func", a[0], b[0])
...     return 0
...
>>> cmp_func = CMPFUNC(py_cmp_func)
>>>

Результат:

>>> qsort(ia, len(ia), sizeof(c_int), cmp_func)  
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 5 7
py_cmp_func 1 7
>>>

Теперь мы можем фактически сравнить два элемента и вернуть полезный результат:

>>> def py_cmp_func(a, b):
...     print("py_cmp_func", a[0], b[0])
...     return a[0] - b[0]
...
>>>
>>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func)) 
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>

Как мы можем легко проверить, теперь наш массив отсортирован:

>>> for i in ia: print(i, end=" ")
...
1 5 7 33 99
>>>

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

>>> @CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
... def py_cmp_func(a, b):
...     print("py_cmp_func", a[0], b[0])
...     return a[0] - b[0]
...
>>> qsort(ia, len(ia), sizeof(c_int), py_cmp_func)
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>

Примечание

Убедитесь, что вы сохраняете ссылки на объекты CFUNCTYPE() до тех пор, пока они используются из кода C. ctypes этого не делает, и если вы этого не сделаете, они могут быть удалены сборщиком мусора, что приведет к сбою вашей программы при вызове обратного вызова.

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

Получение значений, экспортированных из DLL

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

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

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

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

В документации к этому значению указано:

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

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

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

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

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

Поскольку 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
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 не имеет ожидаемого эффекта.

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

Ещё один пример, поведение которого может отличаться от ожидаемого:

>>> 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 или номер версии (это форма, используемая для опции позиксного линкера -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] Не удаётся найти указанный модуль”. Это сообщение об ошибке не содержит имя отсутствующей DLL, так как API Windows не возвращает эту информацию, что затрудняет диагностику ошибки. Чтобы устранить эту ошибку и определить, какая 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 теперь может быть объектом, подобным пути.

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

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

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

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

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

ctypes.pythonapi

Экземпляр PyDLL, который предоставляет функции API C 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, внутренняя копия ctypes переменной системы errno меняется на фактическое значение 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-вызываемого объекта.

prototype(func_spec[, paramflags])

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

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

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

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

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

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

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

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

1

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

2

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

4

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

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

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

Следующий пример демонстрирует, как обернуть функцию Windows MessageBoxW так, чтобы она поддерживала параметры по умолчанию и именованные аргументы. Разъяснение с заголовков 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, которую должен предоставить вызывающий.

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

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

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

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

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

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

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

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

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

ctypes.addressof(obj)

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

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

ctypes.alignment(obj_or_type)

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

ctypes.byref(obj[, offset])

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

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

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

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

ctypes.cast(obj, type)

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

ctypes.create_string_buffer(init_or_size, size=None)

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

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

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

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

ctypes.create_unicode_buffer(init_or_size, size=None)

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

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

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

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

ctypes.DllCanUnloadNow()

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

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

ctypes.DllGetClassObject()

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

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

ctypes.util.find_library(name)

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

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

ctypes.util.find_msvcrt()

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

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

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

ctypes.FormatError([code])

Возвращает текстовое описание кода ошибки code. Если код ошибки не указан, используется последний код ошибки, полученный путём вызова функции Windows 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.

END_OF_DOCUMENT_MARKER
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, которые не являются и не содержат указателей, теперь могут быть закодированы.

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

value

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

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

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

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

Вот основные типы данных ctypes:

class ctypes.c_byte

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

class ctypes.c_char

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

class ctypes.c_char_p

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

class ctypes.c_double

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

class ctypes.c_longdouble

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

class ctypes.c_float

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

class ctypes.c_int

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

class ctypes.c_int8

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

class ctypes.c_int16

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

class ctypes.c_int32

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

class ctypes.c_int64

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

class ctypes.c_long

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

class ctypes.c_longlong

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

class ctypes.c_short

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

class ctypes.c_size_t

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

class ctypes.c_ssize_t

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

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

class ctypes.c_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

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

class ctypes.c_uint16

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

class ctypes.c_uint32

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

END_OF_DOCUMENT_MARKER
class ctypes.c_uint64

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

class ctypes.c_ulong

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

class ctypes.c_ulonglong

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

class ctypes.c_ushort

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

class ctypes.c_void_p

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

class ctypes.c_wchar

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

class ctypes.c_wchar_p

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

class ctypes.c_bool

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

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_ после объявления класса, который определяет подкласс Structure, это позволяет создавать типы данных, которые прямо или косвенно ссылаются на себя:

class List(Structure):
    pass
List._fields_ = [("pnext", POINTER(List)),
                 ...
                ]

Однако, переменная класса _fields_ должна быть определена до первого использования типа (создаётся экземпляр, вызывается sizeof() и т. д.). Поздние присваивания переменной класса _fields_ приведут к ошибке AttributeError.

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

_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 — целое число.

Эта функция мягко устарела в пользу умножения. Её удаление не планируется.

class ctypes._Pointer

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

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

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

_type_

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

contents

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

END_OF_DOCUMENT_MARKER

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

Spec-Zone.ru

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