ctypes — A foreign function library for Python


ctypes — це бібліотека зовнішніх функцій для Python. Він надає C-сумісні типи даних і дозволяє викликати функції в DLL або спільних бібліотеках. Його можна використовувати, щоб обернути ці бібліотеки в чистий Python.

підручник ctypes

Примітка: зразки коду в цьому підручнику використовують doctest, щоб переконатися, що вони справді працюють. Оскільки деякі зразки коду поводяться по-різному в Linux, Windows або macOS, вони містять директиви doctest у коментарях.

Примітка. Деякі зразки коду посилаються на тип ctypes c_int. На платформах, де sizeof(long) == sizeof(int), це псевдонім c_long. Отже, вас не повинно бентежити, якщо надруковано c_long, якщо ви очікуєте c_int — вони насправді одного типу.

Доступ до функцій із завантажених dll

Доступ до функцій здійснюється як атрибути об’єктів dll:

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

Note that win32 system dlls like kernel32 and user32 often export ANSI as well as UNICODE versions of a function. The UNICODE version is exported with an W appended to the name, while the ANSI version is exported with an A appended to the name. The win32 GetModuleHandle function, which returns a module handle for a given module name, has the following C prototype, and a macro is used to expose one of them as GetModuleHandle depending on whether UNICODE is defined or not:

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

windll не намагається вибрати одну з них магією, ви повинні отримати доступ до потрібної вам версії, явно вказавши GetModuleHandleA або GetModuleHandleW, а потім викликати його за допомогою байтів або рядкових об’єктів відповідно.

Іноді бібліотеки dll експортують функції з іменами, які не є дійсними ідентифікаторами Python, наприклад "??2@YAPAXI@Z". У цьому випадку ви повинні використовувати getattr(), щоб отримати функцію:

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

У Windows деякі бібліотеки DLL експортують функції не за назвою, а за порядковим номером. Доступ до цих функцій можна отримати, проіндексувавши об’єкт dll з порядковим номером:

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

Функції виклику

You can call these functions like any other Python callable. This example uses the time() function, which returns system time in seconds since the Unix epoch, and the GetModuleHandleA() function, which returns a win32 module handle.

This example calls both functions with a NULL pointer (None should be used as the NULL pointer):

>>> 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, integers, bytes objects and (unicode) strings are the only native Python objects that can directly be used as parameters in these function calls. None is passed as a C NULL pointer, bytes objects and strings are passed as pointer to the memory block that contains their data (char * or wchar_t *). Python integers are passed as the platforms default C int type, their value is masked to fit into the C type.

Перш ніж перейти до виклику функцій з іншими типами параметрів, ми повинні дізнатися більше про типи даних ctypes.

Основні типи даних

ctypes визначає ряд примітивних C-сумісних типів даних:

тип ctypes

тип С

Тип 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 or long long

внутр

c_ulonglong

unsigned __int64 or unsigned long long

внутр

c_size_t

size_t

внутр

c_ssize_t

ssize_t or Py_ssize_t

внутр

c_float

float

плавати

c_double

double

плавати

c_longdouble

long double

плавати

c_char_p

char * (NUL terminated)

bytes або None

c_wchar_p

wchar_t * (NUL terminated)

рядок або None

c_void_p

void *

int або None

  1. Конструктор приймає будь-який об’єкт зі значенням істинності.

Усі ці типи можна створити, викликавши їх із необов’язковим ініціалізатором правильного типу та значення:

>>> c_int()
c_long(0)
>>> c_wchar_p("Hello, World")
c_wchar_p(140018365411392)
>>> c_ushort(-3)
c_ushort(65533)
>>>

Оскільки ці типи є змінними, їх значення також можна змінити пізніше:

>>> i = c_int(42)
>>> print(i)
c_long(42)
>>> print(i.value)
42
>>> i.value = -99
>>> print(i.value)
-99
>>>

