ctypes — Una biblioteca de funciones foráneas para Python


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

Note: The code samples in this tutorial use doctest to make sure that they actually work. Since some code samples behave differently under Linux, Windows, or macOS, they contain doctest directives in comments.

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.

Acceder a las funciones de los dll cargados

Las funciones se acceden como atributos de los objetos dll:

>>> from ctypes import *
>>> 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
>>>

Nótese que las dlls del sistema win32 como kernel32 y user32 a menudo exportan versiones ANSI y UNICODE de una función. La versión UNICODE se exporta con una W añadida al nombre, mientras que la versión ANSI se exporta con una A añadida al nombre. La función GetModuleHandle de win32, que retorna un manejador de módulo para un nombre de módulo dado, tiene el siguiente prototipo de C, y se usa una macro para exponer uno de ellos como GetModuleHandle dependiendo de si UNICODE está definido o no:

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

Puedes llamar a estas funciones como cualquier otra función en Python. Este ejemplo utiliza la función time(), que retorna el tiempo del sistema en segundos desde la época de Unix, y la función GetModuleHandleA(), que retorna un manejador de módulo de win32.

Este ejemplo llama a ambas funciones con un puntero NULL (None debe ser usado como el puntero NULL):

>>> print(libc.time(None))  
1150640792
>>> 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).

Los objetos None, enteros, bytes y cadenas (unicode) son los únicos objetos nativos de Python que pueden ser usados directamente como parámetros en estas llamadas a funciones. None se pasa como puntero de C NULL, los objetos bytes y las cadenas se pasan como puntero al bloque de memoria que contiene sus datos (char* o wchar_t*). Los enteros de Python se pasan como por defecto en la plataforma como tipo int de C, su valor se enmascara para que encuadre 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

c_bool

_Bool

bool (1)

c_char

char

Un objeto bytes de 1-caracter

c_wchar

wchar_t

Una cadena de 1-caracter

c_byte

char

entero

c_ubyte

unsigned char

entero

c_short

short

entero

c_ushort

unsigned short

entero

c_int

int

entero

c_uint

unsigned int

entero

c_long

long

entero

c_ulong

unsigned long

entero

c_longlong

__int64 o long long

entero

c_ulonglong

unsigned __int64 o unsigned long long

entero

c_size_t

size_t

entero

c_ssize_t

ssize_t o Py_ssize_t

entero

c_float

float

flotante

c_double

double

flotante

c_longdouble

long double

flotante

c_char_p

char* (NUL terminated)

objecto de bytes o None

c_wchar_p

wchar_t* (NUL terminated)

cadena o None

c_void_p

void*

entero o None

  1. 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'
>>>

La función create_string_buffer() reemplaza a la función c_buffer() (que todavía está disponible como un alias), así como a la función c_string() de versiones anteriores de ctypes. Para crear un bloque de memoria mutable que contenga caracteres unicode del tipo C wchar_t utilice la función create_unicode_buffer().

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: exceptions.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
>>>

Funciones de llamada con sus propios tipos de datos personalizados

También puedes personalizar la conversión de argumentos de ctypes para permitir que las instancias de tus propias clases se usen como argumentos de función. ctypes busca un atributo _as_parameter_ y lo usa como argumento de función. Por supuesto, debe ser uno de entero, cadena o bytes:

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

Si no quieres almacenar los datos de la instancia en la variable de instancia _as_parameter_, puedes definir una property que haga que el atributo esté disponible a petición.

Especificar los tipos de argumentos requeridos (prototipos de funciones)

Es posible especificar los tipos de argumentos necesarios de las funciones exportadas desde las DLL estableciendo el atributo argtypes.

argtypes debe ser una secuencia de tipos de datos de C (la función printf probablemente no es un buen ejemplo aquí, porque toma un número variable y diferentes tipos de parámetros dependiendo del formato de la cadena, por otro lado esto es bastante útil para experimentar con esta característica):

