Suche…


Einführung

ctypes ist eine in Python integrierte Bibliothek, die exportierte Funktionen aus nativen kompilierten Bibliotheken aufruft.

Hinweis: Da diese Bibliothek kompilierten Code verarbeitet, ist sie relativ vom Betriebssystem abhängig.

Grundlegende Verwendung

Nehmen wir an, wir wollen die ntohl Funktion von libc ntohl .

Zuerst müssen wir libc.so laden:

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

Dann erhalten wir das Funktionsobjekt:

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

Und jetzt können wir einfach die Funktion aufrufen:

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

Was genau das tut, was wir erwarten.

Häufige Fehler

Laden einer Datei fehlgeschlagen

Der erste mögliche Fehler ist das Laden der Bibliothek. In diesem Fall wird normalerweise ein OSError ausgelöst.

Dies liegt entweder daran, dass die Datei nicht existiert (oder vom Betriebssystem nicht gefunden wird):

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

Wie Sie sehen können, ist der Fehler klar und bezeichnend.

Der zweite Grund ist, dass die Datei gefunden wurde, aber nicht das richtige Format hat.

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

In diesem Fall ist die Datei eine Skriptdatei und keine .so Datei. Dies kann auch passieren, wenn Sie versuchen, eine .dll Datei auf einem Linux-Computer oder eine 64-Bit-Datei auf einem 32-Bit-Python-Interpreter zu öffnen. Wie Sie sehen, ist der Fehler in diesem Fall etwas ungenauer und erfordert einige Eingrabungen.

Fehler beim Zugriff auf eine Funktion

Wenn wir die .so Datei erfolgreich geladen .so , müssen wir wie im ersten Beispiel auf unsere Funktion zugreifen.

Wenn eine nicht vorhandene Funktion verwendet wird, wird ein AttributeError ausgelöst:

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

Basisobjekt für ctypes

Das grundlegendste Objekt ist ein int:

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

Nun bezieht sich obj auf einen Speicherblock, der den Wert 12 enthält.

Dieser Wert kann direkt abgerufen und sogar geändert werden:

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

Da sich obj auf einen Speicherplatz bezieht, können wir auch die Größe und den Speicherort ermitteln:

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

ctypes Arrays

Wie jeder gute C-Programmierer weiß, wird ein einzelner Wert Sie nicht so weit bringen. Was uns wirklich in Fahrt bringen wird, sind Arrays!

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

Dies ist kein tatsächliches Array, aber es ist verdammt nah! Wir haben eine Klasse erstellt, die ein Array von 16 int s kennzeichnet.

Jetzt müssen wir es nur noch initialisieren:

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

Jetzt ist arr ein tatsächliches Array, das die Zahlen von 0 bis 15 enthält.

Sie können wie jede Liste aufgerufen werden:

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

Und wie jedes andere ctypes Objekt hat es auch eine Größe und einen Ort:

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

Wrapping-Funktionen für ctypes

In einigen Fällen akzeptiert eine C-Funktion einen Funktionszeiger. Als avid ctypes Benutzer möchten wir diese Funktionen verwenden und sogar die Python-Funktion als Argumente übergeben.

Definieren wir eine Funktion:

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

Nun nimmt diese Funktion zwei Argumente an und gibt ein Ergebnis desselben Typs zurück. Nehmen wir im Beispiel an, dass type ein int ist.

Wie beim Array-Beispiel können wir ein Objekt definieren, das diesen Prototyp kennzeichnet:

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

Dieser Prototyp bezeichnet eine Funktion, die ein c_int (das erste Argument) c_int und zwei c_int Argumente (die anderen Argumente) akzeptiert.

Lassen Sie uns nun die Funktion umschließen:

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

Funktionsprototypen haben mehr Verwendung: Sie können ctypes function (wie libc.ntohl ) libc.ntohl und überprüfen, ob beim Aufrufen der Funktion die richtigen Argumente verwendet werden.

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

Komplexe Verwendung

Lassen Sie uns alle obigen Beispiele in einem komplexen Szenario kombinieren: Verwenden Sie die lfind Funktion von libc .

Weitere Informationen zu dieser Funktion finden Sie in der Manpage . Ich bitte Sie dringend, es zu lesen, bevor Sie fortfahren.

Zuerst definieren wir die richtigen Prototypen:

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

Dann lassen Sie uns die Variablen erstellen:

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

Und jetzt definieren wir die Vergleichsfunktion:

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

Beachten Sie, dass x und y POINTER(c_int) sind POINTER(c_int) müssen wir sie dereferenzieren und ihre Werte verwenden, um den im Speicher gespeicherten Wert vergleichen zu können.

Jetzt können wir alles miteinander kombinieren:

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

ptr ist der zurückgegebene Leerzeiger. Wenn der key nicht in arr gefunden wurde, arr der Wert None , aber in diesem Fall haben wir einen gültigen Wert erhalten.

Jetzt können wir es konvertieren und auf den Wert zugreifen:

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

Wir können auch sehen, dass ptr auf den korrekten Wert innerhalb von arr :

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


Modified text is an extract of the original Stack Overflow Documentation
Lizenziert unter CC BY-SA 3.0
Nicht angeschlossen an Stack Overflow