Присвоєння нового значення екземплярам типів покажчиків c_char_p, c_wchar_p і c_void_p змінює місце пам’яті, на яке вони вказують, не вміст блоку пам’яті (звичайно, ні, оскільки об’єкти Python 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; якщо ви бажаєте отримати до нього доступ як до рядка з закінченням NUL, використовуйте властивість value:

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

The create_string_buffer() function replaces the c_buffer() function (which is still available as an alias), as well as the c_string() function from earlier ctypes releases. To create a mutable memory block containing unicode characters of the C type wchar_t use the create_unicode_buffer() function.

Функції виклику, продовження

Зауважте, що printf друкує на справжній стандартний канал виводу, а не на sys.stdout, тому ці приклади працюватимуть лише у підказці консолі, а не з IDLE або PythonWin:

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

Як уже згадувалося раніше, усі типи Python, окрім цілих, рядкових і байтових об’єктів, мають бути загорнуті у відповідний тип ctypes, щоб їх можна було перетворити на необхідний тип даних C:

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

Виклик функцій із власними типами даних

You can also customize ctypes argument conversion to allow instances of your own classes be used as function arguments. ctypes looks for an _as_parameter_ attribute and uses this as the function argument. Of course, it must be one of integer, string, or bytes:

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

If you don’t want to store the instance’s data in the _as_parameter_ instance variable, you could define a property which makes the attribute available on request.

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

It is possible to specify the required argument types of functions exported from DLLs by setting the argtypes attribute.

argtypes must be a sequence of C data types (the printf function is probably not a good example here, because it takes a variable number and different types of parameters depending on the format string, on the other hand this is quite handy to experiment with this feature):

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

Зазначення формату захищає від несумісних типів аргументів (як прототип для функції C) і намагається перетворити аргументи на дійсні типи:

>>> printf(b"%d %d %d", 1, 2, 3)
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ArgumentError: argument 2: exceptions.TypeError: wrong type
>>> printf(b"%s %d %f\n", b"X", 2, 3)
X 2 3.000000
13
>>>

If you have defined your own classes which you pass to function calls, you have to implement a from_param() class method for them to be able to use them in the argtypes sequence. The from_param() class method receives the Python object passed to the function call, it should do a typecheck or whatever is needed to make sure this object is acceptable, and then return the object itself, its _as_parameter_ attribute, or whatever you want to pass as the C function argument in this case. Again, the result should be an integer, string, bytes, a ctypes instance, or an object with an _as_parameter_ attribute.

Типи повернення

By default functions are assumed to return the C int type. Other return types can be specified by setting the restype attribute of the function object.

Here is a more advanced example, it uses the strchr function, which expects a string pointer and a char, and returns a pointer to a string:

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

If you want to avoid the ord("x") calls above, you can set the argtypes attribute, and the second argument will be converted from a single character Python bytes object into a C char:

>>> strchr.restype = c_char_p
>>> strchr.argtypes = [c_char_p, c_char]
>>> strchr(b"abcdef", b"d")
'def'
>>> strchr(b"abcdef", b"def")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ArgumentError: argument 2: exceptions.TypeError: one character string expected
>>> print(strchr(b"abcdef", b"x"))
None
>>> strchr(b"abcdef", b"d")
'def'
>>>

You can also use a callable Python object (a function or a class for example) as the restype attribute, if the foreign function returns an integer. The callable will be called with the integer the C function returns, and the result of this call will be used as the result of your function call. This is useful to check for error return values and automatically raise an exception:

>>> 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(), щоб отримати його.

Please note that a much more powerful error checking mechanism is available through the errcheck attribute; see the reference manual for details.

Передача покажчиків (або: передача параметрів за посиланням)

Іноді функція 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'
>>>

Структури та спілки

Structures and unions must derive from the Structure and Union base classes which are defined in the ctypes module. Each subclass must define a _fields_ attribute. _fields_ must be a list of 2-tuples, containing a field name and a field type.

Тип поля має бути типу 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, яка містить дві ТОЧКИ з іменами 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))

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

>>> 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, бібліотека не гарантує роботу в загальному випадку. Об’єднання та структури з бітовими полями слід завжди передавати функціям за вказівником.

Вирівнювання структури/об’єднання та порядок байтів

By default, Structure and Union fields are aligned in the same way the C compiler does it. It is possible to override this behavior by specifying a _pack_ class attribute in the subclass definition. This must be set to a positive integer and specifies the maximum alignment for the fields. This is what #pragma pack(n) also does in MSVC.

ctypes використовує власний порядок байтів для структур і об’єднань. Щоб створити структури з невласним порядком байтів, ви можете використовувати один із базових класів BigEndianStructure, LittleEndianStructure, BigEndianUnion і LittleEndianUnion. Ці класи не можуть містити поля вказівників.

Бітові поля в структурах і об’єднаннях

It is possible to create structures and unions containing bit fields. Bit fields are only possible for integer fields, the bit width is specified as the third item in the _fields_ tuples:

>>> 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 не має OOR (повернення вихідного об’єкта), він створює новий, еквівалентний об’єкт кожного разу, коли ви отримуєте атрибут:

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

Призначення іншого екземпляра c_int атрибуту contents вказівника призведе до того, що вказівник вказуватиме на місце пам’яті, де це зберігається:

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

Екземпляри вказівників також можна індексувати цілими числами:

>>> pi[0]
99
>>>

Присвоєння цілочисельному індексу змінює вказане значення:

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

Також можна використовувати індекси, відмінні від 0, але ви повинні знати, що ви робите, так само як у C: ви можете отримати доступ або змінити довільні місця пам’яті. Зазвичай ви використовуєте цю функцію, лише якщо отримуєте вказівник від функції C і знаєте, що вказівник насправді вказує на масив, а не на один елемент.

За лаштунками функція pointer() робить більше, ніж просто створює екземпляри вказівників, вона повинна спочатку створити типи вказівників. Це робиться за допомогою функції POINTER(), яка приймає будь-який тип ctypes і повертає новий тип:

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

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

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

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

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

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

Перетворення типів

Usually, ctypes does strict type checking. This means, if you have POINTER(c_int) in the argtypes list of a function or as the type of a member field in a structure definition, only instances of exactly the same type are accepted. There are some exceptions to this rule, where ctypes accepts other objects. For example, you can pass compatible array instances instead of pointer types. So, for POINTER(c_int), ctypes accepts an array of 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
>>>

In addition, if a function argument is explicitly declared to be a pointer type (such as POINTER(c_int)) in argtypes, an object of the pointed type (c_int in this case) can be passed to the function. ctypes will apply the required byref() conversion in this case automatically.

Щоб встановити для поля типу 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
>>>

because the new class cell is not available in the class statement itself. In ctypes, we can define the cell class and set the _fields_ attribute later, after the class statement:

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

Обидві ці фабричні функції викликаються з типом результату як першим аргументом, а функції зворотного виклику – очікуваними типами аргументів як рештою аргументів.

I will present an example here which uses the standard C library’s qsort() function, that is used to sort items with the help of a callback function. qsort() will be used to sort an array of integers:

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

qsort() must be called with a pointer to the data to sort, the number of items in the data array, the size of one item, and a pointer to the comparison function, the callback. The callback will then be called with two pointers to items, and it must return a negative integer if the first item is smaller than the second, a zero if they are equal, and a positive integer otherwise.

