ctypes
— A foreign function library for Python¶
Source code: Lib/ctypes
ctypes
es una biblioteca de funciones foráneas para Python. Proporciona tipos de datos compatibles con C y permite llamar a funciones en archivos DLL o bibliotecas compartidas. Se puede utilizar para envolver estas bibliotecas en Python puro.
tutorial de ctypes¶
Nota: Los ejemplos de código en este tutorial usan doctest
para asegurarse de que realmente funcionen. Dado que algunos ejemplos de código se comportan de manera diferente en Linux, Windows o macOS, contienen directivas doctest en los comentarios.
Nota: Algunos ejemplos de código hacen referencia al tipo ctypes c_int
. En las plataformas donde sizeof(long) == sizeof(int)
es un alias de c_long
. Por lo tanto, no debe confundirse si c_long
se imprime si espera c_int
— son en realidad del mismo tipo.
Carga de bibliotecas de enlaces dinámicos¶
ctypes
exporta los objetos cdll y en Windows windll y oledll, para cargar bibliotecas de enlaces dinámicos.
You load libraries by accessing them as attributes of these objects. cdll
loads libraries which export functions using the standard cdecl
calling
convention, while windll libraries call functions using the stdcall
calling convention. oledll also uses the stdcall
calling convention, and
assumes the functions return a Windows HRESULT
error code. The error
code is used to automatically raise an OSError
exception when the
function call fails.
Distinto en la versión 3.3: Los errores de Windows solían generar WindowsError
, que ahora es un alias de OSError
.
Here are some examples for Windows. Note that msvcrt
is the MS standard C
library containing most standard C functions, and uses the cdecl
calling
convention:
>>> from ctypes import *
>>> print(windll.kernel32)
<WinDLL 'kernel32', handle ... at ...>
>>> print(cdll.msvcrt)
<CDLL 'msvcrt', handle ... at ...>
>>> libc = cdll.msvcrt
>>>
Windows agrega automáticamente la extensión común .dll
.
Nota
Acceder a la biblioteca estándar de C a través de cdll.msvcrt
utilizará una versión obsoleta de la biblioteca que puede ser incompatible con la utilizada por Python. Cuando sea posible, use la funcionalidad nativa de Python, o bien importe y use el módulo msvcrt
.
On Linux, it is required to specify the filename including the extension to
load a library, so attribute access can not be used to load libraries. Either the
LoadLibrary()
method of the dll loaders should be used,
or you should load the library by creating an instance of CDLL by calling
the constructor:
>>> cdll.LoadLibrary("libc.so.6")
<CDLL 'libc.so.6', handle ... at ...>
>>> libc = CDLL("libc.so.6")
>>> libc
<CDLL 'libc.so.6', handle ... at ...>
>>>
Acceder a las funciones de los dll cargados¶
Las funciones se acceden como atributos de los objetos dll:
>>> libc.printf
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.GetModuleHandleA)
<_FuncPtr object at 0x...>
>>> print(windll.kernel32.MyOwnFunction)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "ctypes.py", line 239, in __getattr__
func = _StdcallFuncPtr(name, self)
AttributeError: function 'MyOwnFunction' not found
>>>
Note that win32 system dlls like kernel32
and user32
often export ANSI
as well as UNICODE versions of a function. The UNICODE version is exported with
a W
appended to the name, while the ANSI version is exported with an A
appended to the name. The win32 GetModuleHandle
function, which returns a
module handle for a given module name, has the following C prototype, and a
macro is used to expose one of them as GetModuleHandle
depending on whether
UNICODE is defined or not:
/* ANSI version */
HMODULE GetModuleHandleA(LPCSTR lpModuleName);
/* UNICODE version */
HMODULE GetModuleHandleW(LPCWSTR lpModuleName);
windll no intenta seleccionar una de ellas por arte de magia, se debe acceder a la versión que se necesita especificando GetModuleHandleA
o GetModuleHandleW
explícitamente, y luego llamarlo con bytes u objetos de cadena respectivamente.
A veces, las dlls exportan funciones con nombres que no son identificadores válidos de Python, como "??2@YAPAXI@Z"
. En este caso tienes que usar getattr()
para recuperar la función:
>>> getattr(cdll.msvcrt, "??2@YAPAXI@Z")
<_FuncPtr object at 0x...>
>>>
En Windows, algunas dlls exportan funciones no por nombre sino por ordinal. Se pueden acceder a estas funciones indexando el objeto dll con el número ordinal:
>>> cdll.kernel32[1]
<_FuncPtr object at 0x...>
>>> cdll.kernel32[0]
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "ctypes.py", line 310, in __getitem__
func = _StdcallFuncPtr(name, self)
AttributeError: function ordinal 0 not found
>>>
Funciones de llamada¶
You can call these functions like any other Python callable. This example uses
the rand()
function, which takes no arguments and returns a pseudo-random integer:
>>> print(libc.rand())
1804289383
On Windows, you can call the GetModuleHandleA()
function, which returns a win32 module
handle (passing None
as single argument to call it with a NULL
pointer):
>>> print(hex(windll.kernel32.GetModuleHandleA(None)))
0x1d000000
>>>
ValueError
es lanzado cuando se llama a una función stdcall
con la convención de llamada cdecl
, o viceversa:
>>> cdll.kernel32.GetModuleHandleA(None)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with not enough arguments (4 bytes missing)
>>>
>>> windll.msvcrt.printf(b"spam")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ValueError: Procedure probably called with too many arguments (4 bytes in excess)
>>>
Para saber la convención de llamada correcta, hay que mirar en el archivo de encabezado C o en la documentación de la función que se quiere llamar.
En Windows, ctypes
utiliza la gestión de excepciones estructurada de win32 para evitar que se produzcan fallos de protección general cuando se llaman funciones con valores de argumento inválidos:
>>> windll.kernel32.GetModuleHandleA(32)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
OSError: exception: access violation reading 0x00000020
>>>
Sin embargo, hay suficientes maneras de bloquear Python con ctypes
, así que debes tener cuidado de todos modos. El módulo faulthandler
puede ser útil para depurar bloqueos (por ejemplo, provenientes de fallos de segmentación producidos por llamadas erróneas a la biblioteca C).
None
, enteros, objetos de bytes y cadenas (unicode) son los únicos objetos nativos de Python que se pueden usar directamente como parámetros en estas llamadas a funciones. None
se pasa como puntero C NULL
, objetos de bytes y cadenas se pasan como puntero al bloque de memoria que contiene sus datos (char* or wchar_t*). Los enteros de Python se pasan como el tipo C int predeterminado de la plataforma, su valor se enmascara para encajar en el tipo C.
Antes de pasar a llamar funciones con otros tipos de parámetros, tenemos que aprender más sobre los tipos de datos ctypes
.
Tipos de datos fundamentales¶
ctypes
define un número de tipos de datos primitivos compatibles con C:
tipo ctypes |
Tipo C |
Tipo Python |
---|---|---|
_Bool |
bool (1) |
|
char |
Un objeto bytes de 1-caracter |
|
|
Una cadena de 1-caracter |
|
char |
entero |
|
unsigned char |
entero |
|
short |
entero |
|
unsigned short |
entero |
|
int |
entero |
|
unsigned int |
entero |
|
long |
entero |
|
unsigned long |
entero |
|
__int64 o long long |
entero |
|
unsigned __int64 o unsigned long long |
entero |
|
|
entero |
|
|
entero |
|
|
entero |
|
float |
flotante |
|
double |
flotante |
|
long double |
flotante |
|
char* (terminado en NUL) |
objeto de bytes o |
|
wchar_t* (terminado en NUL) |
cadena o |
|
void* |
entero o |
El constructor acepta cualquier objeto con valor verdadero.
Todos estos tipos pueden ser creados llamándolos con un inicializador opcional del tipo y valor correctos:
>>> c_int()
c_long(0)
>>> c_wchar_p("Hello, World")
c_wchar_p(140018365411392)
>>> c_ushort(-3)
c_ushort(65533)
>>>
Dado que estos tipos son mutables, su valor también puede ser cambiado después:
>>> i = c_int(42)
>>> print(i)
c_long(42)
>>> print(i.value)
42
>>> i.value = -99
>>> print(i.value)
-99
>>>
Asignando un nuevo valor a las instancias de los tipos de punteros c_char_p
, c_wchar_p
, y c_void_p
cambia el lugar de memoria al que apuntan, no el contenido del bloque de memoria (por supuesto que no, porque los objetos de bytes de Python son inmutables):
>>> s = "Hello, World"
>>> c_s = c_wchar_p(s)
>>> print(c_s)
c_wchar_p(139966785747344)
>>> print(c_s.value)
Hello World
>>> c_s.value = "Hi, there"
>>> print(c_s) # the memory location has changed
c_wchar_p(139966783348904)
>>> print(c_s.value)
Hi, there
>>> print(s) # first object is unchanged
Hello, World
>>>
Sin embargo, debe tener cuidado de no pasarlos a funciones que esperan punteros a la memoria mutable. Si necesitas bloques de memoria mutables, ctypes tiene una función create_string_buffer()
que los crea de varias maneras. El contenido actual del bloque de memoria puede ser accedido (o cambiado) con la propiedad raw
; si quieres acceder a él como cadena terminada NUL, usa la propiedad value
:
>>> from ctypes import *
>>> p = create_string_buffer(3) # create a 3 byte buffer, initialized to NUL bytes
>>> print(sizeof(p), repr(p.raw))
3 b'\x00\x00\x00'
>>> p = create_string_buffer(b"Hello") # create a buffer containing a NUL terminated string
>>> print(sizeof(p), repr(p.raw))
6 b'Hello\x00'
>>> print(repr(p.value))
b'Hello'
>>> p = create_string_buffer(b"Hello", 10) # create a 10 byte buffer
>>> print(sizeof(p), repr(p.raw))
10 b'Hello\x00\x00\x00\x00\x00'
>>> p.value = b"Hi"
>>> print(sizeof(p), repr(p.raw))
10 b'Hi\x00lo\x00\x00\x00\x00\x00'
>>>
The create_string_buffer()
function replaces the old c_buffer()
function (which is still available as an alias). To create a mutable memory
block containing unicode characters of the C type wchar_t
, use the
create_unicode_buffer()
function.
Funciones de llamada, continuación¶
Note que printf imprime al canal de salida estándar real, no a sys.stdout
, por lo que estos ejemplos sólo funcionarán en el prompt de la consola, no desde dentro de IDLE o PythonWin:
>>> printf = libc.printf
>>> printf(b"Hello, %s\n", b"World!")
Hello, World!
14
>>> printf(b"Hello, %S\n", "World!")
Hello, World!
14
>>> printf(b"%d bottles of beer\n", 42)
42 bottles of beer
19
>>> printf(b"%f bottles of beer\n", 42.5)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ArgumentError: argument 2: TypeError: Don't know how to convert parameter 2
>>>
Como se ha mencionado antes, todos los tipos de Python, excepto los enteros, cadenas y objetos bytes, tienen que ser envueltos en su correspondiente tipo ctypes
, para que puedan ser convertidos al tipo de datos C requerido:
>>> printf(b"An int %d, a double %f\n", 1234, c_double(3.14))
An int 1234, a double 3.140000
31
>>>
Calling variadic functions¶
On a lot of platforms calling variadic functions through ctypes is exactly the same as calling functions with a fixed number of parameters. On some platforms, and in particular ARM64 for Apple Platforms, the calling convention for variadic functions is different than that for regular functions.
On those platforms it is required to specify the argtypes
attribute for the regular, non-variadic, function arguments:
libc.printf.argtypes = [ctypes.c_char_p]
Because specifying the attribute does not inhibit portability it is advised to always
specify argtypes
for all variadic functions.
Funciones de llamada con sus propios tipos de datos personalizados¶
You can also customize ctypes
argument conversion to allow instances of
your own classes be used as function arguments. ctypes
looks for an
_as_parameter_
attribute and uses this as the function argument. The
attribute must be an integer, string, bytes, a ctypes
instance, or an
object with an _as_parameter_
attribute:
>>> class Bottles:
... def __init__(self, number):
... self._as_parameter_ = number
...
>>> bottles = Bottles(42)
>>> printf(b"%d bottles of beer\n", bottles)
42 bottles of beer
19
>>>
If you don’t want to store the instance’s data in the _as_parameter_
instance variable, you could define a property
which makes the
attribute available on request.
Especificar los tipos de argumentos requeridos (prototipos de funciones)¶
It is possible to specify the required argument types of functions exported from
DLLs by setting the argtypes
attribute.
argtypes
must be a sequence of C data types (the printf()
function is
probably not a good example here, because it takes a variable number and
different types of parameters depending on the format string, on the other hand
this is quite handy to experiment with this feature):
>>> printf.argtypes = [c_char_p, c_char_p, c_int, c_double]
>>> printf(b"String '%s', Int %d, Double %f\n", b"Hi", 10, 2.2)
String 'Hi', Int 10, Double 2.200000
37
>>>
La especificación de un formato protege contra los tipos de argumentos incompatibles (al igual que un prototipo para una función C), e intenta convertir los argumentos en tipos válidos:
>>> printf(b"%d %d %d", 1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
ArgumentError: argument 2: TypeError: wrong type
>>> printf(b"%s %d %f\n", b"X", 2, 3)
X 2 3.000000
13
>>>
If you have defined your own classes which you pass to function calls, you have
to implement a from_param()
class method for them to be able to use them
in the argtypes
sequence. The from_param()
class method receives
the Python object passed to the function call, it should do a typecheck or
whatever is needed to make sure this object is acceptable, and then return the
object itself, its _as_parameter_
attribute, or whatever you want to
pass as the C function argument in this case. Again, the result should be an
integer, string, bytes, a ctypes
instance, or an object with an
_as_parameter_
attribute.
Tipos de retorno¶
By default functions are assumed to return the C int type. Other
return types can be specified by setting the restype
attribute of the
function object.
The C prototype of time()
is time_t time(time_t *)
. Because time_t
might be of a different type than the default return type int, you should
specify the restype
attribute:
>>> libc.time.restype = c_time_t
The argument types can be specified using argtypes
:
>>> libc.time.argtypes = (POINTER(c_time_t),)
To call the function with a NULL
pointer as first argument, use None
:
>>> print(libc.time(None))
1150640792
Here is a more advanced example, it uses the strchr()
function, which expects
a string pointer and a char, and returns a pointer to a string:
>>> strchr = libc.strchr
>>> strchr(b"abcdef", ord("d"))
8059983
>>> strchr.restype = c_char_p # c_char_p is a pointer to a string
>>> strchr(b"abcdef", ord("d"))
b'def'
>>> print(strchr(b"abcdef", ord("x")))
None
>>>
If you want to avoid the ord("x")
calls above, you can set the
argtypes
attribute, and the second argument will be converted from a
single character Python bytes object into a C char:
>>> strchr.restype = c_char_p
>>> strchr.argtypes = [c_char_p, c_char]
>>> strchr(b"abcdef", b"d")
b'def'
>>> strchr(b"abcdef", b"def")
Traceback (most recent call last):
ctypes.ArgumentError: argument 2: TypeError: one character bytes, bytearray or integer expected
>>> print(strchr(b"abcdef", b"x"))
None
>>> strchr(b"abcdef", b"d")
b'def'
>>>
You can also use a callable Python object (a function or a class for example) as
the restype
attribute, if the foreign function returns an integer. The
callable will be called with the integer the C function returns, and the
result of this call will be used as the result of your function call. This is
useful to check for error return values and automatically raise an exception:
>>> GetModuleHandle = windll.kernel32.GetModuleHandleA
>>> def ValidHandle(value):
... if value == 0:
... raise WinError()
... return value
...
>>>
>>> GetModuleHandle.restype = ValidHandle
>>> GetModuleHandle(None)
486539264
>>> GetModuleHandle("something silly")
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 3, in ValidHandle
OSError: [Errno 126] The specified module could not be found.
>>>
WinError
es una función que llamará a la api Windows FormatMessage
para obtener la representación de la cadena de un código de error, y retornará una excepción. WinError
toma un parámetro de código de error opcional, si no se usa ninguno, llama a GetLastError`()
para recuperarlo.
Please note that a much more powerful error checking mechanism is available
through the errcheck
attribute;
see the reference manual for details.
Pasar los punteros (o: pasar los parámetros por referencia)¶
A veces una función api C espera un puntero a un tipo de datos como parámetro, probablemente para escribir en el lugar correspondiente, o si los datos son demasiado grandes para ser pasados por valor. Esto también se conoce cómo pasar parámetros por referencia.
ctypes
exporta la función byref()
que se utiliza para pasar parámetros por referencia. El mismo efecto se puede conseguir con la función pointer()
, aunque pointer()
hace mucho más trabajo ya que construye un objeto puntero real, por lo que es más rápido usar byref()
si no se necesita el objeto puntero en el propio Python:
>>> i = c_int()
>>> f = c_float()
>>> s = create_string_buffer(b'\000' * 32)
>>> print(i.value, f.value, repr(s.value))
0 0.0 b''
>>> libc.sscanf(b"1 3.14 Hello", b"%d %f %s",
... byref(i), byref(f), s)
3
>>> print(i.value, f.value, repr(s.value))
1 3.1400001049 b'Hello'
>>>
Estructuras y uniones¶
Structures and unions must derive from the Structure
and Union
base classes which are defined in the ctypes
module. Each subclass must
define a _fields_
attribute. _fields_
must be a list of
2-tuples, containing a field name and a field type.
El tipo de campo debe ser un tipo ctypes
como c_int
, o cualquier otro tipo ctypes
derivado: estructura, unión, matriz, puntero.
Aquí hay un ejemplo simple de una estructura POINT, que contiene dos enteros llamados x y y, y también muestra cómo inicializar una estructura en el constructor:
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = [("x", c_int),
... ("y", c_int)]
...
>>> point = POINT(10, 20)
>>> print(point.x, point.y)
10 20
>>> point = POINT(y=5)
>>> print(point.x, point.y)
0 5
>>> POINT(1, 2, 3)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: too many initializers
>>>
Sin embargo, se pueden construir estructuras mucho más complicadas. Una estructura puede contener por sí misma otras estructuras usando una estructura como tipo de campo.
Aquí hay una estructura RECT que contiene dos POINTs llamados upperleft (superior izquierda)y lowerright (abajo a la derecha):
>>> class RECT(Structure):
... _fields_ = [("upperleft", POINT),
... ("lowerright", POINT)]
...
>>> rc = RECT(point)
>>> print(rc.upperleft.x, rc.upperleft.y)
0 5
>>> print(rc.lowerright.x, rc.lowerright.y)
0 0
>>>
Las estructuras anidadas también pueden ser inicializadas en el constructor de varias maneras:
>>> r = RECT(POINT(1, 2), POINT(3, 4))
>>> r = RECT((1, 2), (3, 4))
El campo descriptor puede ser recuperado de la class, son útiles para la depuración porque pueden proporcionar información útil:
>>> print(POINT.x)
<Field type=c_long, ofs=0, size=4>
>>> print(POINT.y)
<Field type=c_long, ofs=4, size=4>
>>>
Advertencia
ctypes
no soporta el paso de uniones o estructuras con campos de bits a funciones por valor. Aunque esto puede funcionar en 32-bit x86, la biblioteca no garantiza que funcione en el caso general. Las uniones y estructuras con campos de bits siempre deben pasarse a las funciones por puntero.
Alineación de estructura/unión y orden de bytes¶
By default, Structure and Union fields are aligned in the same way the C
compiler does it. It is possible to override this behavior by specifying a
_pack_
class attribute in the subclass definition.
This must be set to a positive integer and specifies the maximum alignment for the fields.
This is what #pragma pack(n)
also does in MSVC.
ctypes
utiliza el orden de bytes nativos para las Estructuras y Uniones. Para construir estructuras con un orden de bytes no nativo, puedes usar una de las clases base BigEndianStructure
, LittleEndianStructure
, BigEndianUnion
, y LittleEndianUnion
. Estas clases no pueden contener campos puntero.
Campos de bits en estructuras y uniones¶
It is possible to create structures and unions containing bit fields. Bit fields
are only possible for integer fields, the bit width is specified as the third
item in the _fields_
tuples:
>>> class Int(Structure):
... _fields_ = [("first_16", c_int, 16),
... ("second_16", c_int, 16)]
...
>>> print(Int.first_16)
<Field type=c_long, ofs=0:0, bits=16>
>>> print(Int.second_16)
<Field type=c_long, ofs=0:16, bits=16>
>>>
Arreglos¶
Los arreglos son secuencias, que contienen un número fijo de instancias del mismo tipo.
La forma recomendada de crear tipos de arreglos es multiplicando un tipo de dato por un entero positivo:
TenPointsArrayType = POINT * 10
Aquí hay un ejemplo de un tipo de datos algo artificial, una estructura que contiene 4 POINTs entre otras cosas:
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = ("x", c_int), ("y", c_int)
...
>>> class MyStruct(Structure):
... _fields_ = [("a", c_int),
... ("b", c_float),
... ("point_array", POINT * 4)]
>>>
>>> print(len(MyStruct().point_array))
4
>>>
Las instancias se crean de la manera habitual, llamando a la clase:
arr = TenPointsArrayType()
for pt in arr:
print(pt.x, pt.y)
El código anterior imprime una serie de líneas 0 0
, porque el contenido del arreglos se inicializa con ceros.
También se pueden especificar inicializadores del tipo correcto:
>>> from ctypes import *
>>> TenIntegers = c_int * 10
>>> ii = TenIntegers(1, 2, 3, 4, 5, 6, 7, 8, 9, 10)
>>> print(ii)
<c_long_Array_10 object at 0x...>
>>> for i in ii: print(i, end=" ")
...
1 2 3 4 5 6 7 8 9 10
>>>
Punteros¶
Las instancias de puntero se crean llamando a la función pointer()
en un tipo ctypes
:
>>> from ctypes import *
>>> i = c_int(42)
>>> pi = pointer(i)
>>>
Las instancias del puntero tienen un atributo contents
que retorna el objeto al que apunta el puntero, el objeto i
arriba:
>>> pi.contents
c_long(42)
>>>
Ten en cuenta que ctypes
no tiene OOR (original object return), construye un nuevo objeto equivalente cada vez que recuperas un atributo:
>>> pi.contents is i
False
>>> pi.contents is pi.contents
False
>>>
Asignar otra instancia c_int
al atributo de contenido del puntero causaría que el puntero apunte al lugar de memoria donde se almacena:
>>> i = c_int(99)
>>> pi.contents = i
>>> pi.contents
c_long(99)
>>>
Las instancias de puntero también pueden ser indexadas con números enteros:
>>> pi[0]
99
>>>
Asignando a un índice entero cambia el valor señalado:
>>> print(i)
c_long(99)
>>> pi[0] = 22
>>> print(i)
c_long(22)
>>>
También es posible usar índices diferentes de 0, pero debes saber lo que estás haciendo, al igual que en C: Puedes acceder o cambiar arbitrariamente las ubicaciones de memoria. Generalmente sólo usas esta característica si recibes un puntero de una función C, y sabes que el puntero en realidad apunta a un arreglo en lugar de a un solo elemento.
Entre bastidores, la función pointer()
hace más que simplemente crear instancias de puntero, tiene que crear primero punteros tipos. Esto se hace con la función POINTER()
, que acepta cualquier tipo de ctypes
, y retorna un nuevo tipo:
>>> PI = POINTER(c_int)
>>> PI
<class 'ctypes.LP_c_long'>
>>> PI(42)
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: expected c_long instead of int
>>> PI(c_int(42))
<ctypes.LP_c_long object at 0x...>
>>>
Llamar al tipo de puntero sin un argumento crea un puntero NULL
. Los punteros NULL
tienen un valor booleano falso..:
>>> null_ptr = POINTER(c_int)()
>>> print(bool(null_ptr))
False
>>>
ctypes
comprueba si hay NULL
cuando los punteros de referencia (pero los punteros no válidos de referencia no-NULL
se romperán en Python):
>>> null_ptr[0]
Traceback (most recent call last):
....
ValueError: NULL pointer access
>>>
>>> null_ptr[0] = 1234
Traceback (most recent call last):
....
ValueError: NULL pointer access
>>>
Conversiones de tipos¶
Usually, ctypes does strict type checking. This means, if you have
POINTER(c_int)
in the argtypes
list of a function or as the type of
a member field in a structure definition, only instances of exactly the same
type are accepted. There are some exceptions to this rule, where ctypes accepts
other objects. For example, you can pass compatible array instances instead of
pointer types. So, for POINTER(c_int)
, ctypes accepts an array of c_int:
>>> class Bar(Structure):
... _fields_ = [("count", c_int), ("values", POINTER(c_int))]
...
>>> bar = Bar()
>>> bar.values = (c_int * 3)(1, 2, 3)
>>> bar.count = 3
>>> for i in range(bar.count):
... print(bar.values[i])
...
1
2
3
>>>
In addition, if a function argument is explicitly declared to be a pointer type
(such as POINTER(c_int)
) in argtypes
, an object of the pointed
type (c_int
in this case) can be passed to the function. ctypes will apply
the required byref()
conversion in this case automatically.
Para poner un campo de tipo POINTER a NULL
, puedes asignar None
:
>>> bar.values = None
>>>
A veces se tienen instancias de tipos incompatibles. En C, puedes cambiar un tipo por otro tipo. ctypes
proporciona una función cast()
qué puede ser usada de la misma manera. La estructura Bar
definida arriba acepta punteros POINTER(c_int)
o arreglos c_int`
para su campo values
, pero no instancias de otros tipos:
>>> bar.values = (c_byte * 4)()
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
TypeError: incompatible types, c_byte_Array_4 instance instead of LP_c_long instance
>>>
Para estos casos, la función cast()
es muy útil.
La función cast()
puede ser usada para lanzar una instancia ctypes en un puntero a un tipo de datos ctypes diferente. cast()
toma dos parámetros, un objeto ctypes que es o puede ser convertido en un puntero de algún tipo, y un tipo de puntero ctypes. retorna una instancia del segundo argumento, que hace referencia al mismo bloque de memoria que el primer argumento:
>>> a = (c_byte * 4)()
>>> cast(a, POINTER(c_int))
<ctypes.LP_c_long object at ...>
>>>
Así, cast()
puede ser usado para asignar al campo values
de Bar
la estructura:
>>> bar = Bar()
>>> bar.values = cast((c_byte * 4)(), POINTER(c_int))
>>> print(bar.values[0])
0
>>>
Tipos incompletos¶
Los Tipos Incompletos son estructuras, uniones o matrices cuyos miembros aún no están especificados. En C, se especifican mediante declaraciones a futuro, que se definen más adelante:
struct cell; /* forward declaration */
struct cell {
char *name;
struct cell *next;
};
La traducción directa al código de ctypes sería esta, pero no funciona:
>>> class cell(Structure):
... _fields_ = [("name", c_char_p),
... ("next", POINTER(cell))]
...
Traceback (most recent call last):
File "<stdin>", line 1, in <module>
File "<stdin>", line 2, in cell
NameError: name 'cell' is not defined
>>>
because the new class cell
is not available in the class statement itself.
In ctypes
, we can define the cell
class and set the
_fields_
attribute later, after the class statement:
>>> from ctypes import *
>>> class cell(Structure):
... pass
...
>>> cell._fields_ = [("name", c_char_p),
... ("next", POINTER(cell))]
>>>
Vamos a intentarlo. Creamos dos instancias de cell
, y dejamos que se apunten una a la otra, y finalmente seguimos la cadena de punteros unas cuantas veces:
>>> c1 = cell()
>>> c1.name = b"foo"
>>> c2 = cell()
>>> c2.name = b"bar"
>>> c1.next = pointer(c2)
>>> c2.next = pointer(c1)
>>> p = c1
>>> for i in range(8):
... print(p.name, end=" ")
... p = p.next[0]
...
foo bar foo bar foo bar foo bar
>>>
Funciones de retrollamadas (callback)¶
ctypes
permite crear punteros de función invocables C a partir de los invocables de Python. A veces se llaman funciones de retrollamada.
Primero, debes crear una clase para la función de retrollamada. La clase conoce la convención de llamada, el tipo de retorno, y el número y tipos de argumentos que esta función recibirá.
La función de fábrica CFUNCTYPE`()
crea tipos para las funciones de retrollamada usando la convención de llamada cdecl
. En Windows, la función de fábrica WINFUNCTYPE()
crea tipos para funciones de retrollamadas usando la convención de llamadas stdcall
.
Ambas funciones de fábrica se llaman con el tipo de resultado como primer argumento, y las funciones de llamada de retorno con los tipos de argumentos esperados como los argumentos restantes.
I will present an example here which uses the standard C library’s
qsort()
function, that is used to sort items with the help of a callback
function. qsort()
will be used to sort an array of integers:
>>> IntArray5 = c_int * 5
>>> ia = IntArray5(5, 1, 7, 33, 99)
>>> qsort = libc.qsort
>>> qsort.restype = None
>>>
qsort()
must be called with a pointer to the data to sort, the number of
items in the data array, the size of one item, and a pointer to the comparison
function, the callback. The callback will then be called with two pointers to
items, and it must return a negative integer if the first item is smaller than
the second, a zero if they are equal, and a positive integer otherwise.
Así que nuestra función de retrollamada recibe punteros a números enteros, y debe retornar un número entero. Primero creamos el tipo
para la función de retrollamada:
>>> CMPFUNC = CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
>>>
Para empezar, aquí hay una simple llamada que muestra los valores que se pasan:
>>> def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return 0
...
>>> cmp_func = CMPFUNC(py_cmp_func)
>>>
El resultado:
>>> qsort(ia, len(ia), sizeof(c_int), cmp_func)
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 5 7
py_cmp_func 1 7
>>>
Ahora podemos comparar los dos artículos y obtener un resultado útil:
>>> def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return a[0] - b[0]
...
>>>
>>> qsort(ia, len(ia), sizeof(c_int), CMPFUNC(py_cmp_func))
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>
Como podemos comprobar fácilmente, nuestro arreglo está ordenado ahora:
>>> for i in ia: print(i, end=" ")
...
1 5 7 33 99
>>>
Las funciones de fabrica pueden ser usadas como decoradores de fabrica, así que podemos escribir:
>>> @CFUNCTYPE(c_int, POINTER(c_int), POINTER(c_int))
... def py_cmp_func(a, b):
... print("py_cmp_func", a[0], b[0])
... return a[0] - b[0]
...
>>> qsort(ia, len(ia), sizeof(c_int), py_cmp_func)
py_cmp_func 5 1
py_cmp_func 33 99
py_cmp_func 7 33
py_cmp_func 1 7
py_cmp_func 5 7
>>>
Nota
Asegúrate de mantener las referencias a los objetos CFUNCTYPE()
mientras se usen desde el código C. ctypes
no lo hace, y si no lo haces, pueden ser basura recolectada, colapsando tu programa cuando se hace una llamada.
Además, nótese que sí se llama a la función de retrollamada en un hilo creado fuera del control de Python (por ejemplo, por el código foráneo que llama a la retrollamada), ctypes crea un nuevo hilo Python tonto en cada invocación. Este comportamiento es correcto para la mayoría de los propósitos, pero significa que los valores almacenados con threading.local
no sobreviven a través de diferentes llamadas de retorno, incluso cuando esas llamadas se hacen desde el mismo hilo C.
Acceder a los valores exportados de los dlls¶
Some shared libraries not only export functions, they also export variables. An
example in the Python library itself is the Py_Version
, Python
runtime version number encoded in a single constant integer.
ctypes
can access values like this with the in_dll()
class methods of
the type. pythonapi is a predefined symbol giving access to the Python C
api:
>>> version = ctypes.c_int.in_dll(ctypes.pythonapi, "Py_Version")
>>> print(hex(version.value))
0x30c00a0
Un ejemplo extendido que también demuestra el uso de punteros accediendo al puntero PyImport_FrozenModules
exportado por Python.
Citando los documentos para ese valor:
Este puntero se inicializa para apuntar a un arreglo de registros
_frozen
, terminados por uno cuyos miembros son todosNULL
o cero. Cuando se importa un módulo congelado, se busca en esta tabla. El código de terceros podría jugar trucos con esto para proporcionar una colección de módulos congelados creada dinámicamente.
Así que manipular este puntero podría incluso resultar útil. Para restringir el tamaño del ejemplo, sólo mostramos cómo esta tabla puede ser leída con ctypes
:
>>> from ctypes import *
>>>
>>> class struct_frozen(Structure):
... _fields_ = [("name", c_char_p),
... ("code", POINTER(c_ubyte)),
... ("size", c_int),
... ("get_code", POINTER(c_ubyte)), # Function pointer
... ]
...
>>>
Hemos definido el tipo de datos _frozen
, por lo que podemos obtener el puntero a la tabla:
>>> FrozenTable = POINTER(struct_frozen)
>>> table = FrozenTable.in_dll(pythonapi, "_PyImport_FrozenBootstrap")
>>>
Como tabla
es un puntero
al arreglo de registros struct_frozen
, podemos iterar sobre ella, pero sólo tenemos que asegurarnos de que nuestro bucle termine, porque los punteros no tienen tamaño. Tarde o temprano, probablemente se caerá con una violación de acceso o lo que sea, así que es mejor salir del bucle cuando le demos a la entrada 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
zipimport 12345
>>>
El hecho de que la Python estándar tenga un módulo congelado y un paquete congelado (indicado por el miembro tamaño
negativo) no se conoce bien, sólo se usa para hacer pruebas. Pruébalo con import __hello__
por ejemplo.
Sorpresas¶
Hay algunas aristas en ctypes
en las que podrías esperar algo distinto de lo que realmente sucede.
Considere el siguiente ejemplo:
>>> from ctypes import *
>>> class POINT(Structure):
... _fields_ = ("x", c_int), ("y", c_int)
...
>>> class RECT(Structure):
... _fields_ = ("a", POINT), ("b", POINT)
...
>>> p1 = POINT(1, 2)
>>> p2 = POINT(3, 4)
>>> rc = RECT(p1, p2)
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
1 2 3 4
>>> # now swap the two points
>>> rc.a, rc.b = rc.b, rc.a
>>> print(rc.a.x, rc.a.y, rc.b.x, rc.b.y)
3 4 3 4
>>>
Hm. Ciertamente esperábamos que la última declaración imprimiera 3 4 1 2
. ¿Qué ha pasado? Aquí están los pasos de la línea rc.a, rc.b = rc.b, rc.a
arriba:
>>> temp0, temp1 = rc.b, rc.a
>>> rc.a = temp0
>>> rc.b = temp1
>>>
Note que temp0
y temp1
son objetos que todavía usan el buffer interno del objeto rc
de arriba. Así que ejecutando rc.a = temp0
copia el contenido del buffer de temp0
en el buffer de rc
. Esto, a su vez, cambia el contenido de temp1
. Por lo tanto, la última asignación rc.b = temp1
, no tiene el efecto esperado.
Tengan en cuenta que la recuperación de subobjetos de Estructuras, Uniones y Arreglos no copia el subobjeto, sino que recupera un objeto contenido que accede al búfer subyacente del objeto raíz.
Otro ejemplo que puede comportarse de manera diferente a lo que uno esperaría es este:
>>> s = c_char_p()
>>> s.value = b"abc def ghi"
>>> s.value
b'abc def ghi'
>>> s.value is s.value
False
>>>
Nota
Los objetos instanciados desde c_char_p
sólo pueden tener su valor fijado en bytes o enteros.
¿Por qué está imprimiendo False
? Las instancias ctypes son objetos que contienen un bloque de memoria más algunos descriptors que acceden al contenido de la memoria. Almacenar un objeto Python en el bloque de memoria no almacena el objeto en sí mismo, en su lugar se almacenan los contenidos
del objeto. ¡Acceder a los contenidos de nuevo construye un nuevo objeto Python cada vez!
Tipos de datos de tamaño variable¶
ctypes
proporciona algo de soporte para matrices y estructuras de tamaño variable.
La función resize()
puede ser usada para redimensionar el buffer de memoria de un objeto ctypes existente. La función toma el objeto como primer argumento, y el tamaño solicitado en bytes como segundo argumento. El bloque de memoria no puede hacerse más pequeño que el bloque de memoria natural especificado por el tipo de objeto, se lanza un ValueError
si se intenta:
>>> short_array = (c_short * 4)()
>>> print(sizeof(short_array))
8
>>> resize(short_array, 4)
Traceback (most recent call last):
...
ValueError: minimum size is 8
>>> resize(short_array, 32)
>>> sizeof(short_array)
32
>>> sizeof(type(short_array))
8
>>>
Esto está bien, pero ¿cómo se puede acceder a los elementos adicionales contenidos en este arreglo? Dado que el tipo todavía sabe sólo 4 elementos, obtenemos errores al acceder a otros elementos:
>>> short_array[:]
[0, 0, 0, 0]
>>> short_array[7]
Traceback (most recent call last):
...
IndexError: invalid index
>>>
Otra forma de utilizar tipos de datos de tamaño variable con ctypes
es utilizar la naturaleza dinámica de Python, y (re)definir el tipo de datos después de que se conozca el tamaño requerido, caso por caso.
referencia ctypes¶
Funciones foráneas¶
As explained in the previous section, foreign functions can be accessed as attributes of loaded shared libraries. The function objects created in this way by default accept any number of arguments, accept any ctypes data instances as arguments, and return the default result type specified by the library loader.
They are instances of a private local class _FuncPtr
(not exposed
in ctypes
) which inherits from the private _CFuncPtr
class:
>>> import ctypes
>>> lib = ctypes.CDLL(None)
>>> issubclass(lib._FuncPtr, ctypes._CFuncPtr)
True
>>> lib._FuncPtr is ctypes._CFuncPtr
False
- class ctypes._CFuncPtr¶
Clase base para funciones foráneas C invocables.
Las instancias de funciones foráneas también son tipos de datos compatibles con C; representan punteros de funciones C.
Este comportamiento puede personalizarse asignando a los atributos especiales del objeto de la función foránea.
- restype¶
Asigne un tipo ctypes para especificar el tipo de resultado de la función externa. Use
None
para void, una función que no retorna nada.It is possible to assign a callable Python object that is not a ctypes type, in this case the function is assumed to return a C int, and the callable will be called with this integer, allowing further processing or error checking. Using this is deprecated, for more flexible post processing or error checking use a ctypes data type as
restype
and assign a callable to theerrcheck
attribute.
- argtypes¶
Asigne una tupla de tipos ctypes para especificar los tipos de argumentos que acepta la función. Las funciones que utilizan la convención de llamada
stdcall
sólo pueden ser llamadas con el mismo número de argumentos que la longitud de esta tupla; las funciones que utilizan la convención de llamada C aceptan también argumentos adicionales no especificados.When a foreign function is called, each actual argument is passed to the
from_param()
class method of the items in theargtypes
tuple, this method allows adapting the actual argument to an object that the foreign function accepts. For example, ac_char_p
item in theargtypes
tuple will convert a string passed as argument into a bytes object using ctypes conversion rules.New: It is now possible to put items in argtypes which are not ctypes types, but each item must have a
from_param()
method which returns a value usable as argument (integer, string, ctypes instance). This allows defining adapters that can adapt custom objects as function parameters.
- errcheck¶
Asigne una función Python u otra llamada a este atributo. El invocable será llamado con tres o más argumentos:
- callable(result, func, arguments)
result is what the foreign function returns, as specified by the
restype
attribute.func es el propio objeto de la función foránea, lo que permite reutilizar el mismo objeto invocable para comprobar o postprocesar los resultados de varias funciones.
arguments es una tupla que contiene los parámetros originalmente pasados a la llamada de la función, esto permite especializar el comportamiento en los argumentos utilizados.
El objeto que retorna esta función será retornado por la llamada de la función foránea, pero también puede comprobar el valor del resultado y hacer una excepción si la llamada de la función foránea ha fallado.
- exception ctypes.ArgumentError¶
Esta excepción se lanza cuando una llamada a una función foránea no puede convertir uno de los argumentos pasados.
En Windows, cuando una llamada a una función foránea plantea una excepción de sistema (por ejemplo, debido a una violación de acceso), será capturada y sustituida por una excepción Python adecuada. Además, un evento de auditoría ctypes.set_exception
con el argumento code
será levantado, permitiendo que un gancho de auditoría reemplace la excepción con la suya propia.
Algunas formas de invocar llamadas a funciones foráneas pueden lanzar un evento de auditoría ctypes.call_function
con los argumentos function pointer
y arguments
.
Prototipos de funciones¶
Las funciones foráneas también pueden crearse mediante la instanciación de prototipos de funciones. Los prototipos de funciones son similares a los prototipos de funciones en C; describen una función (tipo de retorno, tipos de argumentos, convención de llamada) sin definir una implementación. Las funciones de fábrica deben ser llamadas con el tipo de resultado deseado y los tipos de argumento de la función, y pueden ser usadas como fábricas de decoradores, y como tales, ser aplicadas a las funciones a través de la sintaxis @wrapper
. Ver Funciones de retrollamadas (callback) para ejemplos.
- ctypes.CFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)¶
El prototipo de función retornado crea funciones que usan la convención de llamada C estándar. La función liberará el GIL durante la llamada. Si use_errno se configura a true, la copia privada de ctypes de la variable del sistema
errno
se intercambia con el valor realerrno
antes y después de la llamada; use_last_error hace lo mismo con el código de error de Windows.
- ctypes.WINFUNCTYPE(restype, *argtypes, use_errno=False, use_last_error=False)¶
The returned function prototype creates functions that use the
stdcall
calling convention. The function will release the GIL during the call. use_errno and use_last_error have the same meaning as above.Availability: Windows
- ctypes.PYFUNCTYPE(restype, *argtypes)¶
El prototipo de función retornado crea funciones que usan la convención de llamadas de Python. La función no liberará el GIL durante la llamada.
Los prototipos de funciones creados por estas funciones de fábrica pueden ser instanciados de diferentes maneras, dependiendo del tipo y el número de los parámetros en la llamada:
- prototype(address)
Retorna una función foránea en la dirección especificada que debe ser un número entero.
- prototype(callable)
Crear una función de llamada C (una función de retrollamada) a partir de un callable Python.
- prototype(func_spec[, paramflags])
Retorna una función foránea exportada por una biblioteca compartida. func_spec debe ser un 2-tupla
(name_or_ordinal, library)
. El primer elemento es el nombre de la función exportada como cadena, o el ordinal de la función exportada como entero pequeño. El segundo elemento es la instancia de la biblioteca compartida.
- prototype(vtbl_index, name[, paramflags[, iid]])
Retorna una función foránea que llamará a un método COM. vtbl_index es el índice de la tabla de funciones virtuales, un pequeño entero no negativo. name es el nombre del método COM. iid es un puntero opcional para el identificador de la interfaz que se utiliza en el informe de errores extendido.
COM methods use a special calling convention: They require a pointer to the COM interface as first argument, in addition to those parameters that are specified in the
argtypes
tuple.
El parámetro opcional paramflags crea envoltorios de funciones foráneas con mucha más funcionalidad que las características descritas anteriormente.
paramflags must be a tuple of the same length as argtypes
.
Cada elemento de esta tupla contiene más información sobre un parámetro, debe ser una tupla que contenga uno, dos o tres elementos.
El primer elemento es un entero que contiene una combinación de flags de dirección para el parámetro:
- 1
Especifica un parámetro de entrada a la función.
- 2
Parámetro de salida. La función foránea rellena un valor.
- 4
Parámetro de entrada que por defecto es el cero entero.
El segundo elemento opcional es el nombre del parámetro como cadena. Si se especifica esto, se puede llamar a la función foránea con parámetros con nombre.
El tercer elemento opcional es el valor por defecto de este parámetro.
The following example demonstrates how to wrap the Windows MessageBoxW
function so
that it supports default parameters and named arguments. The C declaration from
the windows header file is this:
WINUSERAPI int WINAPI
MessageBoxW(
HWND hWnd,
LPCWSTR lpText,
LPCWSTR lpCaption,
UINT uType);
Aquí está el envoltorio con ctypes
:
>>> 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)
La función foránea de MessageBox
puede ser llamada de esta manera:
>>> MessageBox()
>>> MessageBox(text="Spam, spam, spam")
>>> MessageBox(flags=2, text="foo bar")
Un segundo ejemplo demuestra los parámetros de salida. La función GetWindowRect
de win32 retorna las dimensiones de una ventana especificada copiándolas en la estructura RECT
que la persona que llama tiene que suministrar. Aquí está la declaración C:
WINUSERAPI BOOL WINAPI
GetWindowRect(
HWND hWnd,
LPRECT lpRect);
Aquí está el envoltorio con ctypes
:
>>> 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)
>>>
Las funciones con parámetros de salida retornarán automáticamente el valor del parámetro de salida si hay uno solo, o una tupla que contiene los valores del parámetro de salida cuando hay más de uno, por lo que la función GetWindowRect retorna ahora una instancia RECT, cuando se llama.
Output parameters can be combined with the errcheck
protocol to do
further output processing and error checking. The win32 GetWindowRect
api
function returns a BOOL
to signal success or failure, so this function could
do the error checking, and raises an exception when the api call failed:
>>> def errcheck(result, func, args):
... if not result:
... raise WinError()
... return args
...
>>> GetWindowRect.errcheck = errcheck
>>>
If the errcheck
function returns the argument tuple it receives
unchanged, ctypes
continues the normal processing it does on the output
parameters. If you want to return a tuple of window coordinates instead of a
RECT
instance, you can retrieve the fields in the function and return them
instead, the normal processing will no longer take place:
>>> 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
>>>
Funciones de utilidad¶
- ctypes.addressof(obj)¶
Retorna la dirección del buffer de memoria como un entero. obj debe ser una instancia de tipo ctypes.
Lanza un auditing event
ctypes.addressof
con el argumentoobj
.
- ctypes.alignment(obj_or_type)¶
Retorna los requerimientos de alineación de un tipo de ctypes. obj_or_type debe ser un tipo o instancia ctypes.
- ctypes.byref(obj[, offset])¶
Retorna un puntero ligero a obj, que debe ser un ejemplo de un tipo de ctypes. offset es por defecto cero, y debe ser un entero que se añadirá al valor del puntero interno.
byref(obj, offset)
corresponde a este código C:(((char *)&obj) + offset)
El objeto retornado sólo puede ser utilizado como un parámetro de llamada de función foránea. Se comporta de manera similar a
pointer(obj)
, pero la construcción es mucho más rápida.
- ctypes.cast(obj, type)¶
Esta función es similar a la del operador de reparto en C. retorna una nueva instancia de type que apunta al mismo bloque de memoria que obj. type debe ser un tipo de puntero, y obj debe ser un objeto que pueda ser interpretado como un puntero.
- ctypes.create_string_buffer(init_or_size, size=None)¶
Esta función crea un búfer de caracteres mutables. El objeto retornado es un arreglo de ctypes de
c_char
.init_or_size debe ser un número entero que especifique el tamaño del arreglo, o un objeto de bytes que se utilizará para inicializar los elementos del arreglo.
Si se especifica un objeto bytes como primer argumento, el buffer se hace un elemento más grande que su longitud, de modo que el último elemento del arreglo es un carácter de terminación NUL. Se puede pasar un entero como segundo argumento que permite especificar el tamaño del arreglo si no se debe utilizar la longitud de los bytes.
Lanza un auditing event
ctypes.create_string_buffer
con argumentosinit
,size
.
- ctypes.create_unicode_buffer(init_or_size, size=None)¶
Esta función crea un búfer de caracteres unicode mutable. El objeto retornado es un arreglo de ctypes de
c_wchar
.init_or_size debe ser un entero que especifique el tamaño del arreglo, o una cadena que se utilizará para inicializar los elementos del arreglo.
Si se especifica una cadena como primer argumento, el búfer se hace un elemento más grande que la longitud de la cadena, de modo que el último elemento del arreglo es un carácter de terminación NUL. Se puede pasar un entero como segundo argumento que permite especificar el tamaño del arreglo si no se debe utilizar la longitud de la cadena.
Lanza un auditing event
ctypes.create_unicode_buffer
con argumentosinit
,size
.
- ctypes.DllCanUnloadNow()¶
This function is a hook which allows implementing in-process COM servers with ctypes. It is called from the DllCanUnloadNow function that the _ctypes extension dll exports.
Availability: Windows
- ctypes.DllGetClassObject()¶
This function is a hook which allows implementing in-process COM servers with ctypes. It is called from the DllGetClassObject function that the
_ctypes
extension dll exports.Availability: Windows
- ctypes.util.find_library(name)¶
Intenta encontrar una biblioteca y retornar un nombre de ruta. name es el nombre de la biblioteca sin ningún prefijo como
lib
, sufijo como.so
,.dylib
o número de versión (esta es la forma usada para la opción del enlazador posix-l
). Si no se puede encontrar ninguna biblioteca, retornaNone
.La funcionalidad exacta depende del sistema.
- ctypes.util.find_msvcrt()¶
Returns the filename of the VC runtime library used by Python, and by the extension modules. If the name of the library cannot be determined,
None
is returned.Si necesita liberar memoria, por ejemplo, asignada por un módulo de extensión con una llamada al
free(void *)
, es importante que utilice la función en la misma biblioteca que asignó la memoria.Availability: Windows
- ctypes.FormatError([code])¶
Returns a textual description of the error code code. If no error code is specified, the last error code is used by calling the Windows api function GetLastError.
Availability: Windows
- ctypes.GetLastError()¶
Returns the last error code set by Windows in the calling thread. This function calls the Windows
GetLastError()
function directly, it does not return the ctypes-private copy of the error code.Availability: Windows
- ctypes.get_errno()¶
Retorna el valor actual de la copia ctypes-private de la variable de sistema
errno
en el hilo de llamada.Lanza un auditing event
ctypes.get_errno
sin argumentos.
- ctypes.get_last_error()¶
Returns the current value of the ctypes-private copy of the system
LastError
variable in the calling thread.Availability: Windows
Lanza un auditing event
ctypes.get_last_error
sin argumentos.
- ctypes.memmove(dst, src, count)¶
Igual que la función de la biblioteca estándar de C memmove: copia count bytes de src a dst. dst y src deben ser enteros o instancias ctypes que pueden ser convertidos en punteros.
- ctypes.memset(dst, c, count)¶
Igual que la función de la biblioteca estándar de C memset C: llena el bloque de memoria en la dirección dst con count bytes de valor c. dst debe ser un número entero que especifique una dirección, o una instancia ctypes.
- ctypes.POINTER(type, /)¶
Create and return a new ctypes pointer type. Pointer types are cached and reused internally, so calling this function repeatedly is cheap. type must be a ctypes type.
- ctypes.pointer(obj, /)¶
Create a new pointer instance, pointing to obj. The returned object is of the type
POINTER(type(obj))
.Nota: Si sólo quieres pasar un puntero a un objeto a una llamada de función foránea, deberías usar
byref(obj)
que es mucho más rápido.
- ctypes.resize(obj, size)¶
Esta función redimensiona el búfer de memoria interna de obj, que debe ser una instancia de tipo ctypes. No es posible hacer el buffer más pequeño que el tamaño nativo del tipo de objetos, como lo indica
size of (type(obj))
, pero es posible agrandar el buffer.
- ctypes.set_errno(value)¶
Poner el valor actual de la copia ctypes-private de la variable del sistema
errno
en el hilo de llamada a valor y retornar el valor anterior.Lanza un auditing event
ctypes.set_errno
con argumentoerrno
.
- ctypes.set_last_error(value)¶
Sets the current value of the ctypes-private copy of the system
LastError
variable in the calling thread to value and return the previous value.Availability: Windows
Lanza un auditing event
ctypes.set_last_error
con argumentoerror
.
- ctypes.sizeof(obj_or_type)¶
Retorna el tamaño en bytes de un buffer de memoria tipo ctypes o instancia. Hace lo mismo que el operador C
sizeof
.
- ctypes.string_at(ptr, size=-1)¶
Return the byte string at void *ptr. If size is specified, it is used as size, otherwise the string is assumed to be zero-terminated.
Raises an auditing event
ctypes.string_at
with argumentsptr
,size
.
- ctypes.WinError(code=None, descr=None)¶
This function is probably the worst-named thing in ctypes. It creates an instance of
OSError
. If code is not specified,GetLastError
is called to determine the error code. If descr is not specified,FormatError()
is called to get a textual description of the error.Availability: Windows
Distinto en la versión 3.3: An instance of
WindowsError
used to be created, which is now an alias ofOSError
.
- ctypes.wstring_at(ptr, size=-1)¶
Return the wide-character string at void *ptr. If size is specified, it is used as the number of characters of the string, otherwise the string is assumed to be zero-terminated.
Raises an auditing event
ctypes.wstring_at
with argumentsptr
,size
.
Tipos de datos¶
- class ctypes._CData¶
Esta clase no pública es la clase de base común de todos los tipos de datos de los ctypes. Entre otras cosas, todas las instancias de tipo ctypes contienen un bloque de memoria que contiene datos compatibles con C; la dirección del bloque de memoria es retornada por la función de ayuda
addressof()
. Otra variable de instancia se expone como_objetos
; ésta contiene otros objetos de Python que deben mantenerse vivos en caso de que el bloque de memoria contenga punteros.Métodos comunes de tipos de datos ctypes, estos son todos métodos de clase (para ser exactos, son métodos del metaclass):
- from_buffer(source[, offset])¶
Este método retorna una instancia ctypes que comparte el buffer del objeto source. El objeto source debe soportar la interfaz del buffer de escritura. El parámetro opcional offset especifica un offset en el buffer de la fuente en bytes; el valor por defecto es cero. Si el buffer de la fuente no es lo suficientemente grande se lanza un
ValueError
.Lanza un auditing event
ctypes.cdata/buffer
con argumentospointer
,size
,offset
.
- from_buffer_copy(source[, offset])¶
Este método crea una instancia ctypes, copiando el buffer del buffer de objetos source que debe ser legible. El parámetro opcional offset especifica un offset en el buffer de origen en bytes; el valor por defecto es cero. Si el buffer de fuente no es lo suficientemente grande se lanza un
ValueError
.Lanza un auditing event
ctypes.cdata/buffer
con argumentospointer
,size
,offset
.
- from_address(address)¶
Este método retorna una instancia de tipo ctypes utilizando la memoria especificada por address que debe ser un entero.
Este método, y otros que indirectamente llaman a este método, lanzan un auditing event
ctypes.cdata
con argumentoaddress
.
- from_param(obj)¶
This method adapts obj to a ctypes type. It is called with the actual object used in a foreign function call when the type is present in the foreign function’s
argtypes
tuple; it must return an object that can be used as a function call parameter.Todos los tipos de datos ctypes tienen una implementación por defecto de este método de clase que normalmente retorna obj si es una instancia del tipo. Algunos tipos aceptan también otros objetos.
- in_dll(library, name)¶
Este método retorna una instancia de tipo ctypes exportada por una biblioteca compartida. name es el nombre del símbolo que exporta los datos, library es la biblioteca compartida cargada.
Variables de instancia común de los tipos de datos de ctypes:
- _b_base_¶
A veces, las instancias de datos ctypes no poseen el bloque de memoria que contienen, sino que comparten parte del bloque de memoria de un objeto base. El miembro de sólo lectura
_b_base_
es el objeto raíz ctypes que posee el bloque de memoria.
- _b_needsfree_¶
Esta variable de sólo lectura es verdadera cuando la instancia de datos ctypes ha sido asignada a el propio bloque de memoria, falsa en caso contrario.
- _objects¶
Este miembro es
None
o un diccionario que contiene objetos de Python que deben mantenerse vivos para que el contenido del bloque de memoria sea válido. Este objeto sólo se expone para su depuración; nunca modifique el contenido de este diccionario.
Tipos de datos fundamentales¶
- class ctypes._SimpleCData¶
Esta clase no pública es la clase base de todos los tipos de datos de ctypes fundamentales. Se menciona aquí porque contiene los atributos comunes de los tipos de datos de ctypes fundamentales.
_SimpleCData
es una subclase de_CData
, por lo que hereda sus métodos y atributos. Los tipos de datos ctypes que no son y no contienen punteros ahora pueden ser archivados.Los instancias tienen un solo atributo:
- value¶
Este atributo contiene el valor real de la instancia. Para los tipos enteros y punteros, es un entero, para los tipos de caracteres, es un objeto o cadena de bytes de un solo carácter, para los tipos de punteros de caracteres es un objeto o cadena de bytes de Python.
Cuando el atributo
value
se recupera de una instancia ctypes, normalmente se retorna un nuevo objeto cada vez.ctypes
no implementa el retorno del objeto original, siempre se construye un nuevo objeto. Lo mismo ocurre con todas las demás instancias de objetos ctypes.
Fundamental data types, when returned as foreign function call results, or, for
example, by retrieving structure field members or array items, are transparently
converted to native Python types. In other words, if a foreign function has a
restype
of c_char_p
, you will always receive a Python bytes
object, not a c_char_p
instance.
Subclasses of fundamental data types do not inherit this behavior. So, if a
foreign functions restype
is a subclass of c_void_p
, you will
receive an instance of this subclass from the function call. Of course, you can
get the value of the pointer by accessing the value
attribute.
Estos son los tipos de datos fundamentales de ctypes:
- class ctypes.c_byte¶
Representa el tipo de datos C signed char e interpreta el valor como un entero pequeño. El constructor acepta un inicializador entero opcional; no se realiza ninguna comprobación de desbordamiento.
- class ctypes.c_char¶
Representa el tipo de datos C char e interpreta el valor como un solo carácter. El constructor acepta un inicializador de cadena opcional, la longitud de la cadena debe ser exactamente un carácter.
- class ctypes.c_char_p¶
Representa el tipo de datos C char* cuando apunta a una cadena terminada en cero. Para un puntero de carácter general que también puede apuntar a datos binarios, se debe usar
POINTER(c_char)
. El constructor acepta una dirección entera o un objeto de bytes.
- class ctypes.c_double¶
Representa el tipo de datos C double. El constructor acepta un inicializador flotante opcional.
- class ctypes.c_longdouble¶
Representa el tipo de datos C long double. El constructor acepta un inicializador flotante opcional. En plataformas donde
sizeof(long double) == sizeof(double)
es un alias dec_double
.
- class ctypes.c_float¶
Representa el tipo de datos C float. El constructor acepta un inicializador flotante opcional.
- class ctypes.c_int¶
Representa el tipo de datos C signed int. El constructor acepta un inicializador entero opcional; no se realiza ninguna comprobación de desbordamiento. En plataformas donde
sizeof(int) == sizeof(long)
es un alias dec_long
.
- class ctypes.c_int8¶
Representa el tipo de datos signed int de C de 8 bits. Por lo general, un alias para
c_byte
.
- class ctypes.c_int16¶
Representa el tipo de datos signed int de C de 16 bits. Por lo general, un alias para
c_short
.
- class ctypes.c_int32¶
Representa el tipo de datos signed int de C de 32 bits. Por lo general, un alias para
c_int
.
- class ctypes.c_int64¶
Representa el tipo de datos signed int de C de 64 bits. Por lo general, un alias para
c_longlong
.
- class ctypes.c_long¶
Representa el tipo de datos C signed long. El constructor acepta un inicializador entero opcional; no se realiza ninguna comprobación de desbordamiento.
- class ctypes.c_longlong¶
Representa el tipo de datos C signed long long. El constructor acepta un inicializador entero opcional; no se realiza ninguna comprobación de desbordamiento.
- class ctypes.c_short¶
Representa el tipo de datos C signed short. El constructor acepta un inicializador entero opcional; no se realiza ninguna comprobación de desbordamiento.
- class ctypes.c_size_t¶
Representa el tipo de datos C
size_t
.
- class ctypes.c_ssize_t¶
Representa el tipo de datos C
ssize_t
.Added in version 3.2.
- class ctypes.c_time_t¶
Represents the C
time_t
datatype.Added in version 3.12.
- class ctypes.c_ubyte¶
Representa el tipo de datos C unsigned char, interpreta el valor como un entero pequeño. El constructor acepta un inicializador entero opcional; no se realiza ninguna comprobación de desbordamiento.
- class ctypes.c_uint¶
Representa el tipo de datos C unsigned int. El constructor acepta un inicializador entero opcional; no se realiza ninguna comprobación de desbordamiento. En plataformas donde
sizeof(int) == sizeof(long)
es un alias parac_ulong
.
- class ctypes.c_uint8¶
Representa el tipo de datos unsigned int de C de 8 bits. Por lo general, un alias para
c_ubyte
.
- class ctypes.c_uint16¶
Representa el tipo de datos unsigned int de C de 16 bits. Por lo general, un alias para
c_ushort
.
- class ctypes.c_uint32¶
Representa el tipo de datos unsigned int de C de 32 bits. Por lo general, un alias para
c_uint
.
- class ctypes.c_uint64¶
Representa el tipo de datos unsigned int de C de 64 bits. Por lo general, un alias para
c_ulonglong
.
- class ctypes.c_ulong¶
Representa el tipo de datos C unsigned long. El constructor acepta un inicializador entero opcional; no se realiza ninguna comprobación de desbordamiento.
- class ctypes.c_ulonglong¶
Representa el tipo de datos C unsigned long long. El constructor acepta un inicializador entero opcional; no se realiza ninguna comprobación de desbordamiento.
- class ctypes.c_ushort¶
Representa el tipo de datos C unsigned short. El constructor acepta un inicializador entero opcional; no se realiza ninguna comprobación de desbordamiento.
- class ctypes.c_void_p¶
Representa el tipo C void*. El valor se representa como un número entero. El constructor acepta un inicializador entero opcional.
- class ctypes.c_wchar¶
Represents the C
wchar_t
datatype, and interprets the value as a single character unicode string. The constructor accepts an optional string initializer, the length of the string must be exactly one character.
- class ctypes.c_wchar_p¶
Representa el tipo de datos C wchar_t*, que debe ser un puntero a una cadena de caracteres anchos terminada en cero. El constructor acepta una dirección entera o una cadena.
- class ctypes.c_bool¶
Representa el tipo de dato C bool (más precisamente, _Bool de C99). Su valor puede ser
True
oFalse
, y el constructor acepta cualquier objeto que tenga un valor de verdad.
- class ctypes.HRESULT¶
Represents a
HRESULT
value, which contains success or error information for a function or method call.Availability: Windows
- class ctypes.py_object¶
Representa el tipo de dato de C PyObject*. Llamar esto sin un argumento crea un puntero PyObject*
NULL
.
The ctypes.wintypes
module provides quite some other Windows specific
data types, for example HWND
, WPARAM
, or DWORD
.
Some useful structures like MSG
or RECT
are also defined.
Tipos de datos estructurados¶
- class ctypes.Union(*args, **kw)¶
Clase base abstracta para uniones en orden de bytes nativos.
- class ctypes.BigEndianUnion(*args, **kw)¶
Clase base abstracta para uniones en orden de bytes big endian.
Added in version 3.11.
- class ctypes.LittleEndianUnion(*args, **kw)¶
Clase base abstracta para uniones en orden de bytes little endian.
Added in version 3.11.
- class ctypes.BigEndianStructure(*args, **kw)¶
Clase base abstracta para estructuras en orden de bytes big endian.
- class ctypes.LittleEndianStructure(*args, **kw)¶
Clase base abstracta para estructuras en orden de bytes little endian.
Las estructuras y uniones con un orden de bytes no nativo no pueden contener campos de tipo puntero ni ningún otro tipo de datos que contenga campos de tipo puntero.
- class ctypes.Structure(*args, **kw)¶
Clase base abstracta para estructuras en orden de bytes native.
La estructura concreta y los tipos de unión deben crearse subclasificando uno de estos tipos, y al menos definir una variable de clase
_fields_
.ctypes
creará descriptors que permitan leer y escribir los campos por accesos directos de atributos. Estos son los- _fields_¶
Una secuencia que define los campos de estructura. Los elementos deben ser de 2 o 3 tuplas. El primer ítem es el nombre del campo, el segundo ítem especifica el tipo de campo; puede ser cualquier tipo de datos ctypes.
Para los campos de tipo entero como
c_int
, se puede dar un tercer elemento opcional. Debe ser un pequeño entero positivo que defina el ancho de bit del campo.Los nombres de los campos deben ser únicos dentro de una estructura o unión. Esto no se comprueba, sólo se puede acceder a un campo cuando los nombres se repiten.
Es posible definir la variable de clase
_fields_
después de la sentencia de clase que define la subclase Estructura, esto permite crear tipos de datos que se refieren directa o indirectamente a sí mismos:class List(Structure): pass List._fields_ = [("pnext", POINTER(List)), ... ]
Sin embargo, la variable de clase
_fields_
debe ser definida antes de que el tipo sea usado por primera vez (se crea una instancia, se llama asizeof()
, y así sucesivamente). Las asignaciones posteriores a la variable de clase_fields_
lanzarán un AttributeError.Es posible definir subclases de tipos de estructura, que heredan los campos de la clase base más el
_fields_
definido en la subclase, si existe.
- _pack_¶
An optional small integer that allows overriding the alignment of structure fields in the instance.
_pack_
must already be defined when_fields_
is assigned, otherwise it will have no effect. Setting this attribute to 0 is the same as not setting it at all.
- _anonymous_¶
Una secuencia opcional que enumera los nombres de los campos sin nombre (anónimos).
_anonymous_
debe estar ya definida cuando se asigna_fields_
, de lo contrario no tendrá ningún efecto.Los campos listados en esta variable deben ser campos de tipo estructura o unión.
ctypes
creará descriptores en el tipo de estructura que permitan acceder a los campos anidados directamente, sin necesidad de crear el campo de estructura o unión.Aquí hay un tipo de ejemplo (Windows):
class _U(Union): _fields_ = [("lptdesc", POINTER(TYPEDESC)), ("lpadesc", POINTER(ARRAYDESC)), ("hreftype", HREFTYPE)] class TYPEDESC(Structure): _anonymous_ = ("u",) _fields_ = [("u", _U), ("vt", VARTYPE)]
La estructura
TYPEDESC
describe un tipo de datos COM, el campovt
especifica cuál de los campos de unión es válido. Como el campou
está definido como campo anónimo, ahora es posible acceder a los miembros directamente desde la instancia TYPEDESC.td.lptdesc
ytd.u.lptdesc
son equivalentes, pero el primero es más rápido ya que no necesita crear una instancia de unión temporal:td = TYPEDESC() td.vt = VT_PTR td.lptdesc = POINTER(some_type) td.u.lptdesc = POINTER(some_type)
Es posible definir subclases de estructuras, que heredan los campos de la clase base. Si la definición de la subclase tiene una variable
_fields_
separada, los campos especificados en ella se añaden a los campos de la clase base.Los constructores de estructuras y uniones aceptan tanto argumentos posicionales como de palabras clave. Los argumentos posicionales se usan para inicializar los campos de los miembros en el mismo orden en que aparecen en
_fields_
. Los argumentos de palabras clave en el constructor se interpretan como asignaciones de atributos, por lo que inicializarán_fields_
con el mismo nombre, o crearán nuevos atributos para nombres no presentes en_fields_
.
Arreglos y punteros¶
- class ctypes.Array(*args)¶
Clase base abstracta para arreglos.
La forma recomendada de crear tipos de arreglos concretos es multiplicando cualquier tipo de datos
ctypes
con un número entero no negativo. Como alternativa, puede subclasificar este tipo y definir variables de clase_length_
y_type_
. Los elementos del arreglo se pueden leer y escribir utilizando subíndices estándar y accesos de segmento; para lecturas de segmentos, el objeto resultante no es en sí mismo, unArray
.- _length_¶
Un número entero positivo que especifica el número de elementos del conjunto. Los subíndices fuera de rango dan como resultado un
IndexError
. Será retornado porlen()
.
- _type_¶
Especifica el tipo de cada elemento del arreglo.
Los constructores de subclases de arreglos aceptan argumentos posicionales, usados para inicializar los elementos en orden.
- class ctypes._Pointer¶
Clase base, privada y abstracta para punteros.
Los tipos de punteros concretos se crean llamando a
POINTER()
con el tipo que será apuntado; esto se hace automáticamente porpointer()
.Si un puntero apunta a un arreglo, sus elementos pueden ser leídos y escritos usando accesos de subíndices y cortes estándar. Los objetos punteros no tienen tamaño, así que
len()
lanzará unTypeError
. Los subíndices negativos se leerán de la memoria antes que el puntero (como en C), y los subíndices fuera de rango probablemente se bloqueen con una violación de acceso (si tienes suerte).- _type_¶
Especifica el tipo apuntado.
- contents¶
Retorna el objeto al que el puntero apunta. Asignando a este atributo cambia el puntero para que apunte al objeto asignado.