2012-07-22 21 views
5

Dado: Ejecutable utiliza dll. Tienen diferentes tiempos de ejecución c/C++. ¿Qué restricciones existen en la interfaz entre ellos? Además usan el mismo compilador, la misma versión de Boost (pero diferentes libs de refuerzo precompilados).interfaz entre exe y dll con diferente biblioteca de tiempo de ejecución de C/C++

Entiendo que el tiempo de ejecución diferente puede tener diferentes montones. Por lo tanto, eliminar debe corresponder a nuevo del mismo montón.

Lo más importante es que no podemos pasar a través de objetos STL de interfaz porque cuando construimos exe el objeto STL se vincula con un tiempo de ejecución y cuando construimos dll el mismo objeto (si lo pasamos por referencia o copiamos vía interfaz) otro tiempo de ejecución. Y otro tiempo de ejecución puede tener una implementación diferente de ese objeto.

consideremos casos:

  1. Creo que la siguiente es seguro. Dll exporta la función que tiene el parámetro: referencia a clase definida por el usuario exportada que contiene clase STL privada como miembro. Dll asigna memoria para este objeto. Exe llama Método de liberación de este objeto cuando desea eliminarlo.

  2. Creo que el siguiente NO es seguro. La clase definida por el usuario se crea una instancia en exe y se pasa a través de la interfaz exe/dll. Esta clase contiene una clase privada de STL como miembro. exe y dll comparten encabezados/archivos de implementación de esta clase de usuario. Cuando esta clase se construye en proyectos separados, se usarán diferentes implementaciones de STL. Por ejemplo, se aplicará diferente implementación de string :: size() (de diferentes tiempos de ejecución) para el mismo objeto en la memoria.

  3. Creo que lo siguiente es seguro. La clase definida por el usuario se crea una instancia en exe y se pasa a través de la interfaz exe/dll. Esta clase no depende de la biblioteca estándar, solo usa tipos primitivos de C++. exe y dll comparten encabezados/archivos de implementación de esta clase de usuario. También debemos controlar que new y delete correspondan al mismo montón. Por ejemplo, podemos sobrecargar new/delete para que usen :: GetProcessHeap.

  4. Creo que lo siguiente NO es seguro: pasar objetos de impulso a través de la interfaz exe/dll porque pueden depender de las clases de biblioteca estándar. También eliminar puede no corresponder al nuevo montón.

  5. Creo que lo siguiente NO es seguro: incluso si pasamos objetos boost a través de la interfaz exe/dll y no dependen de las clases de biblioteca estándar pero no se implementan solo como encabezado, entonces el objeto se puede crear con un impulso lib (para un tiempo de ejecución) y utilizado con otro boost lib (para otro tiempo de ejecución). También eliminar puede no corresponder al nuevo montón.

También quiero utilizar un poco de sabor de puntero inteligente para pasar referencia a objetos (mencionados en el punto 3) de EXE a partir de DLL y DLL a EXE. Creo que este puntero inteligente también debería sobrecargar new/delete para asignar el contador de referencia del montón de proceso predeterminado. Cuando se va a tratar de eliminar objeto puntiagudo se llamará a eliminar esa sobrecargada por este objeto (como en elemento3)

Para los objetos de la partida 1 Quiero usar puntero personalizado inteligente que método de liberación de objeto puntiagudo llamar (como se boost :: shared_ptr con versión personalizada)

¿Qué problemas no se mencionaron? Corrígeme por favor.

Respuesta

4

La respuesta general a su pregunta es: hacer algo con un objeto recibido de un EXE/DLL es seguro si lo que realmente se hace no depende del tiempo de ejecución. P.ej. llamadas vtable, llamadas a punteros a funciones, llamadas a funciones explícitas desde DLL.

Las cosas que dependen del tiempo de ejecución (métodos en línea desde archivos de encabezado, cualquier cosa que haga suposiciones sobre el diseño de objetos STL, etc.) no son seguras.

regresando a sus ejemplos:

  1. Si se llama al método Release() debe tener cuidado y asegurarse de que se le llama a la aplicación de la etapa() de la DLL y no otra aplicación que fue creado por el compilador que crea el archivo EXE. La forma más fácil de asegurarse de que sea Release() virtual para que la llamada sea siempre una llamada utilizando un puntero de método de vtable (provisto por DLL).

  2. Correcto, los métodos en línea de STL como string :: size() causarán problemas aquí.

  3. Derecha. Si new/delete están sobrecargados para usar GetProcessHeap(), el código funcionará.
  4. En general, a la derecha. En particular, consulte la implementación de las clases de refuerzo que necesita.
  5. Si no dependen de STL y está seguro de que la huella de memoria es la misma para ambos compiladores y ha proporcionado una nueva/eliminación personalizada(), generalmente no debería ser un problema (consulte los comentarios a continuación).

Observaciones:

Hay algunas cosas de baja probabilidad que pueden causar problemas no mencionados anteriormente:

  1. Si utiliza diferentes compiladores, podrían usar diferentes convenciones de llamada por defecto (cdecl/stdcall).
  2. Las opciones de alineación predeterminadas también pueden ser diferentes, lo que da como resultado diferentes diseños de memoria.
  3. Si arroja excepciones desde su DLL, es posible que el exe con un tiempo de ejecución diferente no las capture, sino que se bloquee.
  4. Marcar los métodos con los atributos de importación/exportación de DLL y asegurarse de que no estén en línea puede ser bastante complicado. Además, si está utilizando compiladores diferentes, el algoritmo de creación de nombres C++ puede ser diferente.

Teniendo todo lo resumido, se recomienda ver cómo se implementan las cosas en Microsoft COM y diseñar algo similar. Es decir. restrinja la interacción EXE/DLL a:

  1. Interfaces. Es decir. C++ clases con métodos virtuales puros solamente. Use Virtual Release() para eliminar los objetos.
  2. Estructuras simples claramente definidas. Asegúrese de que las opciones de alineación para todos los compiladores coincidan. Si desea utilizar estructuras no triviales y está utilizando diferentes compiladores, mejor ser paranoico y poner controles como estos:

    funciones
    struct MyStruct 
    { 
        ... 
    }; 
    C_ASSERT(sizeof(MyStruct) == ...); 
    C_ASSERT(FIELD_OFFSET(MyStruct, MyMember) = ...); 
    
  3. C-como pasan y/o que regresan punteros a interfaces.

  4. No arrojar excepciones de los métodos llamados por EXE
Cuestiones relacionadas