【发布时间】:2020-03-11 10:21:30
【问题描述】:
在我们的项目中,我们间接使用 log4cplus:它被用于我们静态链接到的库中,并且该项目通常也被编译为静态库,并且喜欢从我们的可执行文件中编译。这里的一切都是基于 Windows 和 Visual Studio 的。
由于我们一直遇到应用程序关闭的问题,我发现我们必须initialize log4cplus in our main() function,这解决了问题。
但是,不幸的是,我们正在维护的应用程序基于 ACF(高级组件框架)。这意味着,静态库(链接到链接到 log4cplus 的静态库)可以再次与 DLL 链接,然后在设计时由名为 Compositor 的应用程序加载该 DLL。 (在 Compositor 中,我们可以以高级“基于组件”的方式创建目标应用程序 - 它使用静态库......)。现在,问题是 Compositer 不能再正常关闭了。
当它在关闭主窗口后挂起时,我们可以看到如下调用堆栈:
ntdll.dll!NtWaitForAlertByThreadId() + 20 bytes Unknown
ntdll.dll!RtlSleepConditionVariableSRW() + 265 bytes Unknown
KernelBase.dll!SleepConditionVariableSRW() + 45 bytes Unknown
msvcp140.dll!__crtSetThreadpoolWait() + 80 bytes Unknown
msvcp140.dll!_Cnd_timedwait() + 396 bytes Unknown
msvcp140.dll!_Cnd_timedwait() + 84 bytes Unknown
log4cplusUx64.dll!log4cplus::helpers::getFileInfo() + 3473 bytes Unknown
log4cplusUx64.dll!00007ff86917fefb() Unknown
ucrtbase.dll!_execute_onexit_table() + 342 bytes Unknown
ucrtbase.dll!_execute_onexit_table() + 123 bytes Unknown
ucrtbase.dll!_execute_onexit_table() + 52 bytes Unknown
log4cplusUx64.dll!log4cplus::helpers::getFormattedTime() + 5056 bytes Unknown
log4cplusUx64.dll!log4cplus::helpers::getFormattedTime() + 5364 bytes Unknown
ntdll.dll!RtlAnsiStringToUnicodeString() + 663 bytes Unknown
ntdll.dll!LdrShutdownProcess() + 300 bytes Unknown
ntdll.dll!RtlExitUserProcess() + 173 bytes Unknown
kernel32.dll!ExitProcess() + 10 bytes Unknown
ucrtbase.dll!exit() + 468 bytes Unknown
ucrtbase.dll!exit() + 127 bytes Unknown
> Compositor.exe!__scrt_common_main_seh() Line 295 C++
为了让合成器正常关闭,我引入了一个DllMain函数:
BOOL WINAPI DllMain(HINSTANCE, DWORD fdwReason, LPVOID)
{
switch (fdwReason)
{
case DLL_PROCESS_ATTACH:
log4cplus::initialize();
break;
case DLL_THREAD_ATTACH:
break;
case DLL_THREAD_DETACH:
log4cplus::threadCleanup();
break;
case DLL_PROCESS_DETACH:
log4cplus::Logger::shutdown();
log4cplus::deinitialize();
break;
}
return TRUE;
}
现在,应用程序将不再启动,而是在调用 log4cplus::initialize() 时挂起:
ntdll.dll!NtWaitForAlertByThreadId() + 20 bytes Unknown
ntdll.dll!RtlSleepConditionVariableSRW() + 265 bytes Unknown
KernelBase.dll!SleepConditionVariableSRW() + 45 bytes Unknown
msvcp140.dll!__crtSetThreadpoolWait() + 80 bytes Unknown
msvcp140.dll!_Cnd_timedwait() + 396 bytes Unknown
msvcp140.dll!_Cnd_timedwait() + 84 bytes Unknown
log4cplusUx64.dll!00007ff8697360d0() Unknown
log4cplusUx64.dll!00007ff86973625f() Unknown
log4cplusUx64.dll!log4cplus::spi::FactoryRegistry<log4cplus::spi::LocaleFactory>::FactoryRegistry<log4cplus::spi::LocaleFactory>() + 1438 bytes Unknown
log4cplusUx64.dll!log4cplus::initialize() + 194 bytes Unknown
> MePiaPck.arp!DllMain(HINSTANCE__ * __formal, unsigned long fdwReason, void * __formal) Line 46 C++
如果我删除该调用,启动是正常的,但挂起行为,即 Compositer 未关闭,仍然存在,无论 threadCleanup()、Logger::shutdown() 和 deinitialize()(我已经尝试了所有组合)。
如何关闭 DLL 中的 log4cplus 以便应用程序可以正常终止?
【问题讨论】:
-
当看到提到 ACF 时,我能理解投反对票的冲动......
-
谁在写
log4cplusUx64.dll?这里存在一些挂在析构函数中的全局对象(这是显示您的第一个调用堆栈 - 如果您使用 pdb 符号将更有用)。为什么不使用 globalinit.cxx 中的DllMain?在thread_callback中阅读 cmets。但是很明显,此 dll 代码设计仅适用于在进程启动时加载 dll(不是通过 LoadLibrary)并且从不卸载的情况。initialize()和deinitialize();并非设计为从加载程序锁运行。可能的解决方案为 init/deinit 创建 2 个导出函数,并在 main 开始和结束时从 exe 调用它。 -
花了我一段时间来解读您的评论。谢谢,我会尝试您的最终建议,尽管我的目标是不触摸 Compositer 代码 - 甚至不确定我我被允许了。
-
从快速查找 log4cplus src 代码中我了解到
log4cplus::initialize()并非设计为在加载程序锁内执行 - 它在这里死锁。还有deinitialize()也不是设计。从您的调用堆栈中,我看到您在log4cplusUx64.dll中有一些全局对象,它破坏了死锁(也许它称为deinitialize()- 你需要使用pdb 符号和调用堆栈 - 会提供更多信息)。由于解决方案需要在 dll 中导出函数并从中调用初始化/取消初始化,并且 dll 中没有全局对象,因此在析构函数中调用deinitialize -
@Matz 我目前正在评估 ACF(高级组件框架)。网上资料很少。你能分享一些你的经验吗?