Поделиться через


Точка входа DllMain

Необязательная точка входа в библиотеку динамической компоновки (DLL). Когда система запускает или завершает процесс или поток, она вызывает функцию точки входа для каждой загруженной библиотеки DLL с помощью первого потока процесса. Система также вызывает функцию точки входа для библиотеки DLL при ее загрузке или выгрузке с помощью функций LoadLibrary и FreeLibrary .

Пример

BOOL WINAPI DllMain(
    HINSTANCE hinstDLL,  // handle to DLL module
    DWORD fdwReason,     // reason for calling function
    LPVOID lpvReserved )  // reserved
{
    // Perform actions based on the reason for calling.
    switch( fdwReason ) 
    { 
        case DLL_PROCESS_ATTACH:
         // Initialize once for each new process.
         // Return FALSE to fail DLL load.
            break;

        case DLL_THREAD_ATTACH:
         // Do thread-specific initialization.
            break;

        case DLL_THREAD_DETACH:
         // Do thread-specific cleanup.
            break;

        case DLL_PROCESS_DETACH:
        
            if (lpvReserved != nullptr)
            {
                break; // do not do cleanup if process termination scenario
            }
            
         // Perform any necessary cleanup.
            break;
    }
    return TRUE;  // Successful DLL_PROCESS_ATTACH.
}

Это пример из функции Entry-Point библиотеки динамической компоновки.

Предупреждение

В отношении операций, выполняемых в точке входа DLL, действуют серьезные ограничения. Ознакомьтесь с общими рекомендациями для конкретных API Windows, которые небезопасны для вызова в DllMain. Если требуется сделать что-то помимо простейшей инициализации, сделайте это в функции инициализации для DLL. Вы можете потребовать, чтобы приложения вызывали функцию инициализации после выполнения DllMain и перед вызовом других функций в библиотеке DLL.

Синтаксис

BOOL WINAPI DllMain(
  _In_ HINSTANCE hinstDLL,
  _In_ DWORD     fdwReason,
  _In_ LPVOID    lpvReserved
);

Параметры

hinstDLL [in]

Дескриптор модуля DLL. Значение является базовым адресом библиотеки DLL. HINSTANCE библиотеки DLL совпадает с HMODULE библиотеки DLL, поэтому hinstDLL можно использовать в вызовах функций, требующих дескриптора модуля.

fdwReason [in]

Код причины, указывающий, почему вызывается функция точки входа DLL. Этот параметр может принимать одно из указанных ниже значений.

Значение Значение
DLL_PROCESS_ATTACH
1
Библиотека DLL загружается в виртуальное адресное пространство текущего процесса в результате запуска процесса или в результате вызова LoadLibrary. Библиотеки DLL могут использовать эту возможность для инициализации любых данных экземпляра или использования функции TlsAlloc для выделения индекса локального хранилища потока (TLS).
Параметр lpvReserved указывает, загружается ли библиотека DLL статически или динамически.
DLL_PROCESS_DETACH
0
Библиотека DLL выгружается из виртуального адресного пространства вызывающего процесса, так как она была загружена безуспешно или число ссылок достигло нуля (процессы либо завершали работу, либо вызывали FreeLibrary один раз за каждый раз, когда он назывался LoadLibrary).
Параметр lpvReserved указывает, выгружается ли библиотека DLL в результате вызова FreeLibrary , сбоя загрузки или завершения процесса.
Библиотека DLL может использовать эту возможность для вызова функции TlsFree для освобождения любых индексов TLS, выделенных с помощью TlsAlloc , и для освобождения локальных данных потока.
Обратите внимание, что поток, получающий уведомление о DLL_PROCESS_DETACH , не обязательно совпадает с потоком, который получил уведомление DLL_PROCESS_ATTACH .
DLL_THREAD_ATTACH
2
Текущий процесс создает новый поток. В этом случае система вызывает функцию точки входа всех библиотек DLL, подключенных в данный момент к процессу. Вызов выполняется в контексте нового потока. Библиотеки DLL могут использовать эту возможность для инициализации слота TLS для потока. Поток, вызывающий функцию точки входа DLL с помощью DLL_PROCESS_ATTACH , не вызывает функцию точки входа DLL с DLL_THREAD_ATTACH.
Обратите внимание, что функция точки входа библиотеки DLL вызывается с этим значением только потоками, созданными после загрузки библиотеки DLL процессом. При загрузке библиотеки DLL с помощью LoadLibrary существующие потоки не вызывают функцию точки входа только что загруженной библиотеки DLL.
DLL_THREAD_DETACH
3
Поток завершается чисто. Если библиотека DLL сохранила указатель на выделенную память в слоте TLS, она должна использовать эту возможность для освобождения памяти. Система вызывает функцию точки входа всех загруженных в данный момент библиотек DLL с этим значением. Вызов выполняется в контексте выходного потока.

