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

Доступ к значениям переменных DLL библиотек из модуля ctypes

Некоторые shared C-библиотеки не только экспортируют функции, но и экспортируют переменные. Примером такой переменной в библиотеке Python является Py_OptimizeFlag - это целое число, равное 0, 1 или 2, в зависимости от флага -O или -OO, установленного при запуске.

Модуль ctypes может получить доступ к таким значениям с помощью методов класса типа .in_dll(), например ctypes.c_int.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) или если бы был указан флаг -OO, то c_long (2).

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

Цитата из документации к указателю PyImport_FrozenModules:

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

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

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

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

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

(1) Тот факт, что стандартный Python имеет модуль frozen и пакет frozen (обозначается отрицательным элементом size), малоизвестен, он используется только для тестирования. Попробуйте, например, import __hello__.

>>> import __hello__
# Hello world!