>>> 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: exceptions.TypeError: wrong type
>>> printf(b"%s %d %f\n", b"X", 2, 3)
X 2 3.000000
13
>>>

Si has definido tus propias clases las cuales pasas a las llamadas a funciones, tienes que implementar un método de clase from_param() para que puedan ser usadas en la secuencia argtypes. El método de clase from_param() recibe el objeto Python que se le pasa a la llamada a función, debería hacer una comprobación de tipo o lo que sea necesario para asegurarse de que este objeto es aceptable, y luego retornar el objeto en sí, su atributo _as_parameter_, o lo que se quiera pasar como argumento de la función C en este caso. De nuevo, el resultado debe ser un entero, una cadena, unos bytes, una instancia ctypes, o un objeto con el atributo _as_parameter_.

Tipos de retorno

Por defecto, se supone que las funciones retornan el tipo C int. Se pueden especificar otros tipos de retorno estableciendo el atributo restype del objeto de la función.

Aquí hay un ejemplo más avanzado, utiliza la función strchr, que espera un puntero de cadena y un carácter, y retorna un puntero a una cadena:

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

Si quieres evitar las llamadas ord("x") de arriba, puedes establecer el atributo argtypes, y el segundo argumento se convertirá de un objeto de un solo carácter de bytes de Python a un char:

>>> strchr.restype = c_char_p
>>> strchr.argtypes = [c_char_p, c_char]
>>> strchr(b"abcdef", b"d")
'def'
>>> strchr(b"abcdef", b"def")
Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
ArgumentError: argument 2: exceptions.TypeError: one character string expected
>>> print(strchr(b"abcdef", b"x"))
None
>>> strchr(b"abcdef", b"d")
'def'
>>>

También puedes usar un objeto Python invocable (una función o una clase, por ejemplo) como el atributo restype, si la función foránea retorna un número entero. El objeto invocable será llamado con el entero que la función C retorna, y el resultado de esta llamada será utilizado como resultado de la llamada a la función. Esto es útil para comprobar si hay valores de retorno de error y plantear automáticamente una excepción:

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

Tenga en cuenta que un mecanismo de comprobación de errores mucho más potente está disponible a través del atributo errcheck; consulte el manual de referencia para obtener más detalles.

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

Las estructuras y uniones deben derivar de las clases base Structure y Union que se definen en el módulo ctypes. Cada subclase debe definir un atributo _fields_. _fields_ debe ser una lista de 2-tuplas, que contenga un nombre de campo y un tipo de campo.

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

Por defecto, los campos de Estructura y Unión están alineados de la misma manera que lo hace el compilador C. Es posible anular este comportamiento especificando un atributo de clase _pack_ en la definición de la subclase. Este debe ser establecido como un entero positivo y especifica la alineación máxima de los campos. Esto es lo que #pragma pack(n) también hace en 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

Es posible crear estructuras y uniones que contengan campos de bits. Los campos de bits sólo son posibles para campos enteros, el ancho de bit se especifica como el tercer ítem en las tuplas _fields_:

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

Por lo general, los ctypes hacen un control estricto de los tipos. Esto significa que si tienes POINTER(c_int) en la lista argtypes de una función o como el tipo de un campo miembro en una definición de estructura, sólo se aceptan instancias exactamente del mismo tipo. Hay algunas excepciones a esta regla, en las que ctypes acepta otros objetos. Por ejemplo, se pueden pasar instancias de arreglo compatibles en lugar de tipos de puntero. Así, para POINTER(c_int), ctypes acepta un arreglo de 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
>>>

Además, si se declara explícitamente que un argumento de función es de tipo puntero (como POINTER(c_int)) en argtypes, se puede pasar un objeto de tipo puntero (c_int en este caso) a la función. ctypes aplicará la conversión byref() requerida en este caso automáticamente.

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

porque la nueva class cell no está disponible en la propia declaración de clase. En ctypes, podemos definir la clase cell y establecer el atributo _fields_ más tarde, después de la declaración de clase:

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

