Что происходит с глобальными переменными, объявленными в DLL?

StackOverflow https://stackoverflow.com/questions/75701

  •  09-06-2019
  •  | 
  •  

Вопрос

Допустим, я пишу DLL на C++ и объявляю глобальный объект класса с нетривиальным деструктором.Будет ли вызываться деструктор при выгрузке DLL?

Это было полезно?

Решение

В Windows C++ DLL все глобальные объекты (включая статические члены классов) будут созданы непосредственно перед вызовом DllMain с помощью DLL_PROCESS_ATTACH и будут уничтожены сразу после вызова DllMain с помощью DLL_PROCESS_DETACH.

Теперь вам необходимо рассмотреть три проблемы:

0 — Конечно, глобальные неконстантные объекты — это зло (но вы это уже знаете, поэтому я не буду упоминать многопоточность, блокировки, объекты-боги и т. д.)

1 — Порядок построения объектов или различных единиц компиляции (т.е.CPP-файлы) не гарантируется, поэтому вы не можете надеяться, что объект A будет создан раньше B, если два объекта создаются в двух разных CPP.Это важно, если B зависит от A.Решение состоит в том, чтобы переместить все глобальные объекты в один и тот же файл CPP, поскольку внутри одной и той же единицы компиляции порядок создания объектов будет порядком создания (и обратным порядку уничтожения).

2 - Есть вещи, которые запрещено делать в DllMain.Эти вещи, вероятно, тоже запрещены в конструкторах.Поэтому избегайте блокировки чего-либо.См. превосходный блог Рэймонда Чена на эту тему:

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

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

В этом случае может быть интересна ленивая инициализация:Классы остаются в «неинициализированном» состоянии (внутренние указатели имеют значение NULL, логические значения являются ложными и т. д.) до тех пор, пока вы не вызовете один из их методов, после чего они инициализируются сами.Если вы используете эти объекты внутри функции main (или одной из функций-потомков функции main), все будет в порядке, поскольку они будут вызываться после выполнения DllMain.

3. Конечно, если некоторые глобальные объекты в DLL A зависят от глобальных объектов в DLL B, вам следует очень внимательно относиться к порядку загрузки DLL и, следовательно, к зависимостям.В этом случае библиотеки DLL с прямыми или косвенными циклическими зависимостями вызовут у вас безумное количество головной боли.Лучшее решение — разорвать циклические зависимости.

P.S.:Обратите внимание, что в C++ конструктор может выдавать исключение, и вам не нужно исключение в середине загрузки DLL, поэтому убедитесь, что ваши глобальные объекты не будут использовать исключение без очень и очень веской причины.Поскольку правильно написанные деструкторы не имеют права выдавать исключение, выгрузка 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, достигает нуля.

Его следует вызывать, когда приложение завершает работу или выгружается DLL, в зависимости от того, что наступит раньше.Обратите внимание, что это в некоторой степени зависит от фактической среды выполнения, с которой вы компилируете.

Кроме того, остерегайтесь нетривиальных деструкторов, поскольку здесь возникают проблемы как со временем, так и с порядком.Ваша DLL может быть выгружена после DLL, на которую опирается ваш деструктор, что, очевидно, вызовет проблемы.

Когда вызывается DllMain с параметром fdwReason = DLL_PROCESS_DETACH, это означает, что DLL выгружается приложением.Это время до вызова деструктора глобальных/статических объектов.

В Windows находятся бинарные файлы изображений с расширением *.exe, *.dll. PE-форматТакие файлы имеют Entry Point.Вы можете просмотреть его с помощью инструмента дампа, например

dumpbin /headers имя_dll.dll

Если вы используете время выполнения C от Microsoft, то ваша точка входа будет чем -то вроде *CRTStartup или *dllmaincrtStartup

Такие функции выполняют инициализацию среды выполнения C и C++ и делегируют выполнение (main, WinMain) или DllMain соответственно.

Если вы используете компилятор Microsoft VC, вы можете посмотреть исходный код этой функции в вашем каталоге VC:

  • crt0.c
  • dllcrt0.c

DllMainCRTStartup обрабатывает все, что необходимо для инициализации/деинициализации глобальных переменных из разделов .data в обычном сценарии, когда он получает уведомление DLL_PROCESS_DETACH во время выгрузки dll.Например:

  • main или WinMain потока запуска программы возвращает поток управления
  • вы явно вызываете FreeLibrary, а счетчик использования dll равен нулю
Лицензировано под: CC-BY-SA с атрибуция
Не связан с StackOverflow
scroll top