lpvReserved [in]

Если fdwReason имеет значение DLL_PROCESS_ATTACH, lpvReserved имеет значение NULL для динамических нагрузок и не null для статических нагрузок.

Если fdwReasonDLL_PROCESS_DETACH,lpvReserved имеет значение NULL , если был вызван FreeLibrary или произошел сбой загрузки БИБЛИОТЕКи DLL, и значение, отличное от NULL , если процесс завершается.

Возвращаемое значение

Когда система вызывает функцию DllMain со значением DLL_PROCESS_ATTACH , функция возвращает значение TRUE в случае успешного выполнения или FALSE в случае сбоя инициализации. Если возвращаемое значение равно FALSE при вызове DllMain , так как процесс использует функцию LoadLibrary , LoadLibrary возвращает значение NULL. (Система немедленно вызывает функцию точки входа с DLL_PROCESS_DETACH и выгружает библиотеку DLL.) Если при вызове DllMain во время инициализации процесса возвращается значение FALSE, процесс завершается с ошибкой. Дополнительные сведения об ошибке можно получить, вызвав GetLastError.

Когда система вызывает функцию DllMain с любым значением, кроме DLL_PROCESS_ATTACH, возвращаемое значение игнорируется.

Комментарии

DllMain — это заполнитель для имени функции, определяемой библиотекой. Необходимо указать фактическое имя, используемое при сборке библиотеки DLL. Дополнительные сведения см. в документации по средствам разработки.

Во время начального запуска процесса или после вызова LoadLibrary система проверяет список загруженных библиотек DLL для процесса. Для каждой библиотеки DLL, которая еще не была вызвана со значением DLL_PROCESS_ATTACH , система вызывает функцию точки входа библиотеки DLL. Этот вызов выполняется в контексте потока, который вызвал изменение адресного пространства процесса, например основного потока процесса или потока, вызвавшего LoadLibrary. Доступ к точке входа сериализуется системой на уровне всего процесса. Потоки в DllMain удерживают блокировку загрузчика, поэтому дополнительные библиотеки DLL не могут быть динамически загружены или инициализированы.

Если функция точки входа библиотеки DLL возвращает значение FALSE после уведомления DLL_PROCESS_ATTACH , она получает уведомление DLL_PROCESS_DETACH и библиотека DLL немедленно выгружается. Однако если код DLL_PROCESS_ATTACH создает исключение, функция точки входа не получит уведомление о DLL_PROCESS_DETACH .

Существуют случаи, когда функция точки входа вызывается для завершающего потока, даже если функция точки входа никогда не вызывалась с DLL_THREAD_ATTACH для потока:

  • Поток был начальным потоком в процессе, поэтому система назвала функцию точки входа со значением DLL_PROCESS_ATTACH .
  • Поток уже выполнялся при вызове функции LoadLibrary , поэтому система никогда не вызывала для нее функцию точки входа.

