Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

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

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

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

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

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

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

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

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

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

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

Ответ 3

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

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

Ответ 4

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

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

Ответ 5

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

Ответ 6

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

dumpbin/headers dllname.dll

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

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

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

  • crt0.c
  • dllcrt0.c

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

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