




Он должен вызываться либо при завершении приложения, либо при выгрузке библиотеки DLL, в зависимости от того, что наступит раньше. Обратите внимание, что это в некоторой степени зависит от фактического времени выполнения, с которым вы компилируете.
Также остерегайтесь нетривиальных деструкторов, поскольку есть проблемы как с синхронизацией, так и с порядком. Ваша DLL может быть выгружена после DLL, на которую опирается ваш деструктор, что, очевидно, вызовет проблемы.
Когда вызывается DllMain с параметром fdwReason = DLL_PROCESS_DETACH, это означает, что DLL выгружается приложением. Это время до вызова деструктора глобальных / статических объектов.
На этой странице от Microsoft подробно рассказывается об инициализации DLL и уничтожении глобальных объектов:
http://msdn.microsoft.com/en-us/library/988ye33t.aspx
Если вы хотите увидеть фактический код, который выполняется при связывании .dll, взгляните на %ProgramFiles%\Visual Studio 8\vc\crt\src\dllcrt0.c.
После проверки деструкторы будут вызываться через _cexit(), когда счетчик внутренних ссылок, поддерживаемый DLL CRT, достигнет нуля.
В Windows C++ DLL все глобальные объекты (включая статические члены классов) будут созданы непосредственно перед вызовом DllMain с помощью DLL_PROCESS_ATTACH, и они будут уничтожены сразу после вызова DllMain с помощью DLL_PROCESS_DETACH.
Теперь вы должны рассмотреть три проблемы:
0 - Конечно, глобальные неконстантные объекты - зло (но вы это уже знаете, поэтому я не буду упоминать многопоточность, блокировки, объекты-боги и т. д.)
1 - Порядок построения объектов или различных единиц компиляции (то есть файлов CPP) не гарантируется, поэтому вы не можете надеяться, что объект A будет построен до B, если два объекта созданы в двух разных CPP. Это важно, если B зависит от A. Решение состоит в том, чтобы переместить все глобальные объекты в один и тот же файл CPP, поскольку внутри одного модуля компиляции порядок создания экземпляров объектов будет порядком построения (и обратным порядку разрушения)
2 - Есть вещи, которые запрещено делать в DllMain. Эти вещи, наверное, тоже запрещены в конструкторах. Так что избегайте блокировки чего-либо. См. Отличный блог Раймонда Чена на эту тему:
В этом случае может быть интересна ленивая инициализация: классы остаются в «неинициализированном» состоянии (внутренние указатели равны NULL, логические значения - false и т. д.) До тех пор, пока вы не вызовете один из их методов, после чего они инициализируются сами. Если вы используете эти объекты внутри основной функции (или одной из функций-потомков основного), все будет в порядке, потому что они будут вызываться после выполнения DllMain.
3 - Конечно, если некоторые глобальные объекты в DLL A зависят от глобальных объектов в DLL B, вы должны быть очень осторожны с порядком загрузки DLL и, следовательно, с зависимостями. В этом случае библиотеки DLL с прямой или косвенной циклической зависимостью доставят вам безумную головную боль. Лучшее решение - разорвать круговые зависимости.
P.S .: Обратите внимание, что в C++ конструктор может генерировать, и вам не нужно исключение в середине загрузки DLL, поэтому убедитесь, что ваши глобальные объекты не будут использовать исключение без очень уважительной причины. Поскольку правильно написанные деструкторы не авторизованы для выброса, выгрузка DLL в этом случае должна быть в порядке.
@ LB--: Обычно нет: у каждого процесса есть своя собственная глобальная переменная пока не, вы можете сопоставить их в общей памяти или использовать особый трюк ОС, чтобы сделать их общими для всех процессов, использующих эту DLL (я точно не помню трюк в Windows, но он включал объявления #pragma, IIRC)
В Windows файлы двоичных изображений с расширением * .exe, * .dll находятся в Формат PE Такие файлы имеют точку входа. Вы можете просмотреть его с помощью инструмента dumpbin, например
dumpbin /headers dllname.dll
Если вы используете среду выполнения C от Microsoft, то ваша точка входа будет примерно такой: * CRTStartup или * DllMainCRTStartup
Такие функции выполняют инициализацию среды выполнения c и C++ и делегируют выполнение (main, WinMain) или DllMain соответственно.
Если вы используете компилятор Microsofts VC, вы можете посмотреть исходный код этих функций в своем каталоге VC:
DllMainCRTStartup обрабатывает все, что необходимо для инициализации / деинициализации ваших глобальных переменных из разделов .data в обычном сценарии, когда он извлекает уведомление DLL_PROCESS_DETACH во время выгрузки dll. Например:
Если один процесс изменяет глобальное значение, наблюдается ли это изменение в другом процессе?