Если библиотека DLL выгружается из процесса в результате неудачной загрузки библиотеки DLL, завершения процесса или вызова FreeLibrary, система не вызывает функцию точки входа библиотеки DLL со значением DLL_THREAD_DETACH для отдельных потоков процесса. Библиотека DLL отправляет только уведомление DLL_PROCESS_DETACH . Библиотеки DLL могут воспользоваться этой возможностью, чтобы очистить все ресурсы для всех потоков, известных библиотеке DLL.

При обработке DLL_PROCESS_DETACH библиотека DLL должна освобождать ресурсы, такие как память кучи, только если библиотека DLL выгружается динамически (параметр lpvReserved имеет значение NULL). Если процесс завершается (параметр lpvReserved имеет значение, отличное от NULL), все потоки в процессе, кроме текущего, либо уже завершились или были явно завершены вызовом функции ExitProcess , что может оставить некоторые ресурсы процесса, такие как кучи, в несогласованном состоянии. В этом случае библиотека DLL небезопасна для очистки ресурсов. Вместо этого библиотека DLL должна позволить операционной системе освободить память.

Если процесс завершается вызовом TerminateProcess или TerminateJobObject, библиотеки DLL этого процесса не получают DLL_PROCESS_DETACH уведомлений. Если завершить поток вызовом TerminateThread, библиотеки DLL этого потока не будут получать уведомления DLL_THREAD_DETACH .

Функция точки входа должна выполнять только простые задачи инициализации или завершения. Он не должен вызывать функцию LoadLibrary или LoadLibraryEx (или функцию, которая вызывает эти функции), так как это может создавать циклы зависимостей в порядке загрузки DLL. Это может привести к использованию библиотеки DLL до выполнения системой кода инициализации. Аналогичным образом, функция точки входа не должна вызывать функцию FreeLibrary (или функцию, которая вызывает FreeLibrary) во время завершения процесса, так как это может привести к использованию библиотеки DLL после выполнения системой кода завершения.

Так как Kernel32.dll гарантированно загружается в адресное пространство процесса при вызове функции точки входа, вызов функций в Kernel32.dll не приводит к использованию библиотеки DLL до выполнения кода инициализации. Таким образом, функция точки входа может вызывать функции в Kernel32.dll, которые не загружают другие библиотеки DLL. Например, DllMain может создавать объекты синхронизации , такие как критические разделы и мьютексы, и использовать TLS. К сожалению, полный список безопасных функций в Kernel32.dll отсутствует.

Вызов функций, для которых требуются библиотеки DLL, отличные от Kernel32.dll, может привести к проблемам, которые трудно диагностировать. Например, вызов функций User, Shell и COM может привести к ошибкам нарушения доступа, так как некоторые функции загружают другие системные компоненты. И наоборот, вызов таких функций во время завершения может привести к ошибкам нарушения доступа, так как соответствующий компонент, возможно, уже выгружен или не инициализирован.

Так как уведомления DLL сериализуются, функции точки входа не должны пытаться взаимодействовать с другими потоками или процессами. В результате могут возникнуть взаимоблокировки.

Сведения о рекомендациях по написанию библиотеки DLL см. в статье Рекомендации по библиотекам динамической компоновки.

Если библиотека DLL связана с библиотекой времени выполнения C (CRT), то точка входа, предоставляемая CRT, вызывает конструкторы и деструкторы для глобальных и статических объектов C++. Поэтому эти ограничения для DllMain также применяются к конструкторам и деструкторам, а также к любому коду, вызываемому из них.

Рекомендуется вызывать DisableThreadLibraryCalls при получении DLL_PROCESS_ATTACH, если библиотека DLL не связана со статической библиотекой времени выполнения C (CRT).

Требования

Требование Значение
Минимальная версия клиента
Windows XP [только классические приложения]
Минимальная версия сервера
Windows Server 2003 [только классические приложения]
Заголовок
Process.h

См. также раздел

Функция Entry-Point библиотеки динамической компоновки

Функции библиотеки динамической компоновки

FreeLibrary

GetModuleFileName

LoadLibrary

TlsAlloc

TlsFree

DisableThreadLibraryCalls