Таким чином, наша функція зворотного виклику отримує покажчики на цілі числа та повинна повертати ціле число. Спочатку ми створюємо 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

Some shared libraries not only export functions, they also export variables. An example in the Python library itself is the Py_OptimizeFlag, an integer set to 0, 1, or 2, depending on the -O or -OO flag given on startup.

ctypes can access values like this with the in_dll() class methods of the type. pythonapi is a predefined symbol giving access to the Python C api:

>>> opt_flag = c_int.in_dll(pythonapi, "Py_OptimizeFlag")
>>> print(opt_flag)
c_long(0)
>>>

If the interpreter would have been started with -O, the sample would have printed c_long(1), or c_long(2) if -OO would have been specified.

Розширений приклад, який також демонструє використання покажчиків, звертається до покажчика PyImport_FrozenModules, експортованого Python.

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

This pointer is initialized to point to an array of struct _frozen records, terminated by one whose members are all NULL or zero. When a frozen module is imported, it is searched in this table. Third-party code could play tricks with this to provide a dynamically created collection of frozen modules.

Тож маніпулювання цим покажчиком може навіть виявитися корисним. Щоб обмежити розмір прикладу, ми показуємо лише те, як цю таблицю можна прочитати за допомогою ctypes:

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

We have defined the struct _frozen data type, so we can get the pointer to the table:

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

Оскільки table є покажчиком на масив struct_frozen записів, ми можемо перебирати його, але нам просто потрібно переконатися, що наш цикл завершується, оскільки покажчики не мають розміру. Рано чи пізно він, ймовірно, вийде з ладу через порушення прав доступу чи щось інше, тому краще вийти з циклу, коли ми натиснемо запис NULL:

>>> for item in table:
...     if item.name is None:
...         break
...     print(item.name.decode("ascii"), item.size)
...
_frozen_importlib 31764
_frozen_importlib_external 41499
__hello__ 161
__phello__ -161
__phello__.spam 161
>>>

Той факт, що стандартний Python має заморожений модуль і заморожений пакет (позначений від’ємним елементом size), невідомий, він використовується лише для тестування. Спробуйте, наприклад, import __hello__.

Сюрпризи

У ctypes є деякі переваги, де ви можете очікувати щось інше, ніж те, що відбувається насправді.

Розглянемо такий приклад:

>>> from ctypes import *
>>> class POINT(Structure):
...     _fields_ = ("x", c_int), ("y", c_int)
...
>>> class RECT(Structure):
...     _fields_ = ("a", POINT), ("b", POINT)
...
>>> p1 = POINT(1, 2)
>>> p2 = POINT(3, 4)
>>> rc = RECT(p1, p2)
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
1 2 3 4
>>> # 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 у блоці пам’яті не зберігає сам об’єкт, натомість зберігається вміст об’єкта. Повторний доступ до вмісту щоразу створює новий об’єкт 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

Пошук спільних бібліотек

Під час програмування скомпільованою мовою доступ до спільних бібліотек здійснюється під час компіляції/зв’язування програми та під час виконання програми.

The purpose of the find_library() function is to locate a library in a way similar to what the compiler or runtime loader does (on platforms with several versions of a shared library the most recent should be loaded), while the ctypes library loaders act like when a program is run, and call the runtime loader directly.

The ctypes.util module provides a function which can help to determine the library to load.

ctypes.util.find_library(name)

Спробуйте знайти бібліотеку та повернути шлях. name — це назва бібліотеки без будь-якого префікса, як-от lib, суфікса, як-от .so, .dylib або номера версії (це форма, яка використовується для параметра компонування posix - l). Якщо бібліотеки не знайдено, повертає None.

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

On Linux, find_library() tries to run external programs (/sbin/ldconfig, gcc, objdump and ld) to find the library file. It returns the filename of the library file.

Змінено в версії 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'
>>>

On macOS, find_library() tries several predefined naming schemes and paths to locate the library, and returns a full pathname if successful:

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

On Windows, find_library() searches along the system search path, and returns the full pathname, but since there is no predefined naming scheme a call like find_library("c") will fail and return None.

If wrapping a shared library with ctypes, it may be better to determine the shared library name at development time, and hardcode that into the wrapper module instead of using find_library() to locate the library at runtime.

Завантаження спільних бібліотек

Є кілька способів завантажити спільні бібліотеки в процес Python. Одним із способів є створення екземпляра одного з наступних класів:

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

Instances of this class represent loaded shared libraries. Functions in these libraries use the standard C calling convention, and are assumed to return int.

У Windows створення екземпляра CDLL може завершитися помилкою, навіть якщо ім’я DLL існує. Якщо залежну DLL від завантаженої DLL не знайдено, виникає помилка OSError із повідомленням «[WinError 126] Не вдалося знайти вказаний модуль». Це повідомлення про помилку не містить назви відсутня DLL, оскільки Windows API не повертає цю інформацію, що ускладнює діагностику цієї помилки. Щоб усунути цю помилку та визначити, яку DLL не знайдено, потрібно знайти список залежних DLL і визначити, яка з них не знайдена, за допомогою засобів налагодження та трасування Windows.

Дивись також

Інструмент Microsoft DUMPBIN – Інструмент для пошуку залежних DLL.

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

Windows only: Instances of this class represent loaded shared libraries, functions in these libraries use the stdcall calling convention, and are assumed to return the windows specific HRESULT code. HRESULT values contain information specifying whether the function call failed or succeeded, together with additional error code. If the return value signals a failure, an OSError is automatically raised.

Змінено в версії 3.3: WindowsError used to be raised.

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

