Question

Supposons que j'écris une DLL en C ++ et déclare un objet global d'une classe avec un destructeur non trivial. Le destructeur sera-t-il appelé une fois la DLL déchargée?

Était-ce utile?

La solution

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

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

0 - Bien sûr, les objets non constants globaux sont diaboliques (mais vous le savez déjà, je vais donc éviter de parler de multithreading, de verrous, d'objets divins, etc.)

1 - L'ordre de construction des objets ou des différentes unités de compilation (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 consiste à déplacer tous les objets globaux dans le même fichier CPP, car dans 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 est interdit de faire certaines choses dans DllMain. Ces choses sont probablement interdites aussi 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 différée pourrait être intéressante: les classes restent dans un répertoire "non initialisé". state (les pointeurs internes sont NULL, les booléens sont faux, peu importe) jusqu'à ce que vous appeliez l'une de leurs méthodes, où ils s'initialiseront. Si vous utilisez ces objets dans la main (ou l’une des fonctions descendantes de la main), tout ira bien car ils seront appelés après l’exécution de DllMain.

3 - Bien entendu, si certains objets globaux de la DLL A dépendent d’objets globaux de la DLL B, vous devez être très prudent en ce qui concerne l’ordre de chargement des DLL et donc les dépendances. Dans ce cas, les DLL avec des dépendances circulaires directes ou indirectes vous causeront une quantité insensée de maux de tête. La meilleure solution consiste à rompre les dépendances circulaires.

P.S .: Notez qu'en C ++, le constructeur peut lancer et que vous ne voulez pas d'exception au milieu du chargement d'une DLL. Veillez donc à ce que vos objets globaux ne l'utilisent pas sans une très bonne raison. Comme les destructeurs correctement écrits ne sont pas autorisés à lancer, le déchargement de la DLL doit être correct dans ce cas.

Autres conseils

Cette page de Microsoft décrit en détail l’initialisation des DLL et la destruction des globals:
http://msdn.microsoft.com/en-us/library/988ye33t. aspx

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

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

Il doit être appelé lorsque l'application se termine ou lorsque la DLL est déchargée, selon la première éventualité. Notez que cela dépend un peu du runtime que vous compilez.

Méfiez-vous également des destructeurs non triviaux, car il existe des problèmes de synchronisation et de classement. Votre DLL peut être déchargée après une DLL sur laquelle votre destructeur s'appuie, ce qui causerait évidemment des problèmes.

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

Dans les fichiers d’images binaires Windows portant l’extension * .exe, * .dll se trouvent dans Format PE Ces fichiers ont un point d'entrée. Vous pouvez le voir avec l'outil dumpbin comme

  

dumpbin / headers nomdll.dll

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

Ces fonctions effectuent l'initialisation des runtime c et c ++ et en délèguent l'exécution à (main, WinMain) ou à DllMain, respectivement.

Si vous utilisez le compilateur VC de Microsofts, vous pouvez regarder le code source de cette fonction dans votre répertoire VC:

  • crt0.c
  • dllcrt0.c

Processus DllMainCRTStartup Tout ce qui est nécessaire pour initier / définir vos variables globales à partir de sections .data dans un scénario normal, lorsqu’il récupère la notification DLL_PROCESS_DETACH lors du déchargement de la DLL. Par exemple:

  • main ou WinMain du thread de démarrage du programme renvoie le flux de contrôle
  • vous appelez explicitement FreeLibrary et use-dll-counter vaut zéro
Licencié sous: CC-BY-SA avec attribution
Non affilié à StackOverflow
scroll top