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

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

Ответы [ 6 ]

37 голосов
/ 17 сентября 2008

В 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, логические значения имеют значение false), пока вы не вызовете один из их методов, после чего они будут инициализировать себя. Если вы используете эти объекты внутри main (или одной из функций-потомков main), все будет в порядке, потому что они будут вызываться после выполнения DllMain.

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

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

6 голосов
/ 16 сентября 2008

Эта страница от Microsoft посвящена деталям инициализации DLL и уничтожения глобалов:
http://msdn.microsoft.com/en-us/library/988ye33t.aspx

4 голосов
/ 17 сентября 2008

Если вы хотите увидеть фактический код, который выполняется при компоновке DLL, взгляните на %ProgramFiles%\Visual Studio 8\vc\crt\src\dllcrt0.c.

Из проверки деструкторы будут вызываться через _cexit(), когда внутренний счетчик ссылок, поддерживаемый CRT DLL, достигнет нуля.

3 голосов
/ 16 сентября 2008

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

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

1 голос
/ 01 июля 2015

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

dumpbin / headers dllname.dll

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

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

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

  • crt0.c
  • dllcrt0.c

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

  • main или WinMain загрузочного потока программы возвращает поток управления
  • вы явно вызываете FreeLibrary, а use-dll-counter равен нулю
1 голос
/ 16 сентября 2008

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...