2009-06-27 10 views
6

¿Cuál es la mejor forma de diseñar una API C para dlls que trata el problema de pasar "objetos" que dependen del tiempo de ejecución de C (FILE *, puntero devuelto por malloc, etc.) . Por ejemplo, si dos dlls están vinculados con una versión diferente del tiempo de ejecución, entiendo que no se puede pasar un archivo * de una dll a la otra de manera segura.C objetos en tiempo de ejecución, límites dll

¿Es la única solución para usar API dependiente de Windows (que se garantiza que funciona en dlls)? La API C ya existe y está madura, pero fue diseñada desde un punto de vista de Unix, principalmente (y aún tiene que funcionar en Unix, por supuesto).

Respuesta

0

El problema con los diferentes tiempos de ejecución no se puede resolver porque la estructura FILE * pertenece a un tiempo de ejecución en un sistema de Windows.

Pero si escribes una envoltura pequeña Interfaz tu hecho y realmente no duele.

stdcall IFile* IFileFactory(const char* filename, const char* mode); 

class IFile { 

    virtual fwrite(...) = 0; 
    virtual fread(...) = 0; 

    virtual delete() = 0; 
} 

Esto se ahorra para pasar a través de los límites de dll en todas partes y realmente no duele.

P.S .: Tenga cuidado si comienza a lanzar excepciones a través de los límites dll. Esto funcionará muy bien si cumple algunas creteriones de diseño en el sistema operativo Windows, pero fallará en algunos otros.

1

Ninguna respuesta existente es correcta: dado lo siguiente en Windows: tiene dos DLL, cada una está estáticamente vinculada con dos versiones diferentes de las bibliotecas estándar de C/C++.

En este caso, no debe pasar punteros a las estructuras creadas por la biblioteca estándar de C/C++ en una DLL a la otra. La razón es que estas estructuras pueden ser diferentes entre las dos implementaciones de la biblioteca estándar de C/C++.

La otra cosa que no debes hacer es liberar un puntero asignado por nuevo o malloc de una DLL que fue asignada en la otra. El administrador de montón también puede implementarse de manera diferente.

Tenga en cuenta que puede utilizar los punteros entre los archivos DLL, solo apuntan a la memoria. Lo gratuito es el problema.

Ahora, puede encontrar que esto funciona, pero si lo hace, entonces usted es solo suerte. Es probable que esto le cause problemas en el futuro.

Una posible solución a su problema es vincular dinámicamente al CRT. Por ejemplo, puede vincular dinámicamente a MSVCRT.DLL. De esa forma, su DLL siempre usará el mismo CRT.

Nota, sugiero que no es una buena práctica pasar estructuras de datos CRT entre archivos DLL. Es posible que desee ver si puede factorizar mejor las cosas.

Nota, no soy un experto en Linux/Unix, pero también tendrá los mismos problemas en esos sistemas operativos.

+0

Entiendo los problemas - Estoy pidiendo respuestas a este problema :) Esperaba una solución que no suponga cambiar la API C existente (con FILE * en la firma, etc.), pero parece que no existe ¿No puedo garantizar que todo esté vinculado al mismo tiempo de ejecución de C? Aunque el problema es teóricamente el mismo en Unix, rara vez es un problema porque solo hay un tiempo de ejecución de C. Nunca tuve un solo problema al pasar FILE *, descriptores de archivos en Unix entre bibliotecas: muchos C APIS están diseñados de esta manera y funcionan sin problemas en esos sistemas operativos. –

+0

Si tener FILE * en la firma de la API no es una buena práctica, ¿cómo se maneja en absoluto el flujo de archivos? ¿Necesita exportar las funciones para abrir y cerrar los archivos si la DLL que llama tiene algunas funciones que llaman internamente a fprintf y otras funciones que esperan FILE *? –

+0

He sugerido una solución :) No establezca un enlace estático con el CRT. enlace a MSVCRT.DLL. Me sorprendería mucho si se garantizara que todas las bibliotecas CRT en Linux, Unix o MAC funcionarían sin fallas. Creo que has tenido suerte allí también. – Foredecker

0

Si la API de C existe y está madura, eludir el CRT internamente mediante el uso de API pura de Win32 te lleva a la mitad del camino. La otra mitad es asegurarse de que el usuario de la DLL utiliza las funciones correspondientes de la API de Win32. Esto hará que su API sea menos portátil, tanto en uso como en documentación. Además, incluso si va de esta manera con la asignación de memoria, donde las funciones CRT y Win32 se ocupan de void *, aún tiene problemas con el archivo: Win32 API utiliza identificadores, y no sabe nada sobre la estructura FILE.

No estoy seguro de cuáles son las limitaciones del ARCHIVO *, pero supongo que el problema es el mismo que con las asignaciones de CRT en los módulos. MSVCRT usa Win32 internamente para manejar las operaciones de archivo, y el manejador de archivo subyacente se puede usar desde cada módulo dentro del mismo proceso. Lo que podría no funcionar es cerrar un archivo que fue abierto por otro módulo, que implica liberar la estructura de ARCHIVO en un CRT posiblemente diferente.

Lo que haría, si cambiar la API sigue siendo una opción, es exportar las funciones de limpieza para cualquier posible "objeto" creado dentro de la DLL. Estas funciones de limpieza se encargarán de la eliminación del objeto dado de la forma que corresponda a la forma en que se creó en esa DLL. Esto también hará que la DLL sea absolutamente portátil en términos de uso. La única preocupación que tendrá entonces es asegurarse de que el usuario del DLL realmente use sus funciones de limpieza en lugar de las de CRT regulares. Esto se puede hacer usando varios trucos, que merecen otra pregunta ...

2

Usted solicitó una solución C, no una solución C++.

El método usual (s) para hacer este tipo de cosas en C son:

  • Diseño de la API de módulos simplemente no requieren objetos CRT. Haga que se transfieran elementos en los tipos de C sin procesar, es decir, haga que el consumidor cargue el archivo y simplemente le pase el puntero. O bien, haga que el consumidor pase un nombre de archivo totalmente calificado, que se abra, lea y cierre internamente.

  • Me viene a la mente un enfoque utilizado por otros módulos c, el gabinete MS y partes de la biblioteca OpenSSL iirc, hacen que la aplicación consumidora transmita punteros a funciones para la función de inicialización. Por lo tanto, cualquier API a la que le pase un ARCHIVO * en algún momento durante la inicialización tomará un puntero a una estructura con punteros de funciones que coincidan con las firmas de fread, fopen, etc. Cuando se trata de los ARCHIVOS externos *, el dll siempre usa los pasados funciones en lugar de las funciones CRT.

Con algunos trucos simples como esto se puede hacer de su interfaz de DLL C enteramente independiente de la CRT anfitriones - o, de hecho, requieren el ejército para ser escrito en C o C++ en absoluto.

Cuestiones relacionadas