web-dev-qa-db-fra.com

Qu'advient-il des variables globales déclarées dans une DLL?

Disons que j'écris un DLL en C++, et déclare un objet global d'une classe avec un destructeur non trivial. Le destructeur sera-t-il appelé lorsque le DLL est déchargé?

38
Dima

Dans une DLL Windows C++, tous les objets globaux (y compris les membres statiques des classes) seront construits juste avant l'appel du DllMain avec DLL_PROCESS_ATTACH, et ils seront détruits juste après l'appel du DllMain avec DLL_PROCESS_DETACH.

Maintenant, vous devez considérer trois problèmes:

0 - Bien sûr, les objets globaux non const sont mauvais (mais vous le savez déjà, donc j'éviterai de mentionner le multithreading, les verrous, les objets divins, etc.)

1 - L'ordre de construction des objets ou des différentes unités de compilation (c'est-à-dire les fichiers CPP) n'est pas garanti, vous ne pouvez donc pas espérer que l'objet A sera construit avant B si les deux objets sont instanciés dans deux CPP différents. Ceci est important si B dépend de A. La solution est de déplacer tous les objets globaux dans le même fichier CPP, car à l'intérieur de la même unité de compilation, l'ordre d'instanciation des objets sera l'ordre de construction (et l'inverse de l'ordre de destruction)

2 - Il y a des choses interdites dans le DllMain. Ces choses sont probablement également interdites chez les constructeurs. Alors évitez de verrouiller quelque chose. Voir l'excellent blog de Raymond Chen sur le sujet:

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

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

Dans ce cas, l'initialisation paresseuse pourrait être intéressante: les classes restent dans un état "non initialisé" (les pointeurs internes sont NULL, les booléens sont faux, peu importe) jusqu'à ce que vous appeliez l'une de leurs méthodes, auquel cas elles s'initialiseront elles-mêmes. Si vous utilisez ces objets à l'intérieur du main (ou de l'une des fonctions descendantes du main), tout ira bien car ils seront appelés après l'exécution de DllMain.

3 - Bien sûr, si certains objets globaux dans DLL A dépendent des objets globaux dans DLL B, vous devez faire très attention à DLL ordre de chargement, et donc dépendances. Dans ce cas, les DLL avec des dépendances circulaires directes ou indirectes vous causeront une quantité folle de maux de tête. La meilleure solution est de briser les dépendances circulaires.

PS: Notez qu'en C++, le constructeur peut lever et que vous ne voulez pas d'exception au milieu d'un chargement DLL, alors assurez-vous que vos objets globaux n'utiliseront pas d'exception sans un très très bonne raison. Comme les destructeurs correctement écrits ne sont pas autorisés à lancer, le déchargement DLL devrait être correct dans ce cas.

37
paercebal

Cette page de Microsoft rentre dans les détails de DLL initialisation et destruction des globaux:
http://msdn.Microsoft.com/en-us/library/988ye33t.aspx

6
Mark Ransom

Si vous souhaitez voir le code réel qui est exécuté lors de la liaison d'un .dll, jetez un œil à %ProgramFiles%\Visual Studio 8\vc\crt\src\dllcrt0.c.

À partir de l'inspection, les destructeurs seront appelés via _cexit() lorsque le nombre de références internes maintenu par le dll CRT atteint zéro.

4
MSN

Il doit être appelé à la fin de l'application ou lorsque le DLL est déchargé, selon la première éventualité. Notez que cela dépend quelque peu de l'exécution réelle avec laquelle vous compilez.

Méfiez-vous également des destructeurs non triviaux car il y a des problèmes de calendrier et de commande. Votre DLL peut être déchargé après a DLL votre destructeur se fie à, ce qui pourrait évidemment causer des problèmes).

3
Philip Rieck

Lorsque DllMain avec le paramètre fdwReason = DLL_PROCESS_DETACH est appelé, cela signifie que le DLL est déchargé par l'application. C'est le temps avant que le destructeur des objets globaux/statiques soit appelé.

1
INS

Dans les fichiers image binaires Windows avec l'extension * .exe, * .dll sont en format PE Ces fichiers ont un point d'entrée. Vous pouvez le voir avec l'outil de vidage comme

dumpbin/headers dllname.dll

Si vous utilisez le runtime C de Microsoft, votre point d'entrée sera quelque chose comme * CRTStartup ou * DllMainCRTStartup

De telles fonctions effectuent l'initialisation du runtime c et c ++ et déléguent l'exécution à (main, WinMain) ou à DllMain respectivement.

Si vous utilisez le compilateur Microsofts VC alors vous pouvez regarder le code source de ces fonctions dans le vôtre VC répertoire:

  • crt0.c
  • dllcrt0.c

DllMainCRTStartup traite tout ce dont vous avez besoin pour initier/désinstaller vos variables globales à partir des sections .data dans le scénario normal, quand il récupère la notification DLL_PROCESS_DETACH pendant le déchargement de la DLL. Par exemple:

  • principal ou WinMain du thread de démarrage du programme renvoie le flux de contrôle
  • vous appelez explicitement FreeLibrary et use-dll-counter est nul
1
bruziuz