Windows only: Instances of this class represent loaded shared libraries, functions in these libraries use the stdcall calling convention, and are assumed to return int by default.

global interpreter lock Python знімається перед викликом будь-якої функції, експортованої цими бібліотеками, і знову отримується після цього.

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

Екземпляри цього класу поводяться як екземпляри CDLL, за винятком того, що GIL Python не звільняється під час виклику функції, а після виконання функції перевіряється позначка помилки Python. Якщо встановлено позначку помилки, виникає виняток Python.

Таким чином, це корисно лише для прямого виклику API-функцій Python C.

All these classes can be instantiated by calling them with at least one argument, the pathname of the shared library. If you have an existing handle to an already loaded shared library, it can be passed as the handle named parameter, otherwise the underlying platforms dlopen or LoadLibrary function is used to load the library into the process, and to get a handle to it.

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

The use_errno parameter, when set to true, enables a ctypes mechanism that allows accessing the system errno error number in a safe way. ctypes maintains a thread-local copy of the systems errno variable; if you call foreign functions created with use_errno=True then the errno value before the function call is swapped with the ctypes private copy, the same happens immediately after the function call.

Функція ctypes.get_errno() повертає значення приватної копії ctypes, а функція ctypes.set_errno() змінює приватну копію ctypes на нове значення та повертає попереднє значення.

The use_last_error parameter, when set to true, enables the same mechanism for the Windows error code which is managed by the GetLastError() and SetLastError() Windows API functions; ctypes.get_last_error() and ctypes.set_last_error() are used to request and change the ctypes private copy of the windows error code.

The winmode parameter is used on Windows to specify how the library is loaded (since mode is ignored). It takes any value that is valid for the Win32 API LoadLibraryEx flags parameter. When omitted, the default is to use the flags that result in the most secure DLL load to avoiding issues such as DLL hijacking. Passing the full path to the DLL is the safest way to ensure the correct library and dependencies are loaded.

Змінено в версії 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

Ім’я бібліотеки, передане в конструктор.

Shared libraries can also be loaded by using one of the prefabricated objects, which are instances of the LibraryLoader class, either by calling the LoadLibrary() method, or by retrieving the library as attribute of the loader instance.

class ctypes.LibraryLoader(dlltype)

Клас, який завантажує спільні бібліотеки. dlltype має бути одним із типів CDLL, PyDLL, WinDLL або OleDLL.

__getattr__() has special behavior: It allows loading a shared library by accessing it as attribute of a library loader instance. The result is cached, so repeated attribute accesses return the same library each time.

LoadLibrary(name)

Завантажте спільну бібліотеку в процес і поверніть її. Цей метод завжди повертає новий екземпляр бібліотеки.

Ці збірні бібліотечні завантажувачі доступні:

ctypes.cdll

Створює екземпляри CDLL.

ctypes.windll

Windows only: Creates WinDLL instances.

ctypes.oledll

Windows only: Creates OleDLL instances.

ctypes.pydll

Створює екземпляри PyDLL.

Для прямого доступу до API C Python доступний готовий до використання об’єкт спільної бібліотеки Python:

ctypes.pythonapi

An instance of PyDLL that exposes Python C API functions as attributes. Note that all these functions are assumed to return C int, which is of course not always the truth, so you have to assign the correct restype attribute to use these functions.

Завантаження бібліотеки через будь-який із цих об’єктів викликає подію аудиту ctypes.dlopen з рядковим аргументом name, іменем, яке використовується для завантаження бібліотеки.

Доступ до функції у завантаженій бібліотеці викликає подію аудиту ctypes.dlsym з аргументами library (об’єкт бібліотеки) і name (ім’я символу у вигляді рядка або цілого числа).

У випадках, коли доступний лише дескриптор бібліотеки, а не об’єкт, доступ до функції викликає подію аудиту ctypes.dlsym/handle з аргументами handle (необроблений дескриптор бібліотеки) і name.

Іноземні функції

As explained in the previous section, foreign functions can be accessed as attributes of loaded shared libraries. The function objects created in this way by default accept any number of arguments, accept any ctypes data instances as arguments, and return the default result type specified by the library loader. They are instances of a private class:

class ctypes._FuncPtr

Базовий клас для зовнішніх функцій C, що викликаються.

Примірники сторонніх функцій також є C-сумісними типами даних; вони представляють покажчики функцій C.

Цю поведінку можна налаштувати шляхом призначення спеціальних атрибутів стороннього функціонального об’єкта.

restype

Assign a ctypes type to specify the result type of the foreign function. Use None for void, a function not returning anything.

It is possible to assign a callable Python object that is not a ctypes type, in this case the function is assumed to return a C int, and the callable will be called with this integer, allowing further processing or error checking. Using this is deprecated, for more flexible post processing or error checking use a ctypes data type as restype and assign a callable to the errcheck attribute.

argtypes

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

When a foreign function is called, each actual argument is passed to the from_param() class method of the items in the argtypes tuple, this method allows adapting the actual argument to an object that the foreign function accepts. For example, a c_char_p item in the argtypes tuple will convert a string passed as argument into a bytes object using ctypes conversion rules.

New: It is now possible to put items in argtypes which are not ctypes types, but each item must have a from_param() method which returns a value usable as argument (integer, string, ctypes instance). This allows defining adapters that can adapt custom objects as function parameters.

errcheck

Призначте цьому атрибуту функцію Python або інший виклик. Викликається з трьома або більше аргументами:

callable(result, func, arguments)

result is what the foreign function returns, as specified by the restype attribute.

func — це сам зовнішній об’єкт функції, це дозволяє повторно використовувати той самий об’єкт, що викликається, для перевірки або постобробки результатів кількох функцій.