Presentaré un ejemplo aquí que utiliza la función qsort() de la biblioteca estándar de C, que se utiliza para ordenar los elementos con la ayuda de una función de retrollamada. qsort() se utilizará para ordenar un conjunto de números enteros:

>>> IntArray5 = c_int * 5
>>> ia = IntArray5(5, 1, 7, 33, 99)
>>> qsort = libc.qsort
>>> qsort.restype = None
>>>

qsort() debe ser llamada con un puntero a los datos a ordenar, el número de elementos en el array de datos, el tamaño de un elemento, y un puntero a la función de comparación, la llamada de retorno. La llamada de retorno se llamará entonces con dos punteros a los ítems, y debe retornar un entero negativo si el primer ítem es más pequeño que el segundo, un cero si son iguales, y un entero positivo en caso contrario.

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

Algunas bibliotecas compartidas no sólo exportan funciones, sino también variables. Un ejemplo en la propia biblioteca de Python es el Py_OptimizeFlag, un entero establecido en 0, 1, o 2, dependiendo del flag -O o -OO dado en el inicio.

ctypes puede acceder a valores como este con los métodos de la clase in_dll() del tipo. pythonapi es un símbolo predefinido que da acceso a la API de Python C:

>>> opt_flag = c_int.in_dll(pythonapi, "Py_OptimizeFlag")
>>> print(opt_flag)
c_long(0)
>>>

Si el intérprete se hubiera iniciado con -O, el ejemplo habría impreso c_long(1), o c_long(2) si -OO se hubiera especificado.

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 está inicializado para apuntar a un arreglo de registros struct _frozen, terminada por uno cuyos miembros son todos NULL o cero. Cuando se importa un módulo congelado, se busca en esta tabla. El código de terceros podría jugar con esto para proporcionar una colección creada dinámicamente de módulos congelados.

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

Hemos definido el tipo de datos struct _frozen, para que podamos obtener el puntero de la tabla:

>>> FrozenTable = POINTER(struct_frozen)
>>> table = FrozenTable.in_dll(pythonapi, "PyImport_FrozenModules")
>>>

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
__hello__ 161
__phello__ -161
__phello__.spam 161
>>>

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

Encontrar bibliotecas compartidas

Cuando se programa en un lenguaje compilado, se accede a las bibliotecas compartidas cuando se compila/enlaza un programa, y cuándo se ejecuta el programa.

El propósito de la función find_library() es localizar una biblioteca de forma similar a lo que hace el compilador o el cargador en tiempo de ejecución (en plataformas con varias versiones de una biblioteca compartida se debería cargar la más reciente), mientras que los cargadores de bibliotecas ctypes actúan como cuando se ejecuta un programa, y llaman directamente al cargador en tiempo de ejecución.

El módulo ctypes.util proporciona una función que puede ayudar a determinar la biblioteca a cargar.

ctypes.util.find_library(name)

Intenta encontrar una biblioteca y retornar un nombre. 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, retorna None.

La funcionalidad exacta depende del sistema.

En Linux, find_library() intenta ejecutar programas externos (/sbin/ldconfig, gcc, objdump y ld) para encontrar el archivo de la biblioteca. retorna el nombre del archivo de la biblioteca.

Distinto en la versión 3.6: En Linux, el valor de la variable de entorno LD_LIBRARY_PATH se utiliza cuando se buscan bibliotecas, si una biblioteca no puede ser encontrada por ningún otro medio.

Aquí hay algunos ejemplos:

>>> from ctypes.util import find_library
>>> find_library("m")
'libm.so.6'
>>> find_library("c")
'libc.so.6'
>>> find_library("bz2")
'libbz2.so.1.0'
>>>

On macOS, find_library() tries several predefined naming schemes and paths to locate the library, and returns a full pathname if successful:

>>> from ctypes.util import find_library
>>> find_library("c")
'/usr/lib/libc.dylib'
>>> find_library("m")
'/usr/lib/libm.dylib'
>>> find_library("bz2")
'/usr/lib/libbz2.dylib'
>>> find_library("AGL")
'/System/Library/Frameworks/AGL.framework/AGL'
>>>

En Windows, find_library`() busca a lo largo de la ruta de búsqueda del sistema, y retorna la ruta completa, pero como no hay un esquema de nombres predefinido, una llamada como find_library("c") fallará y retornará None.

Si envolvemos una biblioteca compartida con ctypes, puede ser mejor determinar el nombre de la biblioteca compartida en tiempo de desarrollo, y codificarlo en el módulo de envoltura en lugar de usar find_library() para localizar la biblioteca en tiempo de ejecución.

Cargando bibliotecas compartidas

Hay varias maneras de cargar las bibliotecas compartidas en el proceso Python. Una forma es instanciar una de las siguientes clases:

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

Las instancias de esta clase representan bibliotecas compartidas cargadas. Las funciones de estas bibliotecas usan la convención estándar de llamada C, y se asume que retornan int.

En Windows, la creación de una instancia CDLL puede fallar incluso si existe el nombre de la DLL. Cuando no se encuentra una DLL dependiente de la DLL cargada, se lanza un error OSError con el mensaje «[WinError 126] No se pudo encontrar el módulo especificado». Este mensaje de error no contiene el nombre de DLL que falta porque la API de Windows no devuelve esta información, lo que dificulta el diagnóstico de este error. Para resolver este error y determinar qué DLL no se encuentra, debe buscar la lista de DLL dependientes y determinar cuál no se encuentra utilizando las herramientas de depuración y seguimiento de Windows.

Ver también

Herramienta Microsoft DUMPBIN – Una herramienta para encontrar dependientes de DLL.

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

Sólo Windows: Las instancias de esta clase representan bibliotecas compartidas cargadas, las funciones en estas bibliotecas usan la convención de llamada stdcall, y se asume que retornan el código específico de windows HRESULT`. Los valores HRESULT` contienen información que especifica si la llamada a la función falló o tuvo éxito, junto con un código de error adicional. Si el valor de retorno señala un fracaso, se eleva automáticamente un OSError`.

Distinto en la versión 3.3: WindowsError solía ser lanzado.

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

Sólo Windows: Las instancias de esta clase representan bibliotecas compartidas cargadas, las funciones de estas bibliotecas usan la convención de llamada stdcall, y se supone que retornan int por defecto.

En Windows CE sólo se utiliza la convención de llamadas estándar, para mayor comodidad las WinDLL y OleDLL utilizan la convención de llamadas estándar en esta plataforma.

El termino Python global interpreter lock es lanzado antes de llamar a cualquier función exportada por estas librerías, y se requiere después.

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

Las instancias de esta clase se comportan como instancias CDLL , excepto que el GIL de Python es no liberado durante la llamada a la función, y después de la ejecución de la función se comprueba si esta activo el flag de error de Python. Si el flag de error esta activado, se lanza una excepción Python.

Por lo tanto, esto sólo es útil para llamar directamente a las funciones api C de Python.

Todas estas clases pueden ser instanciadas llamándolas con al menos un argumento, la ruta de la biblioteca compartida. Si tienes un manejador existente de una biblioteca compartida ya cargada, se puede pasar como el parámetro llamado handle, de lo contrario la función dlopen o LoadLibrary de la plataforma subyacente es utilizada para cargar la biblioteca en el proceso, y obtener un manejador de la misma.

El parámetro mode puede utilizarse para especificar cómo se carga la biblioteca. Para más detalles, consulte la página dlopen(3) del manual. En Windows, mode es ignorado. En los sistemas posix, RTLD_NOW siempre se agrega, y no es configurable.

El parámetro use_errno, cuando se establece en true, habilita un mecanismo ctypes que permite acceder al número de error del sistema errno de forma segura. ctypes mantiene una copia local del hilo de la variable del sistema errno; si llamas a funciones extranjeras creadas con use_errno=True entonces el valor errno antes de la llamada a la función se intercambia con la copia privada de ctypes, lo mismo ocurre inmediatamente después de la llamada a la función.

