Pregunta

Esta pregunta está relacionada con " Cómo hacer coherente binarios dll en las versiones VS? "

  • Tenemos aplicaciones y archivos DLL construidos con VC6 y una nueva aplicación integrada con VC9. La aplicación VC9 tiene que usar DLL compilados con VC6, la mayoría de que están escritos en C y uno en C ++.
  • La biblioteca de C ++ es problemática debido a decoración de nombre / problemas de destrucción.
  • Compilar todo con VC9 es Actualmente no es una opción ya que hay Parece que hay algunos efectos secundarios. Resolver esto sería bastante tiempo consumiendo.
  • Puedo modificar la biblioteca de C ++, sin embargo, debe compilarse con VC6.
  • La biblioteca de C ++ es esencialmente un contenedor OO para otra biblioteca de C. La aplicación VC9 utiliza algunas funciones estáticas, así como algunas no estáticas.

Si bien las funciones estáticas se pueden manejar con algo como

// Header file
class DLL_API Foo
{
    int init();
}

extern "C"
{
    int DLL_API Foo_init();
}

// Implementation file
int Foo_init()
{
    return Foo::init();
}

no es tan fácil con los métodos no estáticos.

Según tengo entendido, Chris Becke's ¿Estoy allí?

¿Sería la única solución escribir una interfaz DLL de estilo C usando controladores para los objetos o me falta algo? En ese caso, supongo, probablemente tendría menos esfuerzo al usar directamente la biblioteca C envuelta.

¿Fue útil?

Solución

