Pregunta

Digamos que escribo una DLL en C++ y declaro un objeto global de una clase con un destructor no trivial.¿Se llamará al destructor cuando se descargue la DLL?

¿Fue útil?

Solución

En una DLL de Windows C++, todos los objetos globales (incluidos los miembros estáticos de las clases) se construirán justo antes de llamar a DllMain con DLL_PROCESS_ATTACH y se destruirán justo después de llamar a DllMain con DLL_PROCESS_DETACH.

Ahora, debes considerar tres problemas:

0 - Por supuesto, los objetos globales no constantes son malos (pero eso ya lo sabes, así que evitaré mencionar subprocesos múltiples, bloqueos, objetos divinos, etc.)

1 - El orden de construcción de los objetos o de las diferentes unidades de compilación (es decir,archivos CPP) no está garantizado, por lo que no puede esperar que el objeto A se construya antes que B si los dos objetos se instancian en dos CPP diferentes.Esto es importante si B depende de A.La solución es mover todos los objetos globales en el mismo archivo CPP, ya que dentro de la misma unidad de compilación, el orden de instanciación de los objetos será el orden de construcción (y el inverso del orden de destrucción)

2 - Hay cosas que están prohibidas en el DllMain.Probablemente esas cosas también estén prohibidas en los constructores.Así que evita bloquear algo.Vea el excelente blog de Raymond Chen sobre el tema:

http://blogs.msdn.com/oldnewthing/archive/2004/01/27/63401.aspx

http://blogs.msdn.com/oldnewthing/archive/2004/01/28/63880.aspx

En este caso, la inicialización diferida podría resultar interesante:Las clases permanecen en un estado "no inicializado" (los punteros internos son NULL, los valores booleanos son falsos, lo que sea) hasta que llamas a uno de sus métodos, momento en el cual se inicializarán.Si usa esos objetos dentro del main (o una de las funciones descendientes del main), estará bien porque serán llamados después de la ejecución de DllMain.

3 - Por supuesto, si algunos objetos globales en la DLL A dependen de objetos globales en la DLL B, debe tener mucho cuidado con el orden de carga de las DLL y, por lo tanto, con las dependencias.En este caso, las DLL con dependencias circulares directas o indirectas le causarán una enorme cantidad de dolores de cabeza.La mejor solución es romper las dependencias circulares.

PD.:Tenga en cuenta que en C++, el constructor puede generar y no desea una excepción en medio de la carga de una DLL, así que asegúrese de que sus objetos globales no utilicen una excepción sin una muy buena razón.Como los destructores escritos correctamente no están autorizados a lanzarse, la descarga de DLL debería estar bien en este caso.

Otros consejos

Esta página de Microsoft detalla los detalles de la inicialización de DLL y la destrucción de globales:
http://msdn.microsoft.com/en-us/library/988ye33t.aspx

Si desea ver el código real que se ejecuta al vincular un .dll, eche un vistazo a %ProgramFiles%\Visual Studio 8\vc\crt\src\dllcrt0.c.

Desde la inspección, los destructores serán llamados vía _cexit() cuando el recuento de referencias internas mantenido por el dll CRT llega a cero.

Se debe llamar cuando finaliza la aplicación o se descarga la DLL, lo que ocurra primero.Tenga en cuenta que esto depende en cierta medida del tiempo de ejecución real en el que está compilando.

Además, tenga cuidado con los destructores no triviales, ya que existen problemas de orden y sincronización.Su DLL puede estar descargada después una DLL en la que se basa su destructor, lo que obviamente causaría problemas.

Cuando se llama a DllMain con el parámetro fdwReason = DLL_PROCESS_DETACH, significa que la aplicación descarga la DLL.Este es el momento antes de que se llame al destructor de objetos globales/estáticos.

En Windows, los archivos de imagen binaria con extensión *.exe, *.dll están en formato PEEstos archivos tienen un punto de entrada.Puedes verlo con la herramienta dumpbin como

dumpbin /headers nombredll.dll

Si usa el tiempo de ejecución C desde Microsoft, entonces su punto de entrada será algo así como *CRTStartup o *dllMaincrtStartup

Estas funciones realizan la inicialización del tiempo de ejecución de c y c++ y delegan la ejecución a (main, WinMain) o a DllMain respectivamente.

Si utiliza el compilador VC de Microsoft, puede ver el código fuente de estas funciones en su directorio VC:

  • crt0.c
  • dllcrt0.c

El proceso DllMainCRTStartup necesita iniciar/desiniciar sus variables globales de las secciones .data en un escenario normal, cuando recupera la notificación DLL_PROCESS_DETACH durante la descarga de dll.Por ejemplo:

  • main o WinMain del hilo de inicio del programa devuelve el flujo de control
  • llamas explícitamente a FreeLibrary y use-dll-counter es cero
Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top