La función ctypes.get_errno() retorna el valor de la copia privada de ctypes, y la función ctypes.set_errno() cambia la copia privada de ctypes a un nuevo valor y retorna el valor anterior.

El parámetro use_last_error, cuando se establece en true, habilita el mismo mecanismo para el código de error de Windows que es administrado por las funciones de la API de Windows GetLastError() y SetLastError(); ctypes.get_last_error() y ctypes.set_last_error() se utilizan para solicitar y cambiar la copia privada ctypes del código de error de Windows.

El parámetro winmode se utiliza en Windows para especificar cómo se carga la biblioteca (ya que mode se ignora). Toma cualquier valor que sea válido para el parámetro flags de la API de Win32 LoadLibraryEx. Cuando se omite, el valor por defecto es usar los flags que resultan en la carga de DLL más segura para evitar problemas como el secuestro de DLL. Pasar la ruta completa a la DLL es la forma más segura de asegurar que se cargan la biblioteca y las dependencias correctas.

Distinto en la versión 3.8: Añadido el parámetro winmode.

ctypes.RTLD_GLOBAL

Flag para usar como parámetro modo. En las plataformas en las que esta bandera no está disponible, se define como el cero entero.

ctypes.RTLD_LOCAL

Flag para usar como parámetro modo. En las plataformas en las que esto no está disponible, es lo mismo que RTLD_GLOBAL.

ctypes.DEFAULT_MODE

El modo por defecto que se utiliza para cargar las bibliotecas compartidas. En OSX 10.3, esto es RTLD_GLOBAL, de lo contrario es lo mismo que RTLD_LOCAL.

Las instancias de estas clases no tienen métodos públicos. Se puede acceder a las funciones exportadas por la biblioteca compartida como atributos o por índice. Tenga en cuenta que al acceder a la función a través de un atributo se almacena en caché el resultado y, por lo tanto, al acceder a él repetidamente se retorna el mismo objeto cada vez. Por otro lado, acceder a ella a través de un índice retorna un nuevo objeto cada vez:

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

Los siguientes atributos públicos están disponibles, su nombre comienza con un guión bajo para no chocar con los nombres de las funciones exportadas:

PyDLL._handle

El manejador del sistema usado para acceder a la biblioteca.

PyDLL._name

El nombre de la biblioteca pasado en el constructor.

Las bibliotecas compartidas también pueden ser cargadas usando uno de los objetos prefabricados, que son instancias de la clase LibraryLoader, ya sea llamando al método LoadLibrary(), o recuperando la biblioteca como atributo de la instancia de carga.

class ctypes.LibraryLoader(dlltype)

Clase que carga bibliotecas compartidas. dlltype debe ser uno de los tipos CDLL, PyDLL, WinDLL, o OleDLL.

__getattr__() tiene un comportamiento especial: Permite cargar una biblioteca compartida accediendo a ella como atributo de una instancia de carga de biblioteca. El resultado se almacena en caché, de modo que los accesos repetidos a los atributos retornan la misma biblioteca cada vez.

LoadLibrary(name)

Carga una biblioteca compartida en el proceso y la retorna. Este método siempre retorna una nueva instancia de la biblioteca.

Estos cargadores prefabricados de bibliotecas están disponibles:

ctypes.cdll

Crea instancias de CDLL.

ctypes.windll

Sólo Windows: Crea instancias de WinDLL.

ctypes.oledll

Sólo Windows: Crea instancias de OleDLL.

ctypes.pydll

Crea instancias de PyDLL.

Para acceder directamente a la API C de Python, se dispone de un objeto de biblioteca compartida de Python listo-para-usar:

ctypes.pythonapi

Una instancia de PyDLL que expone las funciones de la API C de Python como atributos. Ten en cuenta que se supone que todas estas funciones retornan C int, lo que por supuesto no siempre es cierto, así que tienes que asignar el atributo correcto restype para usar estas funciones.