Аргументи — це кортеж, що містить параметри, спочатку передані до виклику функції, що дозволяє спеціалізувати поведінку на використовуваних аргументах.

Об’єкт, який повертає ця функція, буде повернено з виклику зовнішньої функції, але він також може перевірити значення результату та викликати виняток, якщо виклик зовнішньої функції не вдався.

exception ctypes.ArgumentError

Цей виняток виникає, коли зовнішній виклик функції не може перетворити один із переданих аргументів.

On Windows, when a foreign function call raises a system exception (for example, due to an access violation), it will be captured and replaced with a suitable Python exception. Further, an auditing event ctypes.seh_exception with argument code will be raised, allowing an audit hook to replace the exception with its own.

Деякі способи виклику зовнішніх викликів функцій можуть викликати подію аудиту ctypes.call_function з аргументами вказівник функції і аргументи.

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

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

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

Повернений прототип функції створює функції, які використовують стандартну угоду про виклик C. Функція звільнить GIL під час виклику. Якщо use_errno має значення true, приватна копія ctypes системної змінної errno обмінюється справжнім значенням errno до і після виклику; use_last_error робить те саме для коду помилки Windows.

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

Windows only: The returned function prototype creates functions that use the stdcall calling convention. The function will release the GIL during the call. use_errno and use_last_error have the same meaning as above.

ctypes.PYFUNCTYPE(restype, *argtypes)

Повернений прототип функції створює функції, які використовують угоду про виклики Python. Функція не звільняє GIL під час виклику.

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

prototype(address)

Повертає зовнішню функцію за вказаною адресою, яка має бути цілим числом.

prototype(callable)

Створіть функцію виклику C (функцію зворотного виклику) з Python callable.

prototype(func_spec[, paramflags])

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

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

Повертає зовнішню функцію, яка викликає метод COM. vtbl_index — це індекс у таблиці віртуальних функцій, мале невід’ємне ціле число. ім’я — це ім’я методу COM. iid — це додатковий покажчик на ідентифікатор інтерфейсу, який використовується в розширеному звіті про помилки.

COM methods use a special calling convention: They require a pointer to the COM interface as first argument, in addition to those parameters that are specified in the argtypes tuple.

Необов’язковий параметр paramflags створює зовнішні обгортки функцій із набагато більшою функціональністю, ніж функції, описані вище.

paramflags must be a tuple of the same length as argtypes.

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

Перший елемент є цілим числом, що містить комбінацію прапорів напрямку для параметра:

1

Визначає вхідний параметр для функції.

2

Вихідний параметр. Стороння функція заповнює значення.

4

Вхідний параметр, який за умовчанням дорівнює нулю.

Другим необов’язковим елементом є назва параметра у вигляді рядка. Якщо це вказано, зовнішня функція може бути викликана з іменованими параметрами.

Додатковий третій елемент є значенням за замовчуванням для цього параметра.

This example demonstrates how to wrap the Windows MessageBoxW function so that it supports default parameters and named arguments. The C declaration from the windows header file is this:

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

Другий приклад демонструє вихідні параметри. Функція GetWindowRect win32 отримує розміри вказаного вікна шляхом копіювання їх у структуру RECT, яку має надати абонент. Ось оголошення C:

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

Ось обгортка за допомогою ctypes:

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

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

Output parameters can be combined with the errcheck protocol to do further output processing and error checking. The win32 GetWindowRect api function returns a BOOL to signal success or failure, so this function could do the error checking, and raises an exception when the api call failed:

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

If the errcheck function returns the argument tuple it receives unchanged, ctypes continues the normal processing it does on the output parameters. If you want to return a tuple of window coordinates instead of a RECT instance, you can retrieve the fields in the function and return them instead, the normal processing will no longer take place:

>>> 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. Ціле число може бути передане як другий аргумент, що дозволяє вказати розмір масиву, якщо довжина байтів не повинна використовуватися.

Викликає подію аудиту ctypes.create_string_buffer з аргументами init, size.

ctypes.create_unicode_buffer(init_or_size, size=None)

Ця функція створює змінний буфер символів Unicode. Повернений об’єкт є масивом ctypes c_wchar.

init_or_size має бути цілим числом, що вказує розмір масиву, або рядком, який використовуватиметься для ініціалізації елементів масиву.

Якщо рядок вказано як перший аргумент, буфер робиться на один елемент більшим, ніж довжина рядка, так що останній елемент у масиві є завершальним символом NUL. Ціле число може бути передане як другий аргумент, що дозволяє вказати розмір масиву, якщо довжина рядка не повинна використовуватися.

Викликає подію аудиту ctypes.create_unicode_buffer з аргументами init, size.

ctypes.DllCanUnloadNow()

Windows only: This function is a hook which allows implementing in-process COM servers with ctypes. It is called from the DllCanUnloadNow function that the _ctypes extension dll exports.

ctypes.DllGetClassObject()

Windows only: This function is a hook which allows implementing in-process COM servers with ctypes. It is called from the DllGetClassObject function that the _ctypes extension dll exports.

ctypes.util.find_library(name)

Спробуйте знайти бібліотеку та повернути шлях. ім’я — це ім’я бібліотеки без будь-яких префіксів, як-от lib, суфіксів, як-от .so, .dylib або номера версії (це форма, яка використовується для параметра компонувальника posix -l). Якщо бібліотеки не знайдено, повертає None.

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

ctypes.util.find_msvcrt()

Windows only: return the filename of the VC runtime library used by Python, and by the extension modules. If the name of the library cannot be determined, None is returned.

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

