--- симптомы
Когда я загружаю свою Dll из подпотока хост-приложения, и хост-приложение закрывается, при вызове Dll_PROCESS_DETACH
остается только 1 поток. Это плохо. Это вызывает утечку памяти, и требуемая очистка не может быть выполнена.
Когда я загружаю свою Dll из MAIN-потока хост-приложения, и хост-приложение закрывается,
все потоки, созданные в Dll, все еще работают при вызове Dll_PROCESS_DETACH
.
Это хорошо, потому что я могу сделать всю необходимую работу по очистке.
Мой Dll_PROCESS_ATTACH
НЕ содержит кода. Поток не создается, функция API не вызывается.
-- цель этой Dll, вариант использования
Мне нужна Dll, которая может работать в различных хост-приложениях, где я не знаю, когда именно моя Dll загружается и выгружается.
Некоторые из этих хост-приложений, очевидно, загрузить мою Dll из потока, например. этот поток запускает сценарий, и сценарий использует экспортированные функции моей библиотеки DLL.
Общая проблема заключается в следующем: когда Dll впервые загружается из подпотока хост-приложения, она не выгружается должным образом, потому что все потоки, казалось, были удалены при вызове Dll_PROCESS_DETACH
. Это не только вызывает утечку памяти, но и не может выполнять некоторую внутреннюю работу по очистке, останавливая потоки и выполняя окончательное соединение сокета, используемое для связи с сервером.
Все это прекрасно работает, когда Dll загружается из основного потока (на моем тестовом хосте) или в конкретных тестируемых хост-приложениях.
Две трассировки стека сеансов отладки C++ Dll, работающей в Visual Studio 17. Первый — плохой, когда Dll загружается из подпотока.
Второй - хороший, где Dll загружается из основного потока.
// exit stack Dll Dll loaded in subthread ; breakpoint in `Dll_PROCESS_DETACH` This is BAD
DllTest.dll!DllTest_app::~DllTest_app() Line 176 C++
[External Code]
DllTest.dll!DllTest_app::destroy() Line 208 C++
DllTest.dll!DllMain(HINSTANCE__ * hModule, unsigned long ul_reason_for_call, void * lpReserved) Line 43 C++
[External Code]
DllTest_test.exe!exit_or_terminate_process(const unsigned int return_code) Line 130 C++
DllTest_test.exe!common_exit(const int return_code, const _crt_exit_cleanup_mode cleanup_mode, const _crt_exit_return_mode return_mode) Line 271 C++
DllTest_test.exe!exit(int return_code) Line 283 C++
[External Code]
// exit stack Dll loaded in mainthread; breakpoint in `Dll_PROCESS_DETACH` This is GOOD
DllTest.dll!DllTest_app::~DllTest_app() Line 175 C++
[External Code]
DllTest.dll!DllTest_app::destroy() Line 208 C++
DllTest.dll!DllMain(HINSTANCE__ * hModule, unsigned long ul_reason_for_call, void * lpReserved) Line 43 C++
[External Code]
DllTest_test.exe!DllTestWrap::Unload(int code, int bdeleteerror) Line 139 C++
DllTest_test.exe!DllTestWrap::~DllTestWrap() Line 66 C++
[External Code]
DllTest_test.exe!_execute_onexit_table::__l22::<lambda>() Line 198 C++
DllTest_test.exe!__crt_seh_guarded_call<int>::operator()<void <lambda>(void),int <lambda>(void) & __ptr64,void <lambda>(void) >(__acrt_lock_and_call::__l3::void <lambda>(void) && setup, _execute_onexit_table::__l22::int <lambda>(void) & action, __acrt_lock_and_call::__l4::void <lambda>(void) && cleanup) Line 199 C++
DllTest_test.exe!__acrt_lock_and_call<int <lambda>(void) >(const __acrt_lock_id lock_id, _execute_onexit_table::__l22::int <lambda>(void) && action) Line 882 C++
DllTest_test.exe!_execute_onexit_table(_onexit_table_t * table) Line 222 C++
DllTest_test.exe!common_exit(const int return_code, const _crt_exit_cleanup_mode cleanup_mode, const _crt_exit_return_mode return_mode) Line 211 C++
DllTest_test.exe!exit(int return_code) Line 283 C++
[External Code]
Как добиться того, чтобы при вызове Dll_PROCESS_DETACH все потоки, созданные в Dll, продолжали работать и не завершались, независимо от того, загружена ли Dll из подпотока или основного потока хост-приложения.
Есть ли что-то, что можно сделать с помощью настроек компилятора или компоновщика, или обходной путь?
Заранее благодарю за каждую подсказку.
Имя DllTest_app неудачное, извините. Это просто имя объекта, созданного при первом вызове экспортируемой функции DLL. Это не сама DLL. Но этот объект создает потоки, которые должны завершиться обычным образом. Но это невозможно, и в этом проблема. Пожалуйста, загрузите очень маленький образец проекта по ссылке, где я мог бы воспроизвести эту проблему:
Пример проекта: filedropper.com/testdll2_1 Как использовать: создать папку c:\debuglib проект сборки (win64, отладка) Запустить. Нажмите «Загрузить DLL, создайте поток в DLL». Нажмите «Выгрузить DLL». В файле dllmain.cpp DLL_PROCESS_DETACH не вызывается! Когда вы закрываете приложение, вызывается DLL_PROCESS_DETACH, но все потоки (dll_test_thread_function) завершаются. Это проблема. Спасибо за помощь.
Похоже, что один из потоков в вашем приложении вызывает exit(), что приводит к вызову ExitProcess() API - https://docs.microsoft.com/en-us/windows/win32/api/processthreadsapi/nf-processthreadsapi-exitprocess
Как описано в документации ExitProcess, он завершает все потоки, а затем вызывает DllMain() с DLL_PROCESS_DETACH.
Вызов ExitProcess также может привести к взаимоблокировке, если завершенные потоки содержат мьютекс, который пытается получить последний поток, выполняющий DLL_PROCESS_DETACH.
Функция уничтожения просто уничтожает объект при вызове DLL_PROCESS_DETACH. Этот объект создается при первом вызове экспортируемой функции. Итак, вне DllMain. Этот объект выполняет всю работу, которую необходимо выполнить в моей DLL. Он запускает несколько потоков, устанавливает соединение с сокетом и так далее. Уничтожение не уничтожает Dll, это невозможно, потому что хост-приложение делает это при вызове FreeLibrary. Но, похоже, поскольку мой объект запускает поток, вызывается DLL_PROCESS_DETACH, когда ВСЕ потоки просто удаляются, не давая мне возможности очистить.