Lanza un auditing event ctypes.dlopen con argumento name.

Al acceder a una función en una biblioteca cargada se lanza un evento de auditoría ctypes.dlsym con argumentos library (el objeto de la biblioteca) y name (el nombre del símbolo como cadena o entero).

En los casos en los que sólo está disponible el manejador de la biblioteca en lugar del objeto, al acceder a una función se produce un evento de auditoría ctypes.dlsym/handle con los argumentos handle (el manejador de la biblioteca en bruto) y name.

Funciones foráneas

Como se explicó en la sección anterior, se puede acceder a las funciones foráneas como atributos de las bibliotecas compartidas cargadas. Los objetos de función creados de esta forma aceptan por defecto cualquier número de argumentos, aceptan cualquier instancia de datos ctypes como argumentos y retornan el tipo de resultado por defecto especificado por el cargador de la biblioteca. Son instancias de una clase privada:

class ctypes._FuncPtr

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 de ctypes para especificar el tipo de resultado de la función externa. Usa None para void, una función que no retorna nada.

Es posible asignar un objeto Python invocable que no sea de tipo ctypes, en este caso se supone que la función retorna un C int, y el invocable se llamará con este entero, lo que permite un posterior procesamiento o comprobación de errores. El uso de esto está obsoleto, para un postprocesamiento más flexible o para la comprobación de errores utilice un tipo de datos ctypes como restype y asigne un invocable al atributo errcheck.

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.

Cuando se llama a una función foránea, cada argumento real se pasa al método de la clase from_param() de los elementos de la tupla argtypes, este método permite adaptar el argumento real a un objeto que la función externa acepta. Por ejemplo, un elemento c_char_p de la tupla argtypes convertirá una cadena pasada como argumento en un objeto de bytes utilizando reglas de conversión ctypes.

Nuevo: Ahora es posible poner en argtypes elementos que no son de tipo ctypes, pero cada elemento debe tener un método from_param() que retorne un valor utilizable como argumento (entero, cadena, instancia ctypes). Esto permite definir adaptadores que pueden adaptar objetos personalizados como parámetros de la función.

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 es lo que retorna la función externa, como se especifica en el atributo restype.

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.seh_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 real errno 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)

Sólo Windows: El prototipo de función retornado crea funciones que usan la convención de llamada stdcall, excepto en Windows CE donde WINFUNCTYPE() es lo mismo que CFUNCTYPE(). La función lanzará el GIL durante la llamada. use_errno y use_last_error tienen el mismo significado que arriba.

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.

Los métodos COM usan una convención especial de llamadas: Requieren un puntero a la interfaz COM como primer argumento, además de los parámetros que se especifican en la tupla argtypes.

El parámetro opcional paramflags crea envoltorios de funciones foráneas con mucha más funcionalidad que las características descritas anteriormente.

paramflags deben ser una tupla de la misma longitud que 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.

Este ejemplo demuestra cómo envolver la función MessageBoxW de Windows para que soporte los parámetros por defecto y los argumentos con nombre. La declaración C del archivo de cabecera de Windows es esta:

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.

Los parámetros de salida pueden combinarse con el protocolo errcheck para hacer un mayor procesamiento de la salida y la comprobación de errores. La función api de win32 GetWindowRect retorna un BOOL para señalar el éxito o el fracaso, por lo que esta función podría hacer la comprobación de errores, y plantea una excepción cuando la llamada api ha fallado:

>>> def errcheck(result, func, args):
...     if not result:
...         raise WinError()
...     return args
...
>>> GetWindowRect.errcheck = errcheck
>>>

Si la función errcheck retorna la tupla de argumentos que recibe sin cambios, ctypes continúa el procesamiento normal que hace en los parámetros de salida. Si quieres retornar una tupla de coordenadas de ventana en lugar de una instancia RECT, puedes recuperar los campos de la función y retornarlos en su lugar, el procesamiento normal ya no tendrá lugar:

