Документация по Python

Модуль ctypes

В: Документация по Python

Введение

Примеры

Основное использование

Допустим, мы хотим использовать libc «s ntohl функцию.

Во- первых, мы должны загрузить libc.so :

 >>> from ctypes import *
>>> libc = cdll.LoadLibrary('libc.so.6')
>>> libc
<CDLL 'libc.so.6', handle baadf00d at 0xdeadbeef>

 

Затем мы получаем объект функции:

 >>> ntohl = libc.ntohl
>>> ntohl
<_FuncPtr object at 0xbaadf00d>

 

И теперь мы можем просто вызвать функцию:

 >>> ntohl(0x6C)
1811939328
>>> hex(_)
'0x6c000000'

 

Что делает именно то, что мы ожидаем.

Общие подводные камни

Не удалось загрузить файл

Первая возможная ошибка - не удается загрузить библиотеку. В этом случае обычно возникает OSError.

Это либо потому, что файл не существует (или не может быть найден в ОС):

 >>> cdll.LoadLibrary("foobar.so")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.5/ctypes/__init__.py", line 425, in LoadLibrary
    return self._dlltype(name)
File "/usr/lib/python3.5/ctypes/__init__.py", line 347, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: foobar.so: cannot open shared object file: No such file or directory

 

Как видите, ошибка ясна и довольно показательна.

Вторая причина в том, что файл найден, но имеет неправильный формат.

 >>> cdll.LoadLibrary("libc.so")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.5/ctypes/__init__.py", line 425, in LoadLibrary
    return self._dlltype(name)
File "/usr/lib/python3.5/ctypes/__init__.py", line 347, in __init__
    self._handle = _dlopen(self._name, mode)
OSError: /usr/lib/i386-linux-gnu/libc.so: invalid ELF header

 

В этом случае файл представляет собой файл сценария , а не .so файл. Это может также произойти при попытке открыть .dll - файл на компьютере Linux или 64 - битный файл на переводчике 32bit питона. Как вы можете видеть, в этом случае ошибка немного более расплывчата и требует некоторого изучения.

Неспособность получить доступ к функции

Предположим , что мы успешно загрузили .so файл, затем мы должны получить доступ к нашей функции , как мы сделали на первом примере.

Когда несуществующая функция используются, AttributeError поднимаются:

 >>> libc.foo
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "/usr/lib/python3.5/ctypes/__init__.py", line 360, in __getattr__
    func = self.__getitem__(name)
File "/usr/lib/python3.5/ctypes/__init__.py", line 365, in __getitem__
    func = self._FuncPtr((name_or_ordinal, self))
AttributeError: /lib/i386-linux-gnu/libc.so.6: undefined symbol: foo 

Базовый объект ctypes

Самый основной объект - это int:

 >>> obj = ctypes.c_int(12)
>>> obj
c_long(12)

 

Теперь, obj относится к кусок памяти , содержащей значение 12.

К этому значению можно получить прямой доступ и даже изменить его:

 >>> obj.value
12
>>> obj.value = 13
>>> obj
c_long(13)

 

Поскольку obj относится к кусок памяти, мы также можем узнать это размер и расположение:

 >>> sizeof(obj)
4
>>> hex(addressof(obj))
'0xdeadbeef' 

массивы типов

Как знает любой хороший программист на Си, одно значение не поможет вам так далеко. Что действительно заставит нас работать, так это массивы!

 >>> c_int * 16
<class '__main__.c_long_Array_16'>

 

Это не фактический массив, но это чертовски близко! Мы создали класс , который обозначает массив 16 int с.

Теперь все, что нам нужно сделать, это инициализировать его:

 >>> arr = (c_int * 16)(*range(16))
>>> arr
<__main__.c_long_Array_16 object at 0xbaddcafe>

 

Теперь arr является актуальной массив, содержащий числа от 0 до 15.

К ним можно получить доступ, как и к любому списку:

 >>> arr[5]
5
>>> arr[5] = 20
>>> arr[5]
20

 

И точно так же , как любые другие ctypes объекта, он также имеет размер и расположение:

 >>> sizeof(arr)
64 # sizeof(c_int) * 16
>>> hex(addressof(arr))
'0xc000l0ff' 

Функции обтекания для ctypes

В некоторых случаях функция C принимает указатель на функцию. Как алчный ctypes пользователи, мы хотели бы использовать эти функции, и даже передать функции питона в качестве аргументов.

Давайте определим функцию:

 >>> def max(x, y):
        return x if x >= y else y

 

Теперь эта функция принимает два аргумента и возвращает результат того же типа. Для примера давайте предположим, что тип - это int.

Как и в примере с массивом, мы можем определить объект, обозначающий этот прототип:

 >>> CFUNCTYPE(c_int, c_int, c_int)
<CFunctionType object at 0xdeadbeef>

 

Это прототип обозначает функцию , которая возвращает c_int (первый аргумент), и принимает два c_int аргумента (другие аргументы).

Теперь давайте обернем функцию:

 >>> CFUNCTYPE(c_int, c_int, c_int)(max)
<CFunctionType object at 0xdeadbeef>


 

Функциональные прототипы имеют на более частом использовании: они могут обернуть ctypes функции (например , libc.ntohl ) и убедитесь , что используется правильные аргументы при вызове функции.

 >>> libc.ntohl() # garbage in - garbage out
>>> CFUNCTYPE(c_int, c_int)(libc.ntohl)()
Traceback (most recent call last):
    File "<stdin>", line 1, in <module>
TypeError: this function takes at least 1 argument (0 given) 

Комплексное использование

Давайте объединить все из примеров выше , в один сложный сценарий: с помощью libc «ы lfind функции.

Для получения более подробной информации о функции, прочитать страницу человека . Я призываю вас прочитать это, прежде чем продолжить.

Сначала мы определим правильные прототипы:

 >>> compar_proto = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
>>> lfind_proto = CFUNCTYPE(c_void_p, c_void_p, c_void_p, POINTER(c_uint), c_uint, compar_proto)

 

Затем давайте создадим переменные:

 >>> key = c_int(12)
>>> arr = (c_int * 16)(*range(16))
>>> nmemb = c_uint(16)

 

И теперь мы определяем функцию сравнения:

 >>> def compar(x, y):
        return x.contents.value - y.contents.value

 

Обратите внимание на то, что x и y являются POINTER(c_int) , так что мы должны разыменовать их и принимать их значения для того , чтобы реально сравнить значение , хранящееся в памяти.

Теперь мы можем объединить все вместе:

 >>> lfind = lfind_proto(libc.lfind)
>>> ptr = lfind(byref(key), byref(arr), byref(nmemb), sizeof(c_int), compar_proto(compar))

 

ptr является возвращаемым недействительным указателем. Если key не был найден в arr , значение не будет None , но в данном случае мы получили действительное значение.

Теперь мы можем преобразовать его и получить доступ к значению:

 >>> cast(ptr, POINTER(c_int)).contents
c_long(12)

 

Кроме того , мы можем видеть , что ptr указывает на правильное значение внутри arr :

 >>> addressof(arr) + 12 * sizeof(c_int) == ptr
True 

Синтаксис

Параметры

Примечания

Еще от кодкамп
Замечательно! Вы успешно подписались.
Добро пожаловать обратно! Вы успешно вошли
Вы успешно подписались на кодкамп.
Срок действия вашей ссылки истек.
Ура! Проверьте свою электронную почту на наличие волшебной ссылки для входа.
Успех! Ваша платежная информация обновлена.
Ваша платежная информация не была обновлена.