Как принудительно установить порядок уничтожения статических объектов в разных dll?
-
22-08-2019 - |
Вопрос
у меня есть 2 статический объекты в 2 разные библиотеки DLL:
Объект Ресурсы (который является синглтоном) и объект Пользователь.Пользователь объекта в его деструкторе должен получить доступ к ресурсам объекта.
Как я могу заставить ресурсы объекта не уничтожаться перед пользователем объекта?
Решение
Глобальные объекты уничтожаются при выгрузке соответствующей им DLL.Итак, поскольку ваша dll «Пользователь», вероятно, зависит от вашей dll «Ресурс», у вас проблемы:«ресурс» всегда будет уничтожен перед «пользователем».
Меня также интересует хороший ответ на этот вопрос, если он существует.До сих пор я использую функцию очистки, которая должна вызываться приложением перед его завершением, и сохраняю в деструкторах только безвредный код.
Другие советы
Если вы можете поместить эти две глобальные переменные в одну и ту же DLL, это уже другая история.Как сообщил Джем в своем ответе, порядок отсоединения DLL не гарантируется системой.Поэтому у вас может возникнуть большая проблема при наличии двух отдельных Dll.Я не системный гуру Windows, но, посмотрев в Google, я нашел блоггеров msdn, которые рассказали, что у них была такая же проблема, но не было хорошего решения для ее решения.
Если вы можете поместить их в одну и ту же DLL, по моему мнению, решение проще, в этом случае вам не нужно решать проблему «негарантированного порядка отсоединения DLL» (неразрешимую, насколько я понимаю).
Но тогда вам все равно нужно решить новую проблему:Порядок уничтожения глобальных переменных не гарантируется языком C++.Но с этим можно справиться:
вам нужно использовать какой-то эталонный расчет.boost::shared_ptr может помочь.
Объявите его глобальным и определите его следующим образом:
boost::shared_ptr my_resource_ptr ( new Resource() ); // new operator is important here!
Затем вам нужно изменить реализацию пользователя на магазин свой собственныйshared_ptr:
class User
{
...
boost::share_ptr a_resource_ptr;
...
};
Пока все ваши экземпляры User не уничтожены, они будут «сохранять» экземпляр Resource и, таким образом, предотвращать его преждевременное удаление, даже если глобальный Shared_ptr мог быть уничтожен.
Последний уничтожаемый экземпляр User (косвенно) удалит экземпляр Resource.
Какой бы метод подсчета ссылок вы ни использовали, ComPtr, ваш собственный, он должен сработать.
Я не думаю, что вы можете изменить порядок уничтожения глобалов, находящихся в разных модулях.Есть ли шанс добавить подсчет ссылок?
Если вы действительно хотите получить две отдельные библиотеки Dll, у меня может быть для вас несколько подсказок:вы можете рассмотреть возможность использования FreeLibrary()
из Windows API.Как заявил MSDN FreeLibrary()
уменьшает счетчик ссылок для Dll, который выгружается, когда счетчик достигает 0.
Недостаток:с использованием FreeLibrary()
подразумевает, что вы загружаете его с LoadLibrary()
(ссылка на MSDN) и вызов функции из этой библиотеки подразумевает, что вы используете GetProcAddress()
функция, что может привести к действительно уродливому коду.И это также может подразумевать некоторые изменения в вашем коде - например, получение глобальной переменной, указывающей на функции Dll, для хранения адреса каждой функции...
Если вы хотите реализовать это:
- вы должны загрузить и освободить библиотеку из своего
main()
функция вашего процесса, - а также загрузить и освободить библиотеку из Dll, реализующую класс User.Реализовать это в
DllMain()
функция для этой Dll, когда причина в томDLL_PROCESS_DETACH
(видеть Основная ссылка на Dll mdsn.
Таким образом, библиотека «Ресурс» будет выгружена только после того, как библиотека «Пользователь» завершит ее работу.
Попробуйте, если хотите, и скажите мне, работает ли это, поскольку я никогда этого не реализовал.
пс:Я опубликовал второй ответ на ваш вопрос, чтобы провести значимое разделение между двумя ответами, поскольку я (пытаюсь) подробно описать их оба.Я не хочу, чтобы вы их перепутали и запутались...