>>> 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 argumento obj.

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 argumentos init, 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 argumentos init, size.

ctypes.DllCanUnloadNow()

Sólo Windows: Esta función es un gancho que permite implementar servidores COM en proceso con ctypes. Se llama desde la función DllCanUnloadNow que la extensión _ctypes dll exporta.

ctypes.DllGetClassObject()

Sólo Windows: Esta función es un gancho que permite implementar servidores COM en proceso con ctypes. Se llama desde la función DllGetClassObject que la extensión _ctypes exporta.

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, retorna None.

La funcionalidad exacta depende del sistema.

ctypes.util.find_msvcrt()

Sólo Windows: retorna el nombre de archivo de la biblioteca de tiempo de ejecución de VC usada por Python, y por los módulos de extensión. Si no se puede determinar el nombre de la biblioteca, se retorna None.

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.

ctypes.FormatError([code])

Sólo Windows: retorna una descripción textual del código de error code. Si no se especifica ningún código de error, se utiliza el último código de error llamando a la función de api de Windows GetLastError.

ctypes.GetLastError()

Sólo Windows: retorna el último código de error establecido por Windows en el hilo de llamada. Esta función llama directamente a la función GetLastError() de Windows, no retorna la copia ctypes-private del código de error.

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

Sólo Windows: retorna el valor actual de la copia ctypes-private de la variable de sistema LastError en el hilo de llamada.

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)

Esta función de fábrica crea y retorna un nuevo tipo de puntero ctypes. Los tipos de puntero se almacenan en caché y se reutilizan internamente, por lo que llamar a esta función repetidamente es barato. type debe ser un tipo ctypes.

ctypes.pointer(obj)

