top of page
anatoliykotov822

Fallo En La Llamada A La Funcion Dll: Cómo Reparar Este Error Común



Los argumentos que se envían a una biblioteca de vínculos dinámicos (DLL, Dynamic-Link Library) o a una rutina de recursos de código de Macintosh deben coincidir de forma exacta con los argumentos que espera la rutina. En las convenciones de llamada se especifica el número, el tipo y el orden de los argumentos. Las causas y soluciones de este error son las siguientes:




Fallo En La Llamada A La Funcion Dll




El programa llama a una rutina de una DLL, pero no usa la convención de llamada StdCall. Si la rutina de la DLL espera argumentos por valor, asegúrese de especificar ByVal para dichos argumentos en la declaración de la rutina.


Aunque llamar a funciones DLL no administradas es prácticamente idéntico a llamar a otro código administrado, hay diferencias que pueden hacer que las funciones DLL parezcan confusas al principio. En esta sección se presentan temas que describen algunos de los problemas inusuales relacionados con las llamadas.


Las estructuras que se devuelven de llamadas de invocación de plataforma deben ser tipos de datos que tengan la misma representación en código administrado y no administrado. Estos tipos se denominan tipos que pueden transferirse en bloque de bits porque no requieren conversión (vea Tipos que pueden o que no pueden transferirse en bloque de bits). Para llamar a una función que tiene una estructura que no puede transferirse en bloque de bits como su tipo de valor devuelto, se puede definir un tipo del asistente que pueda transferirse en bloque de bits del mismo tamaño que el tipo que no puede transferirse en bloque de bits y convertir los datos después de que la función devuelva un resultado.


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.


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.


Las bibliotecas se cargan accediendo a ellas como atributos de estos objetos. cdll carga bibliotecas que exportan funciones utilizando la convención de llamada estándar cdecl, mientras que las bibliotecas windll llaman a funciones mediante la convención de llamada stdcall. oledll también utiliza la convención de llamada stdcall y asume que las funciones retornan un código de error Windows HRESULT. El código de error se utiliza para generar automáticamente una excepción OSError cuando se produce un error en la llamada a la función.


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.


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


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:


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


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:


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.


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.


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:


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.


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.


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.


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 lanza automáticamente un OSError`.


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.


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.


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.


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:


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:


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.


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.


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


1 view0 comments

Recent Posts

See All

Comments


bottom of page