¿Por qué se bloquea este progtwig ?: paso de std :: string entre archivos DLL

Tengo algunos problemas para averiguar por qué los siguientes lockings (MSVC9):

//// the following compiles to A.dll with release runtime linked dynamically //Ah class A { __declspec(dllexport) std::string getString(); }; //A.cpp #include "Ah" std::string A::getString() { return "I am a string."; } //// the following compiles to main.exe with debug runtime linked dynamically #include "Ah" int main() { A a; std::string s = a.getString(); return 0; } // crash on exit 

Obviamente (?) Esto se debe a los diferentes modelos de memoria para el ejecutable y DLL. ¿Podría ser que la cadena A::getString() devuelve se asigna en A.dll y se libera en main.exe?

Si es así, ¿por qué? ¿Y cuál sería una forma segura de pasar cadenas entre DLL (o ejecutables, para el caso)? Sin usar wrappers como shared_ptr con un eliminador personalizado.

En realidad, esto no se debe a las diferentes implementaciones de heap: la implementación de MSVC std :: string no utiliza memoria asignada dinámicamente para cadenas tan pequeñas (utiliza la optimización de cadenas pequeñas). Los CRT deben coincidir, pero eso no es lo que te muerde esta vez.

Lo que sucede es que estás invocando un comportamiento indefinido al violar la Regla de una sola definición .

Las comstackciones de versión y depuración tendrán establecidos diferentes indicadores de preprocesador, y usted encontrará que std::string tiene una definición diferente en cada caso. Pregúntele a su comstackdor qué sizeof(std::string) es – MSVC10 me dice que es 32 en una comstackción de depuración y 28 en una comstackción de lanzamiento (esto no es relleno – 28 y 32 son fronteras de 4 bytes).

¿Entonces que esta pasando? La variable s se inicializa utilizando la versión de depuración del constructor de copia para copiar una versión de lanzamiento de std::string . Las compensaciones de las variables miembro son diferentes entre las versiones, por lo que copia basura. La implementación de MSVC almacena de manera efectiva punteros de inicio y final; usted ha copiado basura en ellos; porque ya no son nulos, el destructor intenta liberarlos y obtienes una infracción de acceso.

Incluso si las implementaciones de almacenamiento dynamic fueran las mismas, colapsarían, ya que está liberando punteros a la memoria que nunca se asignó en primer lugar.


En resumen: las versiones CRT deben coincidir, pero también lo hacen las definiciones, incluidas las definiciones en la biblioteca estándar .

¿Podría ser que la cadena A :: getString () devuelve se asigna en A.dll y se libera en main.exe?

Sí.

Si es así, ¿por qué? ¿Y cuál sería una forma segura de pasar cadenas entre DLL (o ejecutables, para el caso)? Sin usar wrappers como shared_ptr con un eliminador personalizado.

Usar un shared_ptr parece algo sensato. Recuerde, como regla general, las asignaciones y desasignaciones deben realizarse en el mismo módulo para evitar problemas técnicos como estos.

La exportación de objetos STL a través de dlls es, en el mejor de los casos, un pony complicado. Le sugiero que consulte este artículo de MSDN KB primero y esta publicación.

Necesita vincular a la misma lib de tiempo de ejecución (la DLL), depurar o liberar, para cada DLL en su aplicación donde la memoria se asigna en una y se libera en otra. (La razón para usar la lib de tiempo de ejecución dinámicamente enlazado es que habrá un montón para todo el proceso, en lugar de uno por dll / exe que se vincule con el estático).

Esto incluye devolver std :: string y stl-containers por valor, ya que eso es lo que haces.

Las razones son dobles (sección actualizada) :

  • las clases tienen diferentes diseños / tamaños, por lo que el código comstackdo de forma diferente asume que los datos están en diferentes lugares. Quien sea que lo creó primero lo hace bien, pero el otro causará un colapso tarde o temprano.
  • las implementaciones de montón de msvc son diferentes en cada runtime-lib, lo que significa que si intentas liberar un puntero en el montón que no lo asignó, se volverá loco. (Esto sucede si los diseños son similares, es decir, donde sobrevive el primer caso).

Por lo tanto, obtenga sus librerías en tiempo de ejecución, o deje de liberar / asignar en diferentes dlls (es decir, deje de pasar cosas por valor).

Además de lo que se ha dicho anteriormente, asegúrese de que Platform Toolset (en Propiedades-> General) sea idéntico en ambos proyectos. De lo contrario, el contenido de la cadena en el lado de llegada podría ser falso.

Eso me sucedió cuando un proyecto de aplicación de consola con la versión del conjunto de herramientas v100 consumió una biblioteca que estaba configurada en v90.

Esto podría deberse a que el DLL y el EXE están comstackdos con diferentes configuraciones de CRT. Entonces cuando pasas una cadena, ocurre algún conflicto de recursos. Verifique la configuración de su proyecto para el DLL y el ejecutable.