Digamos que escribo un DLL en C++, y declaro un objeto global de una clase con un destructor no trivial. ¿Se llamará al destructor cuando el DLL está descargado?
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 al DllMain con DLL_PROCESS_ATTACH, y se destruirán justo después de la llamada del DllMain con DLL_PROCESS_DETACH.
Ahora, debes considerar tres problemas:
0 - Por supuesto, los objetos globales no constantes son malos (pero ya lo sabes, así que evitaré mencionar subprocesos múltiples, cerraduras, objetos divinos, etc.)
1 - El orden de construcción de objetos o unidades de compilación diferentes (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 hacer en el DllMain. Es probable que 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 ser interesante: las clases permanecen en un estado "no inicializado" (los punteros internos son NULL, los booleanos son falsos, lo que sea) hasta que llame a uno de sus métodos, momento en el que se inicializarán. Si usa esos objetos dentro del main (o una de las funciones descendientes del main), estará bien porque se los llamará después de la ejecución de DllMain.
3 - Por supuesto, si algunos objetos globales en DLL A dependen de objetos globales en DLL B, debe tener mucho cuidado con DLL orden de carga y, por lo tanto, dependencias. En este caso, las DLL con dependencias circulares directas o indirectas le causarán una gran cantidad de dolores de cabeza. La mejor solución es romper las dependencias circulares.
PD: Tenga en cuenta que en C++, el constructor puede lanzar, y no desea una excepción en medio de una carga de DLL, así que asegúrese de que sus objetos globales no usarán una excepción sin un muy, muy buena razón. Como los destructores escritos correctamente no están autorizados a lanzar, la descarga DLL debería estar bien en este caso.
Esta página de Microsoft entra en los detalles de DLL inicialización y 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
.
A partir de la inspección, los destructores se llamarán a través de _cexit()
cuando el recuento de referencia interno mantenido por el CRT dll llegue a cero.
Debería llamarse cuando finalice la aplicación o cuando se descargue DLL, lo que ocurra primero. Tenga en cuenta que esto depende en cierta medida del tiempo de ejecución real con el que está compilando.
Además, tenga cuidado con los destructores no triviales, ya que hay problemas de tiempo y de orden. Su DLL puede descargarse después a DLL su destructor depende, 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 el DLL). Este es el tiempo antes de que se llame al destructor de objetos globales/estáticos.
En Windows, los archivos de imagen binarios con extensión * .exe, * .dll están en formato PE Tales archivos tienen un punto de entrada. Puedes verlo con la herramienta dumpbin como
dumpbin/headers dllname.dll
Si usa C Runtime de Microsoft, su punto de entrada será algo así como * CRTStartup o * DllMainCRTStartup
Dichas funciones realizan la inicialización del tiempo de ejecución de c y c ++ y delegan la ejecución a (main, WinMain) o DllMain respectivamente.
Si usa Microsofts VC compilador), puede ver el código fuente de estas funciones en el directorio VC:
DllMainCRTStartup procesa todas las cosas necesarias para iniciar/eliminar sus variables globales de las secciones .data en el escenario normal, cuando recupera la notificación DLL_PROCESS_DETACH durante la descarga de dll. Por ejemplo: