ventajas variable usar una temporales son qué que locales las hay globales entre diferencia desventajas c++ windows dll

c++ - usar - ¿Qué sucede con las variables globales declaradas en una DLL?



variables temporales c++ (6)

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?


Cuando se llama a DllMain con el parámetro fdwReason = DLL_PROCESS_DETACH, significa que la DLL descarga la 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 la extensión * .exe, * .dll están en formato PE. Estos 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 como * CRTStartup o * DllMainCRTStartup

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

Si usa el compilador Microsofts VC, entonces puede ver el código fuente de estas funciones en su directorio VC:

  • crt0.c
  • dllcrt0.c

DllMainCRTStartup procesa todo lo necesario para iniciar / deinituar las 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:

  • main o WinMain del subproceso de inicio del programa devuelve flujo de control
  • llamas explícitamente a FreeLibrary y use-dll-counter es cero

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

Ahora, debes considerar tres problemas:

0 - Por supuesto, los objetos globales no const son malos (pero ya lo sabes, así que evitaré mencionar los multihilos, bloqueos, objetos de dios, etc.)

1 - El orden de construcción de objetos o diferentes unidades de compilación (es decir, archivos CPP) no está garantizado, por lo que no se puede esperar que el objeto A se construya antes 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 de la orden de destrucción)

2 - Hay cosas que están prohibidas en el DllMain. Esas cosas probablemente 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 usted llama a uno de sus métodos, en cuyo punto se inicializarán. Si usa esos objetos dentro de la principal (o una de las funciones descendientes de la principal), estará bien, ya que se invocarán 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 el orden de carga DLL y, por lo tanto, dependencias. En este caso, los archivos 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 lanzar, y usted no quiere una excepción en el medio de una carga de DLL, así que asegúrese de que sus objetos globales no estarán utilizando la excepción sin una muy buena razón. Como los destructores correctamente escritos no están autorizados a lanzar, la descarga de DLL debería estar bien en este caso.



Se debe invocar cuando la aplicación finaliza o cuando se descarga el archivo DLL, lo que ocurra primero. Tenga en cuenta que esto es algo dependiente del tiempo de ejecución real contra el que está compilando.

Además, tenga cuidado con los destructores no triviales, ya que hay problemas de tiempo y pedidos. Su DLL se puede descargar después de una DLL en la que se basa su destructor, lo que obviamente causaría problemas.


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 serán llamados a través de _cexit() cuando el recuento de referencia interno mantenido por el dll CRT llegue a cero.