2009-10-28 11 views
8

Tengo un archivo EXE que utiliza un archivo DLL que utiliza otro archivo DLL. Esta situación ha surgido:Liberar memoria asignada en una DLL diferente

En el archivo DLL de 1:

class abc 
{ 
    static bool FindSubFolders(const std::string & sFolderToCheck, 
           std::vector<std::string> & vecSubFoldersFound); 
} 

En el archivo DLL 2:

void aFunction() 
{ 
    std::vector<std::string> folders; 
    std::string sLocation; 
    ... 
    abc::FindSubFolders(sLocation, folders) 
} 

en modo de lanzamiento, todo funciona bien. Pero en modo de depuración, se me ocurre un error de aserción en el destructor de uno de los std::strings en el vector de carpetas (cuando las carpetas se salen del ámbito al final de AFunction):

dbgheap.c : line 1274

/* 
* If this ASSERT fails, a bad pointer has been passed in. It may be 
* totally bogus, or it may have been allocated from another heap. 
* The pointer MUST come from the 'local' heap. 
*/ 
_ASSERTE(_CrtIsValidHeapPointer(pUserData)); 

Supongo que esto es porque la memoria ha sido asignada en el archivo DLL 1, pero está siendo liberada en el archivo DLL 2.

El comentario en dbgheap.c parece bastante insistente en que esto es un problema.

¿Por qué es esto un problema, cuando parece funcionar bien si simplemente lo ignoro? ¿Hay alguna forma de hacer esto que no sea afirmativa?

+2

NO. IGNORAR . ASERCIONES – KeatsPeeks

+22

NO. POSTERGACIÓN. ESO. ESO ES. POR QUÉ. YO PREGUNTÉ. = P – Smashery

+0

Solo quiero saber * por qué * es un problema. – Smashery

Respuesta

11

Como ya lo han dicho, la versión de lanzamiento simplemente ignora esa declaración de eliminación, por lo que lo mejor que puede esperar es una pérdida de memoria.

Si tiene control sobre cómo se compilan ambos archivos DLL, asegúrese de utilizar la configuración de DLL de depuración de múltiples subprocesos (/ MDd) o DLL de subprocesos múltiples (/ MD) para la biblioteca de tiempo de ejecución. De esta forma, ambos archivos DLL usarán el mismo sistema de tiempo de ejecución y compartirán el mismo montón.

El inconveniente es que necesita instalar el sistema de tiempo de ejecución junto con su aplicación (Microsoft ofrece un instalador para eso). Funcionará bien en su máquina de desarrollo ya que Visual Studio instala ese sistema de tiempo de ejecución también, pero en una máquina recién instalada informará los archivos DLL que faltan.

+0

Las propiedades de mi proyecto eran incorrectas. No tenía el conjunto de DLL de depuración (/ MDd); ahora funciona bien. ¡Gracias! – Smashery

6

Lo más probable es que la compilación de versión tenga el mismo problema, pero las compilaciones de versión no se afirman. Simplemente ignoran el problema. Es posible que nunca vea un problema. O puede ver corrupción de datos. O es posible que vea un bloqueo. Quizás solo tus usuarios experimenten errores que simplemente no puedes reproducir.

No ignore las afirmaciones de CRT.

Siempre debe usar el correspondiente separador (el que coincide con el asignador utilizado para empezar). Si está utilizando bibliotecas CRT estáticas en sus archivos DLL, los archivos DLL están usando montones diferentes. No puede desasignar memoria entre montones. Asigne y desasigne un bloque de memoria utilizando el mismo montón.

Si está utilizando bibliotecas CRT compartidas en sus archivos DLL, entonces deberían estar usando el mismo montón y pueden asignar en un archivo DLL y desasignar en otro.

+2

cf. Raymond Chen: http://blogs.msdn.com/oldnewthing/archive/2006/09/15/755966.aspx –

+0

+1 - ¡Gracias! – Smashery

5

Esto es solo un problema si la aplicación o uno (o más) de los archivos DLL está vinculado a la versión estática de la biblioteca estándar. Esto fue resuelto hace una década por MS liberando la versión de biblioteca compartida de la biblioteca estándar. Esto se debe a que cada versión de la biblioteca estándar construirá su propio montón interno y, por lo tanto, con varios montones, debe liberar la memoria al montón correcto. Al usar la versión compartida de la biblioteca estándar, todos usan el mismo montón.

Es una práctica común hoy en día para la solicitud y todos los archivos DLL debe ser construido para utilizar la versión dinámica de la biblioteca estándar.

La única salvedad de lo anterior es si crea su propio montón y asigna memoria de este montón. Pero este es un procedimiento muy especializado que solo se realiza en situaciones excepcionales (y si comprende lo suficiente para usarlo, entonces no se encontrará en la situación de hacer esta pregunta).

6

Como otro dice, el problema puede ser resuelto por asegurarse de que el CRT se comparte entre los dos módulos. Pero hay escenarios comunes en los que este contrato es difícil de aplicar.

La razón es que asegurarse de vincular con un CRT compartido no funcionará si el EXE y la DLL no enlazan con el mismo CRT versión (como en 6.0, 7.0, 8.0). Por ejemplo, si toma una DLL que se ha creado en VC6.0 y trata de usarla con una compilación EXE en VS2010, obtendrá el mismo problema que antes. Las dos versiones de CRT se cargarán en su proceso y cada una usará su propio montón para la asignación, independientemente de si su EXE y DLL usan CRT 'compartido', no serán las mismas.

Una mejor práctica para un API sería para asegurarse de que los objetos asignados en uno de los lados también son destruidos en el mismo lado. Sé que esto suena feo, pero es la única manera de garantizar que una DLL permanezca compatible con archivos binarios.