ctypes
— Библиотека внешних функций для Python¶
Исходный код: Lib/ctypes
ctypes
- это библиотека внешних функций для Python. Она предоставляет типы данных, совместимые с C, и позволяет вызывать функции из библиотек DLL или общих библиотек. Ее можно использовать для перевода этих библиотек в чистый Python.
учебное пособие по ctypes¶
Примечание: В примерах кода в этом руководстве используется doctest
, чтобы убедиться, что они действительно работают. Поскольку некоторые примеры кода ведут себя по-разному в Linux, Windows или macOS, они содержат инструкции по проверке в комментариях.
Примечание: Некоторые примеры кода ссылаются на типы 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¶
Доступ к функциям осуществляется как к атрибутам библиотечных объектов:
>>> 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
, которая возвращает дескриптор модуля для заданного имени модуля, имеет следующий прототип на языке Си, и используется макрос, который отображает один из них как 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. В этом примере используется функция time()
, которая возвращает системное время в секундах с эпохи Unix, и функция GetModuleHandleA()
, которая возвращает дескриптор модуля win32.
В этом примере обе функции вызываются с помощью указателя NULL
(в качестве указателя None
следует использовать указатель NULL
):
>>> print(libc.time(None))
1150640792
>>> print(hex(windll.kernel32.GetModuleHandleA(None)))
0x1d000000
>>>
ValueError
возникает при вызове функции stdcall
с использованием соглашения о вызове cdecl
, или наоборот:
>>> cdll.kernel32.GetModuleHandleA(None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with not enough arguments (4 bytes missing)
>>>
>>> windll.msvcrt.printf(b"spam")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with too many arguments (4 bytes in excess)
>>>
Чтобы узнать правильное соглашение о вызове, вам нужно заглянуть в заголовочный файл C или документацию к функции, которую вы хотите вызвать.
В Windows ctypes
используется структурированная обработка исключений win32 для предотвращения сбоев из-за общих сбоев защиты, когда функции вызываются с недопустимыми значениями аргументов:
>>> windll.kernel32.GetModuleHandleA(32)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: exception: access violation reading 0x00000020
>>>
Однако существует достаточно способов вызвать сбой в работе Python с помощью ctypes
, поэтому в любом случае вам следует быть осторожным. Модуль faulthandler
может быть полезен при отладке сбоев (например, из-за ошибок сегментации, вызванных ошибочными вызовами библиотеки C).
None
, целые числа, байты и строки (в юникоде) - единственные собственные объекты Python, которые могут напрямую использоваться в качестве параметров при вызове этих функций. None
передается как указатель C NULL
, объекты bytes и строки передаются как указатель на блок памяти, содержащий их данные (char* или wchar_t*). Целые числа Python передаются как тип C int, используемый платформами по умолчанию, их значение маскируется, чтобы соответствовать типу C.
Прежде чем мы перейдем к вызову функций с другими типами параметров, мы должны узнать больше о типах данных ctypes
.
Основные типы данных¶
ctypes
определяет ряд примитивных типов данных, совместимых с C:
типы тип |
Тип C |
Тип Python |
---|---|---|
_Bool |
книга (1) |
|
char |
объект размером в 1 символьный байт |
|
|
строка из 1 символа |
|
char |
инт |
|
unsigned char |
инт |
|
short |
инт |
|
unsigned short |
инт |
|
int |
инт |
|
unsigned int |
инт |
|
long |
инт |
|
unsigned long |
инт |
|
__int64 или long long |
инт |
|
unsigned __int64 или unsigned long long |
инт |
|
|
инт |
|
|
инт |
|
float |
плыть |
|
double |
плыть |
|
long double |
плыть |
|
char* (нулевое значение завершено) |
байты объекта или |
|
wchar_t* (нулевое значение завершено) |
строка или |
|
void* |
int или |
Конструктор принимает любой объект с истинностным значением.
Все эти типы можно создать, вызвав их с необязательным инициализатором правильного типа и значения:
>>> c_int()
c_long(0)
>>> c_wchar_p("Hello, World")
c_wchar_p(140018365411392)
>>> c_ushort(-3)
c_ushort(65533)
>>>
Поскольку эти типы изменчивы, их значение также может быть изменено впоследствии:
>>> i = c_int(42)
>>> print(i)
c_long(42)
>>> print(i.value)
42
>>> i.value = -99
>>> print(i.value)
-99
>>>
Присвоение нового значения экземплярам типов указателей c_char_p
, c_wchar_p
, и c_void_p
изменяет местоположение в памяти, на которое они указывают, а не содержимое блока памяти (конечно, нет, потому что объекты Python bytes неизменяемы).:
>>> s = "Hello, World"
>>> c_s = c_wchar_p(s)
>>> print(c_s)
c_wchar_p(139966785747344)
>>> print(c_s.value)
Hello World
>>> c_s.value = "Hi, there"
>>> print(c_s) # the memory location has changed
c_wchar_p(139966783348904)
>>> print(c_s.value)
Hi, there
>>> print(s) # first object is unchanged
Hello, World
>>>
Однако вам следует быть осторожным и не передавать их функциям, ожидающим указатели на изменяемую память. Если вам нужны изменяемые блоки памяти, в ctypes есть функция create_string_buffer()
, которая создает их различными способами. Доступ к текущему содержимому блока памяти (или его изменение) можно получить с помощью свойства raw
; если вы хотите получить доступ к нему в виде строки, заканчивающейся нулем, используйте свойство value
:
>>> from ctypes import *
>>> p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes
>>> print(sizeof(p), repr(p.raw))
3 b'\x00\x00\x00'
>>> p = create_string_buffer(b"Hello") # create a buffer containing a NUL terminated string
>>> print(sizeof(p), repr(p.raw))
6 b'Hello\x00'
>>> print(repr(p.value))
b'Hello'
>>> p = create_string_buffer(b"Hello", 10) # create a 10 byte buffer
>>> print(sizeof(p), repr(p.raw))
10 b'Hello\x00\x00\x00\x00\x00'
>>> p.value = b"Hi"
>>> print(sizeof(p), repr(p.raw))
10 b'Hi\x00lo\x00\x00\x00\x00\x00'
>>>
Функция create_string_buffer()
заменяет старую функцию c_buffer()
(которая все еще доступна в качестве псевдонима). Чтобы создать изменяемый блок памяти, содержащий символы юникода типа C wchar_t
, используйте функцию create_unicode_buffer()
.
Вызывающие функции, продолжение¶
Обратите внимание, что printf выводит данные на реальный стандартный канал вывода, а не на sys.stdout
, поэтому эти примеры будут работать только в командной строке консоли, а не из IDLE или PythonWin:
>>> printf = libc.printf
>>> printf(b"Hello, %s\n", b"World!")
Hello, World!
14
>>> printf(b"Hello, %S\n", "World!")
Hello, World!
14
>>> printf(b"%d bottles of beer\n", 42)
42 bottles of beer
19
>>> printf(b"%f bottles of beer\n", 42.5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ArgumentError: argument 2: TypeError: Don't know how to convert parameter 2
>>>
Как упоминалось ранее, все типы объектов Python, за исключением integers, strings и 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_
и использует его в качестве аргумента функции. Атрибут должен быть целым числом, строкой, байтами, экземпляром ctypes
или объектом с атрибутом _as_parameter_
:
>>> class Bottles:
... def __init__(self, number):
... self._as_parameter_ = number
...
>>> bottles = Bottles(42)
>>> printf(b"%d bottles of beer\n", bottles)
42 bottles of beer
19
>>>
Если вы не хотите хранить данные экземпляра в переменной экземпляра _as_parameter_
, вы можете определить property
, которая делает атрибут доступным по запросу.
Указание требуемых типов аргументов (прототипов функций)¶
Можно указать требуемые типы аргументов функций, экспортируемых из библиотек DLL, установив атрибут argtypes
.
argtypes
должно быть последовательностью типов данных C (функция printf
, вероятно, здесь не является хорошим примером, поскольку она принимает переменное число и различные типы параметров в зависимости от строки формата, с другой стороны, это довольно удобно для экспериментов с помощью этой функции):
>>> printf.argtypes = [c_char_p, c_char_p, c_int, c_double]
>>> printf(b"String '%s', Int %d, Double %f\n", b"Hi", 10, 2.2)
String 'Hi', Int 10, Double 2.200000
37
>>>
Указание формата защищает от несовместимых типов аргументов (как прототип для функции C) и пытается преобразовать аргументы в допустимые типы:
>>> printf(b"%d %d %d", 1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ArgumentError: argument 2: TypeError: wrong type
>>> printf(b"%s %d %f\n", b"X", 2, 3)
X 2 3.000000
13
>>>
Если вы определили свои собственные классы, которые вы передаете вызовам функций, вам необходимо реализовать метод класса from_param()
, чтобы они могли использовать их в последовательности argtypes
. Метод класса from_param()
получает объект Python, переданный в вызов функции, он должен выполнить проверку типа или что-то еще, что необходимо, чтобы убедиться, что этот объект приемлем, а затем вернуть сам объект, его атрибут _as_parameter_
или все, что вы хотите передать в данном случае в качестве аргумента функции C . Опять же, результатом должно быть целое число, строка, байты, экземпляр ctypes
или объект с атрибутом _as_parameter_
.
Типы возвращаемых данных¶
По умолчанию предполагается, что функции возвращают тип C int. Другие типы возвращаемых данных можно задать, установив атрибут restype
для объекта function.
Вот более продвинутый пример, в нем используется функция strchr
, которая ожидает указатель на строку и символ и возвращает указатель на строку:
>>> strchr = libc.strchr
>>> strchr(b"abcdef", ord("d"))
8059983
>>> strchr.restype = c_char_p # c_char_p is a pointer to a string
>>> strchr(b"abcdef", ord("d"))
b'def'
>>> print(strchr(b"abcdef", ord("x")))
None
>>>
Если вы хотите избежать описанных выше вызовов ord("x")
, вы можете установить атрибут argtypes
, и второй аргумент будет преобразован из односимвольного объекта Python bytes в символ C:
>>> strchr.restype = c_char_p
>>> strchr.argtypes = [c_char_p, c_char]
>>> strchr(b"abcdef", b"d")
'def'
>>> strchr(b"abcdef", b"def")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ArgumentError: argument 2: TypeError: one character string expected
>>> print(strchr(b"abcdef", b"x"))
None
>>> strchr(b"abcdef", b"d")
'def'
>>>
Вы также можете использовать вызываемый объект Python (например, функцию или класс) в качестве атрибута restype
, если внешняя функция возвращает целое число. Вызываемый объект будет вызываться с целым числом, возвращаемым функцией C, и результат этого вызова будет использоваться в качестве результата вызова вашей функции. Это полезно для проверки возвращаемых значений на наличие ошибок и автоматического создания исключения:
>>> GetModuleHandle = windll.kernel32.GetModuleHandleA
>>> def ValidHandle(value):
... if value == 0:
... raise WinError()
... return value
...
>>>
>>> GetModuleHandle.restype = ValidHandle
>>> GetModuleHandle(None)
486539264
>>> GetModuleHandle("something silly")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in ValidHandle
OSError: [Errno 126] The specified module could not be found.
>>>
WinError
- это функция, которая вызывает Windows FormatMessage()
api для получения строкового представления кода ошибки и возвращает исключение. 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
: структура, объединение, массив, указатель.
Вот простой пример точечной структуры, которая содержит два целых числа с именами 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
>>>
Однако вы можете создавать гораздо более сложные структуры. Структура сама по себе может содержать другие структуры, используя структуру в качестве типа поля.
Вот прямоугольная структура, которая содержит две точки с именами 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))
Поля descriptor можно извлечь из class, они полезны для отладки, поскольку могут содержать полезную информацию:
>>> print(POINT.x)
<Field type=c_long, ofs=0, size=4>
>>> print(POINT.y)
<Field type=c_long, ofs=4, size=4>
>>>
Предупреждение
ctypes
не поддерживает передачу объединений или структур с битовыми полями в функции по значению. Хотя это может работать на 32-разрядной платформе x86, библиотека не гарантирует, что это сработает в общем случае. Объединения и структуры с битовыми полями всегда должны передаваться функциям по указателю.
Выравнивание структуры/объединения и порядок байтов¶
По умолчанию поля Structure и Union выровнены таким же образом, как это делает компилятор C. Можно переопределить это поведение, указав атрибут _pack_
class в определении подкласса. Это значение должно быть положительным целым числом и указывать максимальное выравнивание полей. Это то, что #pragma pack(n)
также выполняется в MSVC.
ctypes
использует собственный порядок байтов для структур и объединений. Для создания структур с нестандартным порядком байтов вы можете использовать один из базовых классов BigEndianStructure
, LittleEndianStructure
, BigEndianUnion
, и LittleEndianUnion
. Эти классы не могут содержать поля указателей.
Битовые поля в структурах и объединениях¶
Можно создавать структуры и объединения, содержащие битовые поля. Битовые поля возможны только для целых полей, битовая ширина указывается в качестве третьего элемента в кортежах _fields_
:
>>> class Int(Structure):
... _fields_ = [("first_16", c_int, 16),
... ("second_16", c_int, 16)]
...
>>> print(Int.first_16)
<Field type=c_long, ofs=0:0, bits=16>
>>> print(Int.second_16)
<Field type=c_long, ofs=0:16, bits=16>
>>>
Массивы¶
Массивы - это последовательности, содержащие фиксированное количество экземпляров одного и того же типа.
Рекомендуемый способ создания типов массивов - это умножение типа данных на положительное целое число:
TenPointsArrayType = POINT * 10
Вот пример несколько искусственного типа данных, структура которого, помимо прочего, содержит 4 точки:
>>> 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
не возвращает значение OUR (исходный объект), он создает новый эквивалентный объект каждый раз, когда вы извлекаете атрибут:
>>> 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...>
>>>
Вызов типа pointer без аргумента создает указатель 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)
types принимает массив 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()
.
Чтобы задать полю типа УКАЗАТЕЛЯ значение 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
недоступен в самой инструкции 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. Их иногда называют функциями обратного вызова.
Во-первых, вы должны создать класс для функции обратного вызова. Класс знает соглашение о вызове, тип возвращаемого значения, а также количество и типы аргументов, которые будет принимать эта функция.
Фабричная функция CFUNCTYPE()
создает типы для функций обратного вызова, используя соглашение о вызове cdecl
. В Windows фабричная функция WINFUNCTYPE()
создает типы для функций обратного вызова, используя соглашение о вызове stdcall
.
Обе эти фабричные функции вызываются с типом результата в качестве первого аргумента, а функции обратного вызова используют ожидаемые типы аргументов в качестве остальных аргументов.
Здесь я приведу пример, в котором используется функция стандартной библиотеки C qsort()
, которая используется для сортировки элементов с помощью функции обратного вызова. qsort()
будет использоваться для сортировки массива целых чисел:
>>> IntArray5 = c_int * 5
>>> ia = IntArray5(5, 1, 7, 33, 99)
>>> qsort = libc.qsort
>>> qsort.restype = None
>>>
qsort()
должен вызываться с указателем на данные для сортировки, количество элементов в массиве данных, размер одного элемента и указатель на функцию сравнения, обратный вызов. Затем будет вызван обратный вызов с двумя указателями на элементы, и он должен вернуть отрицательное целое число, если первый элемент меньше второго, ноль, если они равны, и положительное целое число в противном случае.
Таким образом, наша функция обратного вызова получает указатели на целые числа и должна возвращать целое число. Сначала мы создаем type
для функции обратного вызова:
>>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
>>>
Для начала, вот простой обратный вызов, который показывает передаваемые значения:
>>> def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return 0
...
>>> cmp_func = CMPFUNC(py_cmp_func)
>>>
Результат:
>>> qsort(ia, len(ia), sizeof(c_int), cmp_func)
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 5 7
py_cmp_func 1 7
>>>
Теперь мы действительно можем сравнить эти два элемента и получить полезный результат:
>>> def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return a[0] - b[0]
...
>>>
>>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func))
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>
Как мы можем легко проверить, теперь наш массив отсортирован:
>>> for i in ia: print(i, end=" ")
...
1 5 7 33 99
>>>
Фабрики функций можно использовать в качестве фабрик декораторов, поэтому мы также можем написать:
>>> @CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
... def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return a[0] - b[0]
...
>>> qsort(ia, len(ia), sizeof(c_int), py_cmp_func)
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>
Примечание
Убедитесь, что вы сохраняете ссылки на объекты CFUNCTYPE()
до тех пор, пока они используются из кода на C. ctypes
этого не делает, и если вы этого не сделаете, они могут быть собраны мусором, что приведет к сбою вашей программы при выполнении обратного вызова.
Также обратите внимание, что если функция обратного вызова вызывается в потоке, созданном вне контроля Python (например, с помощью внешнего кода, который вызывает обратный вызов), ctypes создает новый фиктивный поток Python при каждом вызове. Такое поведение корректно для большинства целей, но это означает, что значения, сохраненные с помощью threading.local
, не будут сохраняться при разных обратных вызовах, даже если эти вызовы выполняются из одного и того же потока C.
Доступ к значениям, экспортированным из библиотек dll¶
Некоторые совместно используемые библиотеки экспортируют не только функции, но и переменные. Примером в самой библиотеке Python является Py_OptimizeFlag
, целое число, равное 0, 1 или 2, в зависимости от флага -O
или -OO
, заданного при запуске.
ctypes
может получать доступ к таким значениям с помощью методов класса in_dll()
типа. pythonapi - это предопределенный символ, предоставляющий доступ к api Python C:
>>> opt_flag = c_int.in_dll(pythonapi, "Py_OptimizeFlag")
>>> print(opt_flag)
c_long(0)
>>>
Если бы интерпретатор был запущен с -O
, в примере было бы напечатано c_long(1)
или c_long(2)
, если бы было указано -OO
.
Расширенный пример, который также демонстрирует использование указателей, обращается к указателю PyImport_FrozenModules
, экспортированному Python.
Цитирую документы для этого значения:
Этот указатель инициализируется как указывающий на массив записей
_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 - это объекты, содержащие блок памяти плюс некоторые descriptor, которые обращаются к содержимому памяти. Сохранение объекта Python в блоке памяти не приводит к сохранению самого объекта, вместо этого сохраняется contents
объекта. При повторном обращении к содержимому каждый раз создается новый объект Python!
Типы данных переменного размера¶
ctypes
обеспечивает некоторую поддержку массивов и структур переменного размера.
Функция resize()
может использоваться для изменения размера буфера памяти существующего объекта ctypes. Функция принимает объект в качестве первого аргумента и запрашиваемый размер в байтах в качестве второго аргумента. Размер блока памяти не может быть меньше размера естественного блока памяти, указанного типом objects, при попытке выполнения этого действия генерируется значение 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
>>>
Это хорошо, но как можно получить доступ к дополнительным элементам, содержащимся в этом массиве? Поскольку type по-прежнему знает только о 4 элементах, мы получаем ошибки при доступе к другим элементам:
>>> short_array[:]
[0, 0, 0, 0]
>>> short_array[7]
Traceback (most recent call last):
...
IndexError: invalid index
>>>
Другой способ использовать типы данных переменного размера с ctypes
- это использовать динамическую природу Python и (повторно) определять тип данных после того, как требуемый размер уже известен, в каждом конкретном случае.
ссылка на ctypes¶
Иностранные функции¶
Как объяснялось в предыдущем разделе, к внешним функциям можно обращаться как к атрибутам загруженных разделяемых библиотек. Созданные таким образом функциональные объекты по умолчанию принимают любое количество аргументов, принимают в качестве аргументов экземпляры данных любых типов и возвращают тип результата по умолчанию, указанный загрузчиком библиотеки. Они являются экземплярами закрытого класса:
- class ctypes._FuncPtr¶
Базовый класс для внешних функций, вызываемых на языке Си.
Экземпляры внешних функций также являются типами данных, совместимыми с C; они представляют собой указатели на функции C.
Это поведение можно настроить, назначив специальные атрибуты объекту внешней функции.
- restype¶
Назначьте тип ctypes, чтобы указать тип результата внешней функции. Используйте
None
для void, функции, которая ничего не возвращает.Можно назначить вызываемый объект Python, который не является типом ctypes, в этом случае предполагается, что функция возвращает C int, и вызываемый объект будет вызываться с этим целым числом, что позволяет выполнять дальнейшую обработку или проверку ошибок. Использование этого параметра не рекомендуется, для более гибкой последующей обработки или проверки ошибок используйте тип данных ctypes как
restype
и назначьте вызываемый атрибут атрибутуerrcheck
.
- argtypes¶
Назначьте набор типов types, чтобы указать типы аргументов, которые принимает функция. Функции, использующие соглашение о вызове
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¶
Это исключение возникает, когда вызов внешней функции не может преобразовать один из переданных аргументов.
Создает auditing event ctypes.seh_exception
с аргументом code
.
Создает auditing event ctypes.call_function
с аргументами func_pointer
, arguments
.
Прототипы функций¶
Внешние функции также могут быть созданы путем создания экземпляров прототипов функций. Прототипы функций аналогичны прототипам функций в C; они описывают функцию (тип возвращаемого значения, типы аргументов, соглашение о вызове) без определения реализации. Фабричные функции должны вызываться с требуемым типом результата и типами аргументов функции и могут использоваться в качестве фабрик-декораторов и, как таковые, применяться к функциям с помощью синтаксиса @wrapper
. Примеры приведены в Функции обратного вызова.
- ctypes.CFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)¶
Возвращаемый прототип функции создает функции, которые используют стандартное соглашение о вызове C. Функция освободит GIL во время вызова. Если для параметра use_errno установлено значение true, личная копия системной переменной
errno
заменяется на реальное значениеerrno
до и после вызова; use_last_error делает то же самое для кода ошибки Windows.
- ctypes.WINFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)¶
Только для Windows: Возвращаемый прототип функции создает функции, которые используют соглашение о вызове
stdcall
. Функция освободит GIL во время вызова. use_errno и use_last_error имеют то же значение, что и выше.
- ctypes.PYFUNCTYPE(restype, *argtypes)¶
Возвращаемый прототип функции создает функции, которые используют соглашение о вызове Python. Функция не будет * освобождать GIL во время вызова.
Прототипы функций, созданные этими фабричными функциями, могут быть созданы различными способами, в зависимости от типа и количества параметров в вызове:
- prototype(address)
Возвращает внешнюю функцию по указанному адресу, который должен быть целым числом.
- prototype(callable)
Создайте вызываемую функцию C (функцию обратного вызова) из вызываемой функции Python.
- prototype(func_spec[, paramflags])
Возвращает внешнюю функцию, экспортированную из общей библиотеки. func_spec должен содержать 2 кортежа
(name_or_ordinal, library)
. Первый элемент - это имя экспортируемой функции в виде строки или порядковый номер экспортируемой функции в виде небольшого целого числа. Второй элемент - это экземпляр общей библиотеки.
- prototype(vtbl_index, name[, paramflags[, iid]])
Возвращает внешнюю функцию, которая вызовет COM-метод. vtbl_index - это индекс в таблице виртуальной функции, небольшое неотрицательное целое число. name - это имя COM-метода. iid - это необязательный указатель на идентификатор интерфейса, который используется в расширенных отчетах об ошибках.
COM-методы используют специальное соглашение о вызове: они требуют указатель на COM-интерфейс в качестве первого аргумента в дополнение к тем параметрам, которые указаны в кортеже
argtypes
.
Необязательный параметр paramflags создает внешние оболочки функций с гораздо большей функциональностью, чем функции, описанные выше.
paramflags должен быть кортежем той же длины, что и argtypes
.
Каждый элемент в этом кортеже содержит дополнительную информацию о параметре, это должен быть кортеж, содержащий один, два или три элемента.
Первый элемент - это целое число, содержащее комбинацию флагов направления для параметра:
- 1
Задает входной параметр для функции.
- 2
Выходной параметр. Внешняя функция вводит значение.
- 4
Входной параметр, значение которого по умолчанию равно целочисленному нулю.
Вторым необязательным элементом является имя параметра в виде строки. Если это указано, внешняя функция может быть вызвана с именованными параметрами.
Необязательный третий элемент является значением по умолчанию для этого параметра.
В следующем примере показано, как обернуть функцию Windows MessageBoxW
таким образом, чтобы она поддерживала параметры по умолчанию и именованные аргументы. Объявление на языке C из заголовочного файла Windows выглядит следующим образом:
WINUSERAPI int WINAPI
MessageBoxW(
HWND hWnd,
LPCWSTR lpText,
LPCWSTR lpCaption,
UINT uType);
Вот обертывание с помощью ctypes
:
>>> from ctypes import c_int, WINFUNCTYPE, windll
>>> from ctypes.wintypes import HWND, LPCWSTR, UINT
>>> prototype = WINFUNCTYPE(c_int, HWND, LPCWSTR, LPCWSTR, UINT)
>>> paramflags = (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", "Hello from ctypes"), (1, "flags", 0)
>>> MessageBox = prototype(("MessageBoxW", windll.user32), paramflags)
Внешняя функция MessageBox
теперь может быть вызвана следующими способами:
>>> MessageBox()
>>> MessageBox(text="Spam, spam, spam")
>>> MessageBox(flags=2, text="foo bar")
Второй пример демонстрирует выходные параметры. Функция win32 GetWindowRect
извлекает размеры указанного окна, копируя их в структуру RECT
, которую должен предоставить вызывающий объект. Вот объявление на языке Си:
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
api возвращает значение BOOL
, сигнализирующее об успехе или неудаче, поэтому эта функция может выполнять проверку ошибок и генерировать исключение при сбое вызова api:
>>> def errcheck(result, func, args):
... if not result:
... raise WinError()
... return args
...
>>> GetWindowRect.errcheck = errcheck
>>>
Если функция errcheck
возвращает полученный ею кортеж аргументов без изменений, ctypes
продолжает обычную обработку выходных параметров. Если вы хотите вернуть набор координат окна вместо экземпляра RECT
, вы можете извлечь поля из функции и вернуть их вместо этого, обычная обработка больше не будет выполняться:
>>> def errcheck(result, func, args):
... if not result:
... raise WinError()
... rc = args[1]
... return rc.left, rc.top, rc.bottom, rc.right
...
>>> GetWindowRect.errcheck = errcheck
>>>
Функции полезности¶
- ctypes.addressof(obj)¶
Возвращает адрес буфера памяти в виде целого числа. obj должен быть экземпляром типа ctypes.
Создает auditing event
ctypes.addressof
с аргументомobj
.
- ctypes.alignment(obj_or_type)¶
Возвращает требования к выравниванию для типа ctypes. obj_or_type должен быть типом или экземпляром ctypes.
- ctypes.byref(obj[, offset])¶
Возвращает упрощенный указатель на obj, который должен быть экземпляром типа ctypes. Значение offset по умолчанию равно нулю и должно быть целым числом, которое будет добавлено к значению внутреннего указателя.
byref(obj, offset)
соответствует этому коду на языке Си:(((char *)&obj) + offset)
Возвращаемый объект может использоваться только как параметр вызова внешней функции. Он ведет себя аналогично
pointer(obj)
, но построение выполняется намного быстрее.
- ctypes.cast(obj, type)¶
Эта функция аналогична оператору cast в C. Она возвращает новый экземпляр type, который указывает на тот же блок памяти, что и obj. type должен быть типом указателя, а obj должен быть объектом, который можно интерпретировать как указатель.
- ctypes.create_string_buffer(init_or_size, size=None)¶
Эта функция создает изменяемый символьный буфер. Возвращаемый объект представляет собой массив ctypes
c_char
.init_or_size должно быть целым числом, определяющим размер массива, или объектом bytes, который будет использоваться для инициализации элементов массива.
Если в качестве первого аргумента указан объект bytes, размер буфера увеличивается на один элемент по сравнению с его длиной, так что последний элемент в массиве является символом завершения NUL. В качестве второго аргумента может быть передано целое число, которое позволяет указать размер массива, если не следует использовать длину байтов.
Создает auditing event
ctypes.create_string_buffer
с аргументамиinit
,size
.
- ctypes.create_unicode_buffer(init_or_size, size=None)¶
Эта функция создает изменяемый буфер символов unicode. Возвращаемый объект представляет собой массив ctypes, содержащий
c_wchar
.init_or_size должно быть целым числом, определяющим размер массива, или строкой, которая будет использоваться для инициализации элементов массива.
Если в качестве первого аргумента указана строка, размер буфера увеличивается на один элемент по сравнению с длиной строки, так что последний элемент в массиве является символом завершения NUL. В качестве второго аргумента может быть передано целое число, которое позволяет указать размер массива, если не следует использовать длину строки.
Создает auditing event
ctypes.create_unicode_buffer
с аргументамиinit
,size
.
- ctypes.DllCanUnloadNow()¶
Только для Windows: Эта функция представляет собой дополнение, позволяющее внедрять внутрипроцессные COM-серверы с ctypes. Она вызывается из функции DllCanUnloadNow, которую экспортирует библиотека расширения _ctypes.
- ctypes.DllGetClassObject()¶
Только для Windows: Эта функция является подсчетом, позволяющим внедрять внутрипроцессные COM-серверы с ctypes. Она вызывается из функции DllGetClassObject, которую экспортирует библиотека расширения
_ctypes
.
- ctypes.util.find_library(name)¶
Попробуйте найти библиотеку и вернуть путь к ней. name - это имя библиотеки без какого-либо префикса типа
lib
, суффикса типа.so
,.dylib
или номера версии (это форма, используемая для параметра компоновщика posix-l
). Если библиотека не найдена, возвращаетNone
.Точная функциональность зависит от системы.
- ctypes.util.find_msvcrt()¶
Только для Windows: возвращает имя файла библиотеки времени выполнения VC, используемой Python и модулями расширения. Если имя библиотеки не может быть определено, возвращается
None
.Если вам нужно освободить память, например, выделенную модулем расширения с помощью вызова
free(void *)
, важно, чтобы вы использовали функцию в той же библиотеке, которая выделила память.
- ctypes.FormatError([code])¶
Только для Windows: Возвращает текстовое описание кода ошибки code. Если код ошибки не указан, используется последний код ошибки при вызове функции Windows api GetLastError.
- ctypes.GetLastError()¶
Только для Windows: возвращает последний код ошибки, заданный Windows в вызывающем потоке. Эта функция вызывает функцию Windows
GetLastError()
напрямую, она не возвращает личную копию кода ошибки ctypes.
- ctypes.get_errno()¶
Возвращает текущее значение ctypes-частной копии системной переменной
errno
в вызывающем потоке.Выдает auditing event
ctypes.get_errno
без каких-либо аргументов.
- ctypes.get_last_error()¶
Только для Windows: возвращает текущее значение ctypes-частной копии системной переменной
LastError
в вызывающем потоке.Выдает auditing event
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 значением count в байтах. dst должно быть целым числом, указывающим адрес, или экземпляром ctypes.
- ctypes.POINTER(type, /)¶
Создайте и верните новый тип указателя ctypes. Типы указателей кэшируются и повторно используются внутри системы, поэтому повторный вызов этой функции обходится дешево. type должен быть типом ctypes.
- ctypes.pointer(obj, /)¶
Создайте новый экземпляр указателя, указывающий на obj. Возвращаемый объект имеет тип
POINTER(type(obj))
.Примечание: Если вы просто хотите передать указатель на объект вызову внешней функции, вам следует использовать
byref(obj)
, что намного быстрее.
- ctypes.resize(obj, size)¶
Эта функция изменяет размер буфера внутренней памяти obj, который должен быть экземпляром типа ctypes. Невозможно уменьшить размер буфера по сравнению с исходным размером типа objects, как указано в
sizeof(type(obj))
, но можно увеличить размер буфера.
- ctypes.set_errno(value)¶
Установите текущее значение переменной ctypes-частной копии системной переменной
errno
в вызывающем потоке равным value и верните предыдущее значение.Создает auditing event
ctypes.set_errno
с аргументомerrno
.
- ctypes.set_last_error(value)¶
Только для Windows: установите текущее значение переменной ctypes-частной копии системной
LastError
в вызывающем потоке равным value и верните предыдущее значение.Создает auditing event
ctypes.set_last_error
с аргументомerror
.
- ctypes.sizeof(obj_or_type)¶
Возвращает размер в байтах буфера памяти типа ctypes или экземпляра. Выполняет те же действия, что и оператор C
sizeof
.
- ctypes.string_at(address, size=-1)¶
Эта функция возвращает строку на языке Си, начинающуюся с адреса памяти address, в виде объекта bytes. Если указан размер, он используется в качестве size, в противном случае предполагается, что строка заканчивается нулем.
Создает auditing event
ctypes.string_at
с аргументамиaddress
,size
.
- ctypes.WinError(code=None, descr=None)¶
Только для Windows: эта функция, вероятно, имеет самое неудачное название в ctypes. Она создает экземпляр
OSError
. Если code не указан, для определения кода ошибки вызываетсяGetLastError
. Если параметр descr не указан, вызываетсяFormatError()
для получения текстового описания ошибки.Изменено в версии 3.3: Раньше создавался экземпляр
WindowsError
, который теперь является псевдонимомOSError
.
- ctypes.wstring_at(address, size=-1)¶
Эта функция возвращает расширенную символьную строку, начинающуюся с адреса памяти address, в виде строки. Если указан размер *, он используется в качестве количества символов в строке, в противном случае предполагается, что строка заканчивается нулем.
Создает auditing event
ctypes.wstring_at
с аргументамиaddress
,size
.
Типы данных¶
- class ctypes._CData¶
Этот закрытый класс является общим базовым классом для всех типов данных types. Помимо прочего, все экземпляры типа ctypes содержат блок памяти, в котором хранятся данные, совместимые с C; адрес блока памяти возвращается вспомогательной функцией
addressof()
. Другая переменная экземпляра представлена как_objects
; она содержит другие объекты Python, которые необходимо сохранить активными на случай, если блок памяти содержит указатели.Общие методы типов данных ctypes, это все методы класса (если быть точным, это методы metaclass):
- from_buffer(source[, offset])¶
Этот метод возвращает экземпляр ctypes, который совместно использует буфер исходного объекта. Исходный объект должен поддерживать интерфейс буфера, доступный для записи. Необязательный параметр offset определяет смещение в исходном буфере в байтах; значение по умолчанию равно нулю. Если исходный буфер недостаточно велик, генерируется значение
ValueError
.Создает auditing event
ctypes.cdata/buffer
с аргументамиpointer
,size
,offset
.
- from_buffer_copy(source[, offset])¶
Этот метод создает экземпляр ctypes, копируя буфер из буфера объекта source, который должен быть доступен для чтения. Необязательный параметр offset определяет смещение в исходном буфере в байтах; значение по умолчанию равно нулю. Если исходный буфер недостаточно велик, генерируется значение
ValueError
.Создает auditing event
ctypes.cdata/buffer
с аргументамиpointer
,size
,offset
.
- from_address(address)¶
Этот метод возвращает экземпляр типа ctypes, используя память, указанную в address, которая должна быть целым числом.
Создает auditing event
ctypes.cdata
с аргументомaddress
.
- from_param(obj)¶
Этот метод адаптирует obj к типу ctypes. Он вызывается с использованием фактического объекта, используемого при вызове внешней функции, когда тип присутствует в кортеже внешней функции
argtypes
; он должен возвращать объект, который может использоваться в качестве параметра вызова функции.Все типы данных ctypes имеют реализацию этого метода класса по умолчанию, которая обычно возвращает obj, если это экземпляр типа. Некоторые типы также принимают другие объекты.
- in_dll(library, name)¶
Этот метод возвращает экземпляр типа ctypes, экспортированный общей библиотекой. name - это название символа, который экспортирует данные, library - загруженная общая библиотека.
Общие переменные экземпляра типов типы данных:
- _b_base_¶
Иногда экземпляры данных ctypes не владеют содержащимся в них блоком памяти, вместо этого они совместно используют часть блока памяти базового объекта. Элемент
_b_base_
, доступный только для чтения, является корневым объектом ctypes, которому принадлежит блок памяти.
- _b_needsfree_¶
Эта переменная, доступная только для чтения, имеет значение true, когда экземпляр данных ctypes сам выделил блок памяти, в противном случае значение false.
- _objects¶
Этот элемент является либо
None
, либо словарем, содержащим объекты Python, которые необходимо поддерживать в рабочем состоянии, чтобы содержимое блока памяти оставалось действительным. Этот объект доступен только для отладки; никогда не изменяйте содержимое этого словаря.
Основные типы данных¶
- class ctypes._SimpleCData¶
Этот закрытый класс является базовым классом для всех типов данных fundamental types. Он упоминается здесь потому, что содержит общие атрибуты типов данных fundamental ctypes.
_SimpleCData
является подклассом_CData
, поэтому он наследует их методы и атрибуты. теперь можно выбирать типы данных ctypes, которые не являются указателями и не содержат их.Экземпляры имеют один атрибут:
- value¶
Этот атрибут содержит фактическое значение экземпляра. Для типов integer и pointer это целое число, для символьных типов - объект или строка в байтах с одним символом, для типов символьных указателей - объект или строка в байтах Python.
Когда атрибут
value
извлекается из экземпляра ctypes, обычно каждый раз возвращается новый объект.ctypes
не реализует возврат исходного объекта, всегда создается новый объект. То же самое верно для всех экземпляров объектов других типов.
Основные типы данных, возвращаемые в виде результатов вызова внешней функции или, например, путем извлечения элементов структурного поля или массива, прозрачно преобразуются в собственные типы Python. Другими словами, если внешняя функция имеет restype
из c_char_p
, вы всегда будете получать объект Python bytes, а не экземпляр c_char_p
.
Подклассы основных типов данных не наследуют это поведение. Таким образом, если внешняя функция restype
является подклассом c_void_p
, вы получите экземпляр этого подкласса при вызове функции. Конечно, вы можете получить значение указателя, обратившись к атрибуту value
.
Это основные типы типы данных:
- 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¶
Представляет 8-разрядный код C :тип данных c:expr:signed int. Обычно это псевдоним для
c_byte
.
- class ctypes.c_int16¶
Представляет 16-разрядный код C :тип данных C:expr:signed int. Обычно это псевдоним для
c_short
.
- class ctypes.c_int32¶
Представляет 32-разрядный код C :тип данных c:expr:signed int. Обычно это псевдоним для
c_int
.
- class ctypes.c_int64¶
Представляет 64-разрядный код C :тип данных c:expr:signed int. Обычно это псевдоним для
c_longlong
.
- class ctypes.c_long¶
Представляет тип данных C signed long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
- class ctypes.c_longlong¶
Представляет тип данных C signed long long. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
- class ctypes.c_short¶
Представляет тип данных C signed short. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
- class ctypes.c_size_t¶
Представляет тип данных C
size_t
.
- class ctypes.c_ssize_t¶
Представляет тип данных C
ssize_t
.Добавлено в версии 3.2.
- class ctypes.c_ubyte¶
Представляет тип данных C unsigned char, он интерпретирует значение как малое целое число. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется.
- class ctypes.c_uint¶
Представляет тип данных C unsigned int. Конструктор принимает необязательный целочисленный инициализатор; проверка переполнения не выполняется. На платформах, где
sizeof(int) == sizeof(long)
является псевдонимом дляc_ulong
.
- class ctypes.c_uint8¶
Представляет 8-разрядный код C :тип данных c:expr:unsigned int. Обычно это псевдоним для
c_ubyte
.
- class ctypes.c_uint16¶
Представляет 16-разрядный код C :тип данных C:expr:unsigned int. Обычно это псевдоним для
c_ushort
.
- class ctypes.c_uint32¶
Представляет 32-разрядный код C :тип данных c:expr:unsigned int. Обычно это псевдоним для
c_uint
.
- class ctypes.c_uint64¶
Представляет 64-разрядный код C :тип данных c:expr: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¶
Только для Windows: Представляет значение
HRESULT
, которое содержит информацию об успешном завершении или ошибке вызова функции или метода.
- class ctypes.py_object¶
Представляет тип данных C PyObject*. При вызове этого параметра без аргумента создается указатель
NULL
PyObject*.
Модуль ctypes.wintypes
предоставляет некоторые другие типы данных, специфичные для Windows, например : c:type:HWND, WPARAM
или DWORD
. Также определены некоторые полезные структуры, такие как MSG
или RECT
.
Структурированные типы данных¶
- class ctypes.Union(*args, **kw)¶
Абстрактный базовый класс для объединений в собственном порядке байтов.
- class ctypes.BigEndianUnion(*args, **kw)¶
Абстрактный базовый класс для объединений в порядке байтов по *большому порядку следования байтов.
Добавлено в версии 3.11.
- class ctypes.LittleEndianUnion(*args, **kw)¶
Абстрактный базовый класс для объединений в порядке байтов по строкам со строчной запятой.
Добавлено в версии 3.11.
- class ctypes.BigEndianStructure(*args, **kw)¶
Абстрактный базовый класс для структур в *порядке следования по порядку байтов.
- class ctypes.LittleEndianStructure(*args, **kw)¶
Абстрактный базовый класс для структур, расположенных в порядке байтов.
Структуры и объединения с нестандартным порядком байтов не могут содержать поля типа указателя или любые другие типы данных, содержащие поля типа указателя.
- class ctypes.Structure(*args, **kw)¶
Абстрактный базовый класс для структур в собственном порядке байтов.
Конкретные типы структур и объединений должны быть созданы путем создания подкласса одного из этих типов и, по крайней мере, определения переменной класса
_fields_
.ctypes
создаст descriptor, которые позволяют считывать и записывать поля с помощью прямого доступа к атрибутам. Это те самые- _fields_¶
Последовательность, определяющая структуру полей. Элементы должны состоять из 2-х или 3-х кортежей. Первый элемент - это название поля, второй элемент определяет тип поля; это может быть любой тип данных ctypes.
Для полей целочисленного типа, таких как
c_int
, может быть задан третий необязательный элемент. Это должно быть небольшое положительное целое число, определяющее разрядность поля.Имена полей должны быть уникальными в пределах одной структуры или объединения. Этот флажок не установлен, при повторении имен можно получить доступ только к одному полю.
Можно определить переменную
_fields_
class *после инструкции class, которая определяет подкласс Structure, это позволяет создавать типы данных, которые прямо или косвенно ссылаются сами на себя:class List(Structure): pass List._fields_ = [("pnext", POINTER(List)), ... ]
Однако переменная класса
_fields_
должна быть определена до первого использования типа (создается экземпляр, для него вызываетсяsizeof()
и так далее). Более поздние присвоения переменной класса_fields_
вызовут ошибку атрибута.Можно определить подклассы структурных типов, они наследуют поля базового класса плюс
_fields_
, определенные в подклассе, если таковые имеются.
- _pack_¶
Необязательное маленькое целое число, которое позволяет переопределить выравнивание полей структуры в экземпляре.
_pack_
должно быть уже определено при назначении_fields_
, иначе это не будет иметь никакого эффекта.
- _anonymous_¶
Необязательная последовательность, в которой перечислены имена неназванных (анонимных) полей.
_anonymous_
должно быть уже определено при назначении_fields_
, в противном случае это не будет иметь никакого эффекта.Поля, перечисленные в этой переменной, должны быть полями типа structure или union.
ctypes
в типе structure будут созданы дескрипторы, позволяющие напрямую обращаться к вложенным полям без необходимости создания поля structure или union.Вот пример типа (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_
, поля, указанные в ней, добавляются к полям базового класса.Конструкторы Structure и union принимают как позиционные, так и ключевые аргументы. Позиционные аргументы используются для инициализации полей-членов в том же порядке, в котором они отображаются в
_fields_
. Аргументы ключевого слова в конструкторе интерпретируются как присвоения атрибутов, поэтому они будут инициализировать_fields_
с тем же именем или создавать новые атрибуты для имен, отсутствующих в_fields_
.
Массивы и указатели¶
- class ctypes.Array(*args)¶
Абстрактный базовый класс для массивов.
Рекомендуемый способ создания конкретных типов массивов - умножение любого типа данных
ctypes
на неотрицательное целое число. В качестве альтернативы вы можете создать подкласс этого типа и определить переменные класса_length_
и_type_
. Элементы массива могут быть считаны и записаны с использованием стандартных индексов и срезов; при чтении срезов результирующий объект сам по себе не являетсяArray
.- _length_¶
Положительное целое число, указывающее количество элементов в массиве. Индексы, находящиеся вне диапазона, приводят к
IndexError
. Будет возвращено значениеlen()
.
- _type_¶
Определяет тип каждого элемента в массиве.
Конструкторы подкласса Array принимают позиционные аргументы, используемые для инициализации элементов по порядку.
- class ctypes._Pointer¶
Частный, абстрактный базовый класс для указателей.
Конкретные типы указателей создаются путем вызова
POINTER()
с типом, на который будет указано; это делается автоматически с помощьюpointer()
.Если указатель указывает на массив, его элементы могут быть прочитаны и записаны с использованием стандартных индексов и срезов. Объекты-указатели не имеют размера, поэтому
len()
приведет кTypeError
. Отрицательные индексы будут считываться из памяти перед указателем (как в C), а индексы, находящиеся вне диапазона, вероятно, приведут к сбою из-за нарушения доступа (если вам повезет).- _type_¶
Определяет указанный тип.
- contents¶
Возвращает объект, на который указывает to pointer. Присвоение этому атрибуту изменяет указатель так, чтобы он указывал на назначенный объект.