Los nombres de los miembros de la interfaz no se decorarán, solo se compensan en una vtable. Puede definir una interfaz (usando una estructura C, en lugar de una interfaz COM ") en un archivo de encabezado, por lo tanto:

struct IFoo {
    int Init() = 0;
};

Entonces, puede exportar una función desde la DLL, sin alterar:

class CFoo : public IFoo { /* ... */ };
extern "C" IFoo * __stdcall GetFoo() { return new CFoo(); }

Esto funcionará bien, siempre que esté utilizando un compilador que genere vtables compatibles. Microsoft C ++ ha generado el mismo formato vtable desde (al menos, creo) MSVC6.1 para DOS, donde vtable es una lista simple de punteros a funciones (con thunking en el caso de herencia múltiple). GNU C ++ (si no recuerdo mal) genera vtables con punteros de función y compensaciones relativas. Estos no son compatibles entre sí.

Otros consejos

El mayor problema a tener en cuenta cuando se utiliza una DLL compilada con un compilador de C ++ diferente al EXE que llama es la asignación de memoria y la vida útil del objeto.

Supongo que puede pasar el cambio de nombre (y la convención de llamadas), lo cual no es difícil si usa un compilador con cambio compatible (creo que VC6 es ampliamente compatible con VS2008), o si usa extern " C " ;.

Donde se encontrará con problemas es cuando asigna algo usando new (o malloc ) desde la DLL, y luego lo devuelve a la persona que llama. La delete (o free ) de la persona que llama intentará liberar el objeto de un montón diferente. Esto saldrá terriblemente mal.

Puede hacer una cosa de IFoo :: Release de estilo COM, o una cosa de MyDllFree () . Ambos, debido a que vuelven a llamar a la DLL, utilizarán la implementación correcta de delete (o free () ), por lo que eliminarán el objeto correcto.

O bien, puede asegurarse de usar LocalAlloc (por ejemplo), de modo que el EXE y la DLL estén usando el mismo montón.

Bueno, creo que sugerencia de Chris Becke está bien. No usaría primera solución de Roger , que utiliza una interfaz solo de nombre y, como él menciona, puede encontrarse con problemas de manejo incompatible de compiladores de clases abstractas y métodos virtuales. Roger señala el atractivo caso compatible con COM en su seguimiento .

  1. El punto débil: debe aprender a realizar solicitudes de interfaz COM y tratar adecuadamente con IUnknown, confiando en al menos IUnknown: AddRef e IUnknown: Release. Si las implementaciones de interfaces pueden admitir más de una interfaz o si los métodos también pueden devolver interfaces, también es posible que deba sentirse cómodo con IUnknown: QueryInterface.

  2. Aquí está la idea clave. Todos los programas que usan la implementación de la interfaz (pero no la implementan) usan un #include común "*. H" archivo que define la interfaz como una estructura (C) o una clase C / C ++ (VC ++) o estructura (no VC ++ sino C ++). El archivo * .h se adapta automáticamente de manera apropiada dependiendo de si está compilando un programa de lenguaje C o un programa de lenguaje C ++. No tiene que saber sobre esa parte simplemente para usar el archivo * .h. Lo que hace el archivo * .h es definir la estructura o tipo de interfaz, digamos, IFoo, con sus funciones de miembro virtual (y solo funciones, sin visibilidad directa para los miembros de datos en este enfoque).

  3. El archivo de encabezado se construye para cumplir con el estándar binario COM de una manera que funciona para C y que funciona para C ++ independientemente del compilador de C ++ que se utilice. (La gente de Java JNI descubrió esto.) Esto significa que funciona entre módulos compilados por separado de cualquier origen, siempre y cuando todos ellos asignen a la memoria una estructura que consista completamente en punteros de entrada de función (una vtable). (por lo que tienen que ser todos x86 de 32 bits, o todos x64, por ejemplo).

  4. En la DLL que implementa la interfaz COM a través de una clase de envoltura de algún tipo, solo necesita un punto de entrada de fábrica. Algo así como un

    externo '' C '' HRESULT MkIFooImplementation (nulo ** ppv);

que devuelve un HRESULT (también necesitará aprender sobre ellos) y también devolverá un * pv en una ubicación que proporcione para recibir el puntero de interfaz IFoo. (Estoy leyendo y hay detalles más cuidadosos que necesitará aquí. No confíe en mi sintaxis) El estereotipo de función real que utiliza para esto también se declara en el archivo * .h.

  1. El punto es que la entrada de fábrica, que siempre es una '' C '' externa sin decoración realiza toda la creación de clase de contenedor necesaria y luego entrega un puntero de interfaz Ifoo a la ubicación que especifique. Esto significa que toda la administración de memoria para la creación de la clase, y toda la administración de memoria para finalizarla, etc., sucederá en la DLL donde construye el contenedor. Este es el único lugar donde tiene que lidiar con esos detalles.

  2. Cuando obtiene un resultado OK de la función de fábrica, se le ha emitido un puntero de interfaz y ya se ha reservado para usted (hay una operación implícita de IFoo: Addref ya realizada en nombre del puntero de interfaz que fueron enviados).

  3. Cuando haya terminado con la interfaz, la libera con una llamada en el método IFoo: Release de la interfaz. Es la implementación de la versión final (en caso de que haya hecho más copias AddRef'd) lo que derribará la clase y su soporte de interfaz en la DLL de fábrica. Esto es lo que le permite confiar correctamente en una asignación de almacenamiento dinámico consistente y liberar detrás de la interfaz

También hay otras cosas que debe tener en cuenta, como los tiempos de ejecución que utilizan las distintas bibliotecas. Si no se comparten objetos, está bien, pero eso parece bastante improbable a primera vista.
Las sugerencias de Chris Becker son bastante precisas: el uso de una interfaz COM real puede ayudarlo a obtener la compatibilidad binaria que necesita. Su kilometraje puede variar :)

no es divertido, hombre. te encuentras con mucha frustración, probablemente deberías dar esto:

  

¿Sería la única solución escribir un   Interfaz DLL de estilo C con controladores   a los objetos o me estoy perdiendo   ¿alguna cosa? En ese caso, supongo, yo   probablemente tendría menos esfuerzo con   directamente usando la biblioteca C envuelta.

una mirada muy cercana. buena suerte.

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top