Как прекратить log4cplus в DLL? - PullRequest
1 голос
/ 11 марта 2020

В нашем проекте мы косвенно используем log4cplus: он используется в библиотеке, на которую мы статически ссылаемся, и этот проект обычно также компилируется как stati c lib и ему нравится из нашего исполняемого файла. Все здесь Windows и основано на Visual Studio.

Поскольку у нас возникла проблема с закрытием приложения, я обнаружил, что мы должны инициализировать log4cplus в нашей main() функции , которая решила проблема.

Однако поддерживаемое нами приложение, к сожалению, основано на ACF ( Advanced Component Framework ). Это означает, что stati c lib (которая ссылается на stati c lib, которая ссылается на log4cplus) может быть снова связана с DLL, которая затем загружается приложением под названием Compositor во время разработки , (В Compositor мы можем создать целевое приложение - которое использует stati c lib - высокоуровневым способом «на основе компонентов» ...). Теперь проблема в том, что 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++

Чтобы правильно закрыть Compositor, я ввел функцию 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() (я пробовал все комбинации).

Как я могу завершить log4cplus в DLL, чтобы приложение могло правильно завершиться?

1 Ответ

0 голосов
/ 24 марта 2020

Как уже упоминалось в комментариях @ RbMm, причина, по которой инициализация log4cplus в DllMain не работает, состоит в том, что эта функция выполняется внутри «блокировки загрузчика» - ср. здесь или здесь .

Решение состоит в том, чтобы найти функцию в 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 при первом вызове в пакет позволяет приложению Compositor снова закрываться и нормально завершаться.

...