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

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

Стоит ли изучать PHP в 2026-2027 годах?
Стоит ли изучать PHP в 2026-2027 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
41
0
44 220
6
Перейти к ответу Данный вопрос помечен как решенный

Ответы 6

Он должен вызываться либо при завершении приложения, либо при выгрузке библиотеки 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-- 30.10.2013 01:39

@ LB--: Обычно нет: у каждого процесса есть своя собственная глобальная переменная пока не, вы можете сопоставить их в общей памяти или использовать особый трюк ОС, чтобы сделать их общими для всех процессов, использующих эту DLL (я точно не помню трюк в Windows, но он включал объявления #pragma, IIRC)

paercebal 30.10.2013 23:15

В 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 равен нулю

Другие вопросы по теме