ctypes.FormatError([code])

Windows only: Returns a textual description of the error code code. If no error code is specified, the last error code is used by calling the Windows api function GetLastError.

ctypes.GetLastError()

Windows only: Returns the last error code set by Windows in the calling thread. This function calls the Windows GetLastError() function directly, it does not return the ctypes-private copy of the error code.

ctypes.get_errno()

Повертає поточне значення ctypes-private копії системної змінної errno у потоці виклику.

Викликає подію аудиту ctypes.get_errno без аргументів.

ctypes.get_last_error()

Windows only: returns the current value of the ctypes-private copy of the system LastError variable in the calling thread.

Викликає подію аудиту ctypes.get_last_error без аргументів.

ctypes.memmove(dst, src, count)

Те саме, що стандартна функція бібліотеки C memmove: копіює count байти з src до dst. dst і src мають бути цілими числами або екземплярами ctypes, які можна перетворити на покажчики.

ctypes.memset(dst, c, count)

Те саме, що стандартна функція бібліотеки memset C: заповнює блок пам’яті за адресою dst count байтами зі значенням c. dst має бути цілим числом, що визначає адресу, або екземпляр ctypes.

ctypes.POINTER(type)

This factory function creates and returns a new ctypes pointer type. Pointer types are cached and reused internally, so calling this function repeatedly is cheap. type must be a ctypes type.

ctypes.pointer(obj)

This function creates a new pointer instance, pointing to obj. The returned object is of the type POINTER(type(obj)).

Примітка. Якщо ви просто хочете передати вказівник на об’єкт у зовнішній виклик функції, вам слід використовувати byref(obj), що набагато швидше.

ctypes.resize(obj, size)

Ця функція змінює розмір буфера внутрішньої пам’яті obj, який має бути екземпляром типу ctypes. Неможливо зробити буфер меншим, ніж власний розмір типу об’єктів, як задано sizeof(type(obj)), але можна збільшити буфер.

ctypes.set_errno(value)

Установіть поточне значення ctypes-private копії системної змінної errno у викликаючому потоці на value і поверніть попереднє значення.

Викликає подію аудиту ctypes.set_errno з аргументом errno.

ctypes.set_last_error(value)

Windows only: set the current value of the ctypes-private copy of the system LastError variable in the calling thread to value and return the previous value.

Викликає подію аудиту ctypes.set_last_error з аргументом помилка.

ctypes.sizeof(obj_or_type)

Повертає розмір у байтах типу ctypes або буфера пам’яті примірника. Діє так само, як і оператор C sizeof.

ctypes.string_at(address, size=-1)

This function returns the C string starting at memory address address as a bytes object. If size is specified, it is used as size, otherwise the string is assumed to be zero-terminated.

Raises an auditing event ctypes.string_at with arguments address, size.

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

Windows only: this function is probably the worst-named thing in ctypes. It creates an instance of OSError. If code is not specified, GetLastError is called to determine the error code. If descr is not specified, FormatError() is called to get a textual description of the error.

Змінено в версії 3.3: An instance of WindowsError used to be created.

ctypes.wstring_at(address, size=-1)

This function returns the wide character string starting at memory address address as a string. If size is specified, it is used as the number of characters of the string, otherwise the string is assumed to be zero-terminated.

Raises an auditing event ctypes.wstring_at with arguments address, size.

Типи даних

class ctypes._CData

Цей непублічний клас є загальним базовим класом для всіх типів даних ctypes. Серед іншого, усі екземпляри типу ctypes містять блок пам’яті, який містить C-сумісні дані; адреса блоку пам’яті повертається допоміжною функцією addressof(). Інша змінна екземпляра представлена як _objects; це містить інші об’єкти Python, які потрібно підтримувати в активному стані, якщо блок пам’яті містить покажчики.

Загальні методи типів даних ctypes, це всі методи класу (точніше, це методи metaclass):

from_buffer(source[, offset])

Цей метод повертає екземпляр ctypes, який спільно використовує буфер об’єкта source. Об’єкт джерело має підтримувати інтерфейс буфера з можливістю запису. Необов’язковий параметр offset визначає зсув у вихідному буфері в байтах; за замовчуванням дорівнює нулю. Якщо вихідний буфер недостатньо великий, виникає ValueError.

Викликає подію аудиту ctypes.cdata/buffer з аргументами pointer, size, offset.

from_buffer_copy(source[, offset])

Цей метод створює екземпляр ctypes, копіюючи буфер із буфера джерельного об’єкта, який має бути читабельним. Необов’язковий параметр offset визначає зсув у вихідному буфері в байтах; за замовчуванням дорівнює нулю. Якщо вихідний буфер недостатньо великий, виникає ValueError.

Викликає подію аудиту ctypes.cdata/buffer з аргументами pointer, size, offset.

from_address(address)

Цей метод повертає екземпляр типу ctypes, використовуючи пам’ять, визначену адресою, яка має бути цілим числом.

Цей метод та інші, які опосередковано викликають цей метод, викликають подію аудиту ctypes.cdata з аргументом адреса.

from_param(obj)

This method adapts obj to a ctypes type. It is called with the actual object used in a foreign function call when the type is present in the foreign function’s argtypes tuple; it must return an object that can be used as a function call parameter.

Усі типи даних ctypes мають стандартну реалізацію цього методу класу, який зазвичай повертає obj, якщо це екземпляр типу. Деякі типи також приймають інші об’єкти.

in_dll(library, name)

Цей метод повертає екземпляр типу ctypes, експортований спільною бібліотекою. name — це ім’я символу, який експортує дані, library — це завантажена спільна бібліотека.

