Сообщить об ошибке.

Классы для загрузки C-библиотек модулем ctypes

Существует несколько способов загрузки общих библиотек в процесс Python. Один из способов-создать экземпляр одного из следующих классов:

Содержание:


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

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

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

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

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

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

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

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

В Windows CE используется только стандартное соглашение о вызовах, для удобства WinDLL и OleDLL используют стандартное соглашение о вызовах на этой платформе.

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

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

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

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

Описание аргументов классов-загрузчиков.

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

Аргумент mode можно использовать, для указания режима загрузки библиотеки. За подробностями обращайтесь к странице руководства dlopen(3), можно посмотреть консольной командой $ man dlopen. В Windows режим mode игнорируется. В системах posix всегда добавляется RTLD_NOW, и его нельзя настроить.

Аргумент use_errno, если он установлен в значение True, то он включает механизм ctypes, который позволяет безопасно получить доступ к системному номеру ошибки errno. Модуль ctypes поддерживает локальную для потока копию системной переменной errno; Если вызывать внешние функции, созданные с помощью use_errno=True, то тогда значение errno перед вызовом функции заменяется частной копией ctypes, то же самое происходит сразу после вызова функции.

Функция ctypes.get_errno() возвращает значение частной копии ctypes, а функция ctypes.set_errno() изменяет частную копию ctypes на новое значение и возвращает прежнее значение.

Аргумент use_last_error, если ему задано значение True, то он включает тот же механизм, только для кода ошибки Windows, которым управляют функции Windows API GetLastError() и SetLastError(). Функции модуля ctypes.get_last_error() и ctypes.set_last_error() используются для запроса и изменения частной копии ctypes кода ошибки Windows.

Изменено в Python 3.8: добавлен параметр winmode.

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

>>> from ctypes import CDLL
# На Linux
>>> libc = CDLL("libc.so.6")  
>>> libc.time == libc.time
# True
>>> libc['time'] == libc['time']
# False

Альтернативный способ загрузки DLL библиотек.

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

ctypes.LibraryLoader(dlltype):

Класс загружающий библиотеки языка C. Аргумент dlltype должен быть одним из типов ctypes.CDLL, ctypes.PyDLL, ctypes.WinDLL или ctypes.OleDLL.

Магический метод __getattr__() в этом классе имеет особое поведение: он позволяет загружать библиотеку языка C, обращаясь к ней как к атрибуту экземпляра загрузчика библиотеки. Результат кэшируется, поэтому повторные обращения к атрибутам каждый раз возвращают одну и ту же библиотеку.

  • Метод LoadLibrary(name) загружает библиотеку языка C в процесс Python и возвращает ее объект. Этот метод всегда возвращает новый экземпляр библиотеки.

Модуль ctypes имеет сборные библиотечные погрузчики:

ctypes.cdll:

  • создает экземпляр класса ctypes.CDLL.

ctypes.windll:

  • создает экземпляр класса ctypes.WinDLL. Используется только в Windows!

ctypes.oledll:

  • создает экземпляр класса ctypes.OleDLL. Используется только в Windows!

ctypes.pydll:

  • создает экземпляр класса ctypes.PyDLL.

Пример загрузки общих DLL библиотек в Windows.

Для загрузки общих библиотек в Windows, модуль ctypes автоматически экспортирует windll и oledll.

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

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

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

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

Пример загрузки общих C-библиотек в Linux.

Для загрузки общих библиотек в Linux, модуль ctypes автоматически экспортирует cdll.

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

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