Esta función crea una nueva instancia de puntero, apuntando a obj. El objeto retornado es del tipo POINTER(tipo(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 argumento errno.

ctypes.set_last_error(value)

Sólo para Windows: pone el valor actual de la copia ctypes-private de la variable del sistema LastError en el hilo de llamada a valor y retorna el valor anterior.

Lanza un auditing event ctypes.set_last_error con argumento error.

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(address, size=- 1)

Esta función retorna la cadena C que comienza en la dirección de memoria address como un objeto de bytes. Si se especifica el tamaño, se utiliza como tamaño, de lo contrario se asume que la cadena tiene un final cero.

Lanza un auditing event ctypes.string_at con argumentos address, size.

ctypes.WinError(code=None, descr=None)

Sólo para Windows: esta función es probablemente la cosa peor nombrada de los ctypes. Crea una instancia de OSError. Si no se especifica el code, se llama a GetLastError para determinar el código de error. Si no se especifica descr, se llama a FormatError`() para obtener una descripción textual del error.

Distinto en la versión 3.3: Una instancia de WindowsError solía ser creada.

ctypes.wstring_at(address, size=- 1)

Esta función retorna la cadena de caracteres anchos que comienza en la dirección de memoria address como una cadena. Si se especifica size, se utiliza como el número de caracteres de la cadena, de lo contrario se asume que la cadena tiene un final cero.

Lanza un auditing event ctypes.wstring_at con argumentos address, 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 argumentos pointer, 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 argumentos pointer, 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.

Lanza un auditing event ctypes.cdata con argumento address.

from_param(obj)

Este método adapta el obj a un tipo de ctypes. Se llama con el objeto real usado en una llamada a una función externa cuando el tipo está presente en la tupla argtypes de la función foránea; debe retornar un objeto que pueda ser usado como parámetro de llamada a la función.

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.

Los tipos de datos fundamentales, cuando se retornan como resultados de llamadas de funciones foráneas, o, por ejemplo, al recuperar miembros de campo de estructura o elementos de arreglos, se convierten de forma transparente a tipos nativos de Python. En otras palabras, si una función externa tiene un restype de c_char_p, siempre recibirá un objeto de bytes Python, no una instancia de c_char_p.

Las subclases de los tipos de datos fundamentales no heredan este comportamiento. Así, si una función externa restype es una subclase de c_void_p, recibirás una instancia de esta subclase desde la llamada a la función. Por supuesto, puedes obtener el valor del puntero accediendo al atributo value.

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 de entero opcional; no se hace 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 las plataformas donde sizeof(long double) == sizeof(double) es un alias de c_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 hace ninguna comprobación de desbordamiento. En plataformas donde sizeof(int) == sizeof(long) es un alias de c_long.

class ctypes.c_int8

Representa el tipo de datos C 8-bit signed int. Normalmente un alias para c_byte.

class ctypes.c_int16

Representa el tipo de datos C 16-bit signed int. Normalmente un alias para c_short.

class ctypes.c_int32

Representa el tipo de datos C 32-bit signed int. Normalmente un alias para c_int.

class ctypes.c_int64

Representa el tipo de datos C 64-bit signed int. Normalmente 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 hace 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 hace 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 hace 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.

Nuevo en la versión 3.2.

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 hace 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 hace ninguna comprobación de desbordamiento. En plataformas donde sizeof(int) == sizeof(long) es un alias para c_ulong.

class ctypes.c_uint8

Representa el tipo de datos C 8-bit unsigned int. Normalmente un alias para c_ubyte.

class ctypes.c_uint16

Representa el tipo de datos C 16-bit unsigned int. Normalmente un alias para c_ushort.

class ctypes.c_uint32

Representa el tipo de datos C 32-bit unsigned int. Normalmente un alias para c_uint.

class ctypes.c_uint64

Representa el tipo de datos C 64-bit unsigned int. Normalmente 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 hace 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 hace 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 hace ninguna comprobación de desbordamiento.

class ctypes.c_void_p

Representa el tipo C void*. El valor se representa como un entero. El constructor acepta un inicializador entero opcional.

class ctypes.c_wchar

Representa el tipo de datos C wchar_t, e interpreta el valor como una cadena unicode de un solo carácter. El constructor acepta un inicializador de cadena opcional, la longitud de la cadena debe ser exactamente de un carácter.

class ctypes.c_wchar_p

Representa el tipo de datos C wchar_t*, que debe ser un puntero a una cadena de caracteres anchos con terminación cero. El constructor acepta una dirección entera, o una cadena.

class ctypes.c_bool

Representa el tipo de datos C bool (más exactamente, _Bool de C99). Su valor puede ser True o False, y el constructor acepta cualquier objeto que tenga un valor verdadero.

class ctypes.HRESULT

Sólo Windows: Representa un valor HRESULT , que contiene información de éxito o error para una llamada de función o método.

class ctypes.py_object

Representa el tipo de datos C PyObject*. Llamar esto sin un argumento crea un puntero NULL PyObject*.

El módulo ctypes.wintypes proporciona otros tipos de datos específicos de Windows, por ejemplo HWND, WPARAM, o DWORD. Algunas estructuras útiles como MSG o RECT también están definidas.

Tipos de datos estructurados

class ctypes.Union(*args, **kw)

Clase base abstracta para uniones en orden de bytes nativos.

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 con un orden de bytes no nativo no pueden contener campos de tipo puntero, o cualquier 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 a sizeof(), 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_

Un pequeño entero opcional que permite anular la alineación de los campos de estructura en la instancia. _pack_ ya debe estar definido cuando se asigna _fields_, de lo contrario no tendrá ningún efecto.

_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 campo vt especifica cuál de los campos de unión es válido. Como el campo u está definido como campo anónimo, ahora es posible acceder a los miembros directamente desde la instancia TYPEDESC. td.lptdesc y td.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 positivo. Alternativamente, puedes subclasificar este tipo y definir las variables de clase _length_ y _type_. Los elementos del arreglo pueden ser leídos y escritos usando subíndices estándar y accesos slice; para las lecturas slice, el objeto resultante no es en sí mismo un Array.

_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 por len().

_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 por pointer().

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á un TypeError. 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.