Загальні змінні екземплярів типів даних ctypes:

_b_base_

Іноді екземпляри даних ctypes не володіють блоком пам’яті, який вони містять, натомість вони спільно використовують частину блоку пам’яті базового об’єкта. Член _b_base_ лише для читання є кореневим об’єктом ctypes, якому належить блок пам’яті.

_b_needsfree_

Ця змінна лише для читання є істиною, коли екземпляр даних ctypes сам виділив блок пам’яті, інакше – false.

_objects

Цей член або None, або словник, що містить об’єкти Python, які потрібно підтримувати в активному стані, щоб вміст блоку пам’яті залишався дійсним. Цей об’єкт доступний лише для налагодження; ніколи не змінюйте вміст цього словника.

Основні типи даних

class ctypes._SimpleCData

Цей закритий клас є базовим класом усіх основних типів даних ctypes. Він згадується тут, оскільки він містить загальні атрибути основних типів даних ctypes. _SimpleCData є підкласом _CData, тому він успадковує їхні методи та атрибути. Типи даних ctypes, які не є та не містять покажчиків, тепер можна маринувати.

Екземпляри мають один атрибут:

value

Цей атрибут містить фактичне значення екземпляра. Для цілочисельних типів і типів покажчиків це ціле число, для типів символів – об’єкт або рядок із одним символом у байтах, для типів покажчиків на символи – це об’єкт або рядок Python bytes.

Коли атрибут value отримується з екземпляра ctypes, зазвичай щоразу повертається новий об’єкт. ctypes не реалізує повернення оригінального об’єкта, завжди створюється новий об’єкт. Те саме стосується всіх інших екземплярів об’єктів ctypes.

Fundamental data types, when returned as foreign function call results, or, for example, by retrieving structure field members or array items, are transparently converted to native Python types. In other words, if a foreign function has a restype of c_char_p, you will always receive a Python bytes object, not a c_char_p instance.

Subclasses of fundamental data types do not inherit this behavior. So, if a foreign functions restype is a subclass of c_void_p, you will receive an instance of this subclass from the function call. Of course, you can get the value of the pointer by accessing the value attribute.

Ось основні типи даних ctypes:

class ctypes.c_byte

Represents the C signed char datatype, and interprets the value as small integer. The constructor accepts an optional integer initializer; no overflow checking is done.

class ctypes.c_char

Represents the C char datatype, and interprets the value as a single character. The constructor accepts an optional string initializer, the length of the string must be exactly one character.

class ctypes.c_char_p

Represents the C char * datatype when it points to a zero-terminated string. For a general character pointer that may also point to binary data, POINTER(c_char) must be used. The constructor accepts an integer address, or a bytes object.

class ctypes.c_double

Represents the C double datatype. The constructor accepts an optional float initializer.

class ctypes.c_longdouble

Represents the C long double datatype. The constructor accepts an optional float initializer. On platforms where sizeof(long double) == sizeof(double) it is an alias to c_double.

class ctypes.c_float

Represents the C float datatype. The constructor accepts an optional float initializer.

class ctypes.c_int

Represents the C signed int datatype. The constructor accepts an optional integer initializer; no overflow checking is done. On platforms where sizeof(int) == sizeof(long) it is an alias to c_long.

class ctypes.c_int8

Represents the C 8-bit signed int datatype. Usually an alias for c_byte.

class ctypes.c_int16

Represents the C 16-bit signed int datatype. Usually an alias for c_short.

class ctypes.c_int32

Represents the C 32-bit signed int datatype. Usually an alias for c_int.

class ctypes.c_int64

Represents the C 64-bit signed int datatype. Usually an alias for c_longlong.

class ctypes.c_long

Represents the C signed long datatype. The constructor accepts an optional integer initializer; no overflow checking is done.

class ctypes.c_longlong

Represents the C signed long long datatype. The constructor accepts an optional integer initializer; no overflow checking is done.

class ctypes.c_short

Represents the C signed short datatype. The constructor accepts an optional integer initializer; no overflow checking is done.

class ctypes.c_size_t

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

class ctypes.c_ssize_t

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

Нове в версії 3.2.

class ctypes.c_ubyte

Represents the C unsigned char datatype, it interprets the value as small integer. The constructor accepts an optional integer initializer; no overflow checking is done.

class ctypes.c_uint

Represents the C unsigned int datatype. The constructor accepts an optional integer initializer; no overflow checking is done. On platforms where sizeof(int) == sizeof(long) it is an alias for c_ulong.

class ctypes.c_uint8

Represents the C 8-bit unsigned int datatype. Usually an alias for c_ubyte.

class ctypes.c_uint16

Represents the C 16-bit unsigned int datatype. Usually an alias for c_ushort.

class ctypes.c_uint32

Represents the C 32-bit unsigned int datatype. Usually an alias for c_uint.

class ctypes.c_uint64

Represents the C 64-bit unsigned int datatype. Usually an alias for c_ulonglong.

class ctypes.c_ulong

Represents the C unsigned long datatype. The constructor accepts an optional integer initializer; no overflow checking is done.

class ctypes.c_ulonglong

Represents the C unsigned long long datatype. The constructor accepts an optional integer initializer; no overflow checking is done.

class ctypes.c_ushort

Represents the C unsigned short datatype. The constructor accepts an optional integer initializer; no overflow checking is done.

class ctypes.c_void_p

Represents the C void * type. The value is represented as integer. The constructor accepts an optional integer initializer.

class ctypes.c_wchar

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

class ctypes.c_wchar_p

Represents the C wchar_t * datatype, which must be a pointer to a zero-terminated wide character string. The constructor accepts an integer address, or a string.

class ctypes.c_bool

Represent the C bool datatype (more accurately, _Bool from C99). Its value can be True or False, and the constructor accepts any object that has a truth value.

class ctypes.HRESULT

Windows only: Represents a HRESULT value, which contains success or error information for a function or method call.

class ctypes.py_object

Represents the C PyObject * datatype. Calling this without an argument creates a NULL PyObject * pointer.

The ctypes.wintypes module provides quite some other Windows specific data types, for example HWND, WPARAM, or DWORD. Some useful structures like MSG or RECT are also defined.

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

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

Абстрактний базовий клас для об’єднань у рідному порядку байтів.

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

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

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

Абстрактний базовий клас для структур у порядку байтів little endian.

Structures with non-native byte order cannot contain pointer type fields, or any other data types containing pointer type fields.

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

Абстрактний базовий клас для структур у власному порядку байтів.

Конкретні типи структури та об’єднання мають бути створені шляхом створення підкласу одного з цих типів і принаймні визначення змінної класу _fields_. ctypes створить descriptors, які дозволяють читати та записувати поля за допомогою прямого доступу до атрибутів. Це

_fields_

Послідовність, що визначає поля структури. Елементи мають бути 2-кортежними або 3-кортежними. Перший пункт – ім’я поля, другий – тип поля; це може бути будь-який тип даних ctypes.

Для полів цілого типу, таких як c_int, можна вказати третій необов’язковий елемент. Це має бути маленьке позитивне ціле число, що визначає розрядність поля.

Імена полів мають бути унікальними в межах однієї структури чи об’єднання. Це не позначено, лише одне поле доступне, якщо імена повторюються.

Можна визначити змінну класу _fields_ після оператора класу, який визначає підклас Structure, це дозволяє створювати типи даних, які прямо чи опосередковано посилаються на себе:

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

Однак змінну класу _fields_ потрібно визначити перед першим використанням типу (створюється екземпляр, для нього викликається sizeof() і так далі). Пізніше призначення змінній класу _fields_ призведе до помилки AttributeError.

Можна визначити під-підкласи структурних типів, вони успадковують поля базового класу плюс _fields_, визначені в під-підкласі, якщо такі є.

_pack_

An optional small integer that allows overriding the alignment of structure fields in the instance. _pack_ must already be defined when _fields_ is assigned, otherwise it will have no effect.

_anonymous_

Необов’язкова послідовність, яка містить імена безіменних (анонімних) полів. _anonymous_ має бути вже визначено під час призначення _fields_, інакше це не матиме ефекту.

Поля, перелічені в цій змінній, мають бути структурними або об’єднаними. ctypes створить дескриптори у структурному типі, що дозволяє отримати доступ до вкладених полів безпосередньо, без необхідності створювати структуру чи поле об’єднання.

Ось приклад типу (Windows):

class _U(Union):
    _fields_ = [("lptdesc", POINTER(TYPEDESC)),
                ("lpadesc", POINTER(ARRAYDESC)),
                ("hreftype", HREFTYPE)]

class TYPEDESC(Structure):
    _anonymous_ = ("u",)
    _fields_ = [("u", _U),
                ("vt", VARTYPE)]

Структура TYPEDESC описує тип даних COM, поле vt визначає, яке з полів об’єднання є дійсним. Оскільки поле u визначено як анонімне поле, тепер можна отримати доступ до членів безпосередньо з примірника TYPEDESC. td.lptdesc і td.u.lptdesc еквівалентні, але перший швидший, оскільки йому не потрібно створювати тимчасовий екземпляр об’єднання:

td = TYPEDESC()
td.vt = VT_PTR
td.lptdesc = POINTER(some_type)
td.u.lptdesc = POINTER(some_type)

Є можливість визначення суб-підкласів структур, вони успадковують поля базового класу. Якщо визначення підкласу має окрему змінну _fields_, поля, зазначені в ній, додаються до полів базового класу.

Конструктори структури та об’єднання приймають як позиційні, так і ключові аргументи. Позиційні аргументи використовуються для ініціалізації полів-членів у такому самому порядку, як вони з’являються в _fields_. Аргументи ключових слів у конструкторі інтерпретуються як призначення атрибутів, тому вони ініціалізують _fields_ з тим же іменем або створять нові атрибути для імен, яких немає в _fields_.

Масиви та покажчики

class ctypes.Array(*args)

Абстрактний базовий клас для масивів.

Рекомендований спосіб створення конкретних типів масивів — це множення будь-якого типу даних ctypes на невід’ємне ціле число. Крім того, ви можете створити підклас цього типу та визначити змінні класу _length_ і _type_. Елементи масиву можна читати та записувати за допомогою стандартного підрядкового і зрізного доступу; для зчитування фрагментів результуючий об’єкт не сам є Array.

_length_

Додатне ціле число, що визначає кількість елементів у масиві. Індекси поза діапазоном призводять до IndexError. Буде повернено len().

_type_

Визначає тип кожного елемента в масиві.

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

class ctypes._Pointer

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

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

Якщо вказівник вказує на масив, його елементи можна читати та записувати за допомогою стандартного підрядкового і зрізного доступу. Об’єкти-вказівники не мають розміру, тому len() викличе TypeError. Негативні індекси читатимуться з пам’яті перед вказівником (як у C), а індекси поза межами діапазону, ймовірно, впадуть із порушенням доступу (якщо вам пощастить).

_type_

Визначає тип, на який вказується.

contents

Повертає об’єкт, на який вказує вказівник. Призначення цьому атрибуту змінює вказівник на призначений об’єкт.