如何终止 DLL 中的 log4cplus?
How to terminate log4cplus in a DLL?
在我们的项目中,我们间接使用 log4cplus:它在我们静态 link 的库中使用,并且该项目通常也被编译为静态库,并且喜欢从我们的可执行文件中编译。这里的一切都是基于 Windows 和 Visual Studio。
由于我们一直遇到应用程序关闭问题,我发现我们必须 initialize log4cplus in our main()
function,这解决了问题。
但是,不幸的是,我们正在维护的应用程序基于 ACF(高级组件框架)。这意味着,静态库(links 到静态库 links 到 log4cplus)可以再次使用 DLL linked,然后由名为 合成器 在设计时。 (在 Compositor 中,我们可以创建目标应用程序 - 它使用静态库 - 以高级 "component-based" 方式......)。现在,问题是 Composer 将不再正常关闭。
关闭主程序后挂起window,我们可以看到如下调用栈:
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++
如果我删除该调用,启动是正常的,但挂起行为(即 Composer 未关闭)仍然存在,无论 threadCleanup()
、Logger::shutdown()
和 deinitialize()
(我试过所有组合)。
如何关闭 DLL 中的 log4cplus 以便应用程序可以正常终止?
正如@RbMm 的评论中提到的,在DllMain
中初始化log4cplus 不起作用的原因是该函数在"loader lock" 中执行- cf. here or here.
解决方法是在加载应用程序主线程执行的DLL中找到一个函数,并在那里初始化log4cplus。
在 ACF 的 Compositor 的情况下,首先调用的是 包导出函数 。通常,它被包装为 ACF 包中的宏,即 I_EXPORT_PACKAGE
。展开该宏允许输入以下代码:
extern "C" I_FUNCTION_EXPORT icomp::CPackageStaticInfo* I_PACKAGE_EXPORT_FUNCTION()
{
static bool bFirstLoad = true;
if (bFirstLoad)
{
log4cplus::initialize();
log4cplus::deinitialize();
bFirstLoad = false;
}
return &packageInfo;
}
在第一次调用包时初始化和取消初始化 log4cplus 使合成器应用程序能够再次正常关闭和终止。
在我们的项目中,我们间接使用 log4cplus:它在我们静态 link 的库中使用,并且该项目通常也被编译为静态库,并且喜欢从我们的可执行文件中编译。这里的一切都是基于 Windows 和 Visual Studio。
由于我们一直遇到应用程序关闭问题,我发现我们必须 initialize log4cplus in our main()
function,这解决了问题。
但是,不幸的是,我们正在维护的应用程序基于 ACF(高级组件框架)。这意味着,静态库(links 到静态库 links 到 log4cplus)可以再次使用 DLL linked,然后由名为 合成器 在设计时。 (在 Compositor 中,我们可以创建目标应用程序 - 它使用静态库 - 以高级 "component-based" 方式......)。现在,问题是 Composer 将不再正常关闭。
关闭主程序后挂起window,我们可以看到如下调用栈:
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++
如果我删除该调用,启动是正常的,但挂起行为(即 Composer 未关闭)仍然存在,无论 threadCleanup()
、Logger::shutdown()
和 deinitialize()
(我试过所有组合)。
如何关闭 DLL 中的 log4cplus 以便应用程序可以正常终止?
正如@RbMm 的评论中提到的,在DllMain
中初始化log4cplus 不起作用的原因是该函数在"loader lock" 中执行- cf. here or here.
解决方法是在加载应用程序主线程执行的DLL中找到一个函数,并在那里初始化log4cplus。
在 ACF 的 Compositor 的情况下,首先调用的是 包导出函数 。通常,它被包装为 ACF 包中的宏,即 I_EXPORT_PACKAGE
。展开该宏允许输入以下代码:
extern "C" I_FUNCTION_EXPORT icomp::CPackageStaticInfo* I_PACKAGE_EXPORT_FUNCTION()
{
static bool bFirstLoad = true;
if (bFirstLoad)
{
log4cplus::initialize();
log4cplus::deinitialize();
bFirstLoad = false;
}
return &packageInfo;
}
在第一次调用包时初始化和取消初始化 log4cplus 使合成器应用程序能够再次正常关闭和终止。