ctypes
;Внешние C-функции могут быть созданы путем создания экземпляров прототипов функций. Прототипы функций похожи на прототипы функций в языке C. Они описывают функцию (тип возвращаемого значения, типы аргументов, соглашение о вызовах) без определения реализации.
Фабричные функции должны вызываться с желаемым типом результата и типами аргументов функции и могут использоваться как фабричные декораторы и как таковые, применяться к функциям с помощью синтаксиса @wrapper
. Смотрите примеры функций обратного вызова.
ctypes
.ctypes.CFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)
:Фабричная функция ctypes.CFUNCTYPE()
возвращает прототип prototype
созданной C-функции, который использует стандартное соглашение о вызове языка Си. Функция отключает GIL во время вызова.
Если аргумент use_errno=True
, то частная копия ctypes
системной переменной errno
обменивается реальным значением errno
до и после вызова.
Аргумент use_last_error
делает то же самое для кода ошибки Windows.
ctypes.WINFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)
:Фабричная функция ctypes.WINFUNCTYPE()
используется только в Windows: возвращает прототип prototype
созданной C-функции, который использует соглашение о вызовах stdcall, за исключением Windows CE, где WINFUNCTYPE()
совпадает с ctypes.CFUNCTYPE()
.
Функция ctypes.WINFUNCTYPE()
освободит GIL во время вызова. Аргументы use_errno
и use_last_error
имеют то же значение, что класс ctypes.CFUNCTYPE()
.
ctypes.PYFUNCTYPE(restype, *argtypes)
:Фабричная функция ctypes.PYFUNCTYPE()
возвращает прототип prototype
созданной C-функции, который использует стандартное соглашение о вызовах Python. Функция ctypes.PYFUNCTYPE()
не освобождает GIL во время вызова.
Аргументы restype
и *argtypes
, представленных выше фабричных функций - это атрибуты базового класса ctypes._FuncPtr
, который представляет все вызываемые C-функции, загруженные из библиотек языка C.
Прототипы функций, созданные фабричными функциями выше, могут быть созданы различными способами, в зависимости от типа и количества параметров в вызове:
prototype(address)
:Возвращает внешнюю функцию по указанному адресу address
, который должен быть целым числом.
prototype(callable)
:Создает вызываемую C-функцию (функцию обратного вызова) из вызываемого объекта Python.
prototype(func_spec[, paramflags])
:Возвращает внешнюю C-функцию, экспортированную из общей библиотеки. Аргумент func_spec
должен быть кортежем из 2-х элементов (name_or_ordinal, library)
.
name_or_ordinal
- это имя экспортируемой функции в виде строки или порядковый номер экспортируемой функции в виде небольшого целого числа. library
- это экземпляр загружаемой библиотеки C.prototype(vtbl_index, name[, paramflags[, iid]])
:Возвращает внешнюю C-функцию, которая вызовет метод COM.
vtbl_index
- это индекс в таблице виртуальных функций, небольшое неотрицательное целое число. name
- это имя COM-метода. iid
- это необязательный указатель на идентификатор интерфейса, который используется в расширенных сообщениях об ошибках.В COM методах используется специальное соглашение о вызовах: им требуется указатель на интерфейс COM в качестве первого аргумента в дополнение к тем параметрам, которые указаны в кортеже argtypes
.
Необязательный аргумент paramflags
создает оболочки внешних функций с гораздо большей функциональностью, чем функции, описанные выше.
Аргумент paramflags
должен быть кортежем той же длины, что и argtypes
.
Каждый элемент в этом кортеже содержит дополнительную информацию о параметре, это должен быть кортеж, содержащий один, два или три элемента.
В этом примере показано, как обернуть функцию Windows MessageBoxW()
, чтобы она поддерживала параметры по умолчанию и именованные аргументы. Объявление C из файла заголовка Windows выглядит следующим образом:
WINUSERAPI int WINAPI MessageBoxW( HWND hWnd, LPCWSTR lpText, LPCWSTR lpCaption, UINT uType);
Оборачиваем функцию Windows MessageBoxW()
:
>>> from ctypes import c_int, WINFUNCTYPE, windll >>> from ctypes.wintypes import HWND, LPCWSTR, UINT >>> prototype = WINFUNCTYPE(c_int, HWND, LPCWSTR, LPCWSTR, UINT) >>> paramflags = ( (1, "hwnd", 0), (1, "text", "Hi"), (1, "caption", "Hello from ctypes"), (1, "flags", 0) ) >>> MessageBox = prototype(("MessageBoxW", windll.user32), paramflags)
Внешнюю функцию MessageBox
теперь можно вызывать следующими способами:
>>> MessageBox() >>> MessageBox(text="Spam, spam, spam") >>> MessageBox(flags=2, text="foo bar")
Второй пример демонстрирует выходные параметры. Функция Win32 GetWindowRect()
извлекает размеры указанного окна, копируя их в структуру RECT
, которую должен предоставить вызывающий. Вот объявление C:
WINUSERAPI BOOL WINAPI GetWindowRect( HWND hWnd, LPRECT lpRect);
Оборачиваем функцию Win32 GetWindowRect()
:
>>> from ctypes import POINTER, WINFUNCTYPE, windll, WinError >>> from ctypes.wintypes import BOOL, HWND, RECT >>> prototype = WINFUNCTYPE(BOOL, HWND, POINTER(RECT)) >>> paramflags = (1, "hwnd"), (2, "lprect") >>> GetWindowRect = prototype(("GetWindowRect", windll.user32), paramflags)
Функции с выходными параметрами автоматически возвращают значение выходного параметра (если есть один) или кортеж со значениями выходных параметров (если их несколько), поэтому при вызове функция GetWindowRect()
теперь возвращает экземпляр RECT
.
Параметры вывода могут быть объединены с протоколом errcheck
для дальнейшей обработки и проверки ошибок. Функция Win32 api GetWindowRect
возвращает BOOL
, чтобы сигнализировать об успехе или неудаче, поэтому эта функция может выполнять проверку ошибок и вызывает исключение, когда вызов api
не удался:
>>> def errcheck(result, func, args): ... if not result: ... raise WinError() ... return args ... >>> GetWindowRect.errcheck = errcheck
Если функция errcheck
возвращает кортеж аргументов, который она получает без изменений, то ctypes
продолжает обычную обработку выходных параметров. Если необходимо вернуть кортеж координат окна вместо экземпляра RECT
, то можно получить поля в функции и вернуть их результат, в этом случае нормальная обработка больше выполняться не будет:
>>> def errcheck(result, func, args): ... if not result: ... raise WinError() ... rc = args[1] ... return rc.left, rc.top, rc.bottom, rc.right ... >>> GetWindowRect.errcheck = errcheck