ilyachalov (ilyachalov) wrote,
ilyachalov
ilyachalov

Category:

Функция точки входа DLL

Начало тут:
1. динамически подключаемые библиотеки;
2. о динамически подключаемых библиотеках подробнее;
3. преимущества динамического связывания;
4. создание динамически подключаемой библиотеки.

Перевод с английского статьи от 31.05.2018 г. «Dynamic-Link Library Entry-Point Function»:
https://docs.microsoft.com/ru-ru/windows/win32/dlls/dynamic-link-library-entry-point-function
(На данный момент на этом сайте нет перевода этой статьи на русский, есть только версия на английском.)

Функция точки входа для DLL необязательна, но может быть определена. Если эта функция присутствует, операционная система вызывает эту функцию точки входа всякий раз, когда процесс или поток выполнения приложения загружает или выгружает DLL. Эта функция может использоваться для выполнения задач простых инициализации и очистки. Например, эта функция может создать локальное хранилище потока [thread local storage] при создании нового потока и ликвидировать его при завершении работы потока.

Если вы компонуете свою DLL с библиотекой времени выполнения для языка Си [C run-time library], то эта библиотека времени выполнения может создать функцию точки входа за вас, при этом вы можете написать свою отдельную функцию инициализации. Для более подробной информации об этом читайте документацию по вашей библиотеке времени выполнения [полезная статья по этому вопросу: «Библиотеки DLL и поведение библиотеки времени выполнения Visual C++»].

Если вы хотите писать свою собственную функцию точки входа, используйте для этого функцию DllMain. Название DllMain является умолчательной заглушкой для функции точки входа, определяемой пользователем. Вы должны указать действительное имя функции точки входа, которое вы используете, при сборке своей DLL. Более подробную информацию об этом ищите в документации, поставляемой с вашими инструментами разработки [полезные статьи по этому вопросу: «Библиотеки DLL и поведение библиотеки времени выполнения Visual C++», «Параметр /ENTRY компоновщика»].

Ситуации, в которых вызывается функция точки входа

Операционная система вызывает функцию точки входа всякий раз, когда происходит одно из следующих событий:
  • процесс загружает DLL. Для процессов, использующих динамическое связывание во время запуска программы, DLL загружается во время инициализации процесса. Для процессов, использующих динамическое связывание во время выполнения программы, DLL загружается перед тем, как функция LoadLibrary или LoadLibraryEx возвратит значение [завершит свою работу];

  • процесс выгружает DLL. DLL выгружается, когда процесс завершается или вызывает функцию FreeLibrary и счетчик ссылок по данной DLL становится равным нулю. Если процесс завершается из-за вызова функций TerminateProcess или TerminateThread, операционная система не вызывает функцию точки входа DLL;

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

  • поток выполнения процесса, загрузившего DLL, завершается нормально (то есть не из-за вызова функций TerminateThread или TerminateProcess). Когда процесс выгружает DLL, функция точки входа вызывается только один раз для всего процесса, а не по разу на каждый существующий поток выполнения процесса. Можно использовать функцию DisableThreadLibraryCalls, чтобы отключить уведомления о завершении потоков выполнения.

Только один поток выполнения за раз может вызвать функцию точки входа.

Операционная система вызывает функцию точки входа в контексте процесса или потока выполнения, ставшего причиной вызова функции. Такой подход разрешает DLL использовать ее функцию точки входа для резервирования памяти в виртуальном адресном пространстве вызывающего процесса или открывать дескрипторы, доступные процессу. Функция точки входа может также резервировать память, которая является собственной [то есть закрытой для других] для нового потока выполнения посредством использования локального хранилища потока [thread local storage (TLS)]. Более подробную информацию о локальном хранилище потока ищите в статье «Локальное хранилище потока».

Написание функции точки входа

Функция точки входа DLL должна быть объявлена с применением соглашения о вызове stdcall [для операционных систем компании Microsoft — __stdcall]. Если точка входа DLL не будет объявлена правильно, DLL не загрузится, а операционная система выдаст сообщение, указывающее, что точка входа DLL должна быть объявлена с применением макроса WINAPI.

В теле функции мы можем управлять в любой комбинации следующими вариантами, в которых вызывается функция точки входа DLL:
  • процесс загружает DLL (эта причина вызова функции точки входа DLL обозначается константой DLL_PROCESS_ATTACH);

  • текущий процесс создает новый поток выполнения (DLL_THREAD_ATTACH);

  • поток выполнения завершается нормально (DLL_THREAD_DETACH);

  • процесс выгружает DLL (DLL_PROCESS_DETACH).

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

Из-за того, что системная библиотека Kernel32.dll гарантированно будет загружена в адресное пространство процесса, когда вызывается функция точки входа, вызов функций из Kernel32.dll не приведет к тому, что DLL будет использована раньше, чем выполнится код ее инициализации. Поэтому функция точки входа может создавать объекты синхронизации, такие как критические секции и мьютексы, и использовать TLS [локальное хранилище потока], потому что эти функции размещены в Kernel32.dll. Не является безопасным, к примеру, вызов функций реестра [операционной системы], потому что они размещены в системной библиотеке Advapi32.dll.

Вызов других функций может привести к проблемам, которые трудно диагностировать. Например, вызов функций [компонентов операционной системы Windows] «User» [функции для работы с пользовательским интерфейсом, окнами], «Shell» [функции для работы с командной оболочкой операционной системы] и функций COM [Component Object Model, по-русски «модель компонентного объекта»] может послужить причиной ошибок нарушения доступа из-за того, что некоторые функции в их DLL вызывают функцию LoadLibrary, чтобы загрузить другие компоненты операционной системы. И, наоборот, вызов этих функций во время завершения [процесса или потока выполнения] может послужить причиной ошибок нарушения доступа из-за того, что соответствующий компонент мог уже быть выгружен или не инициализирован.

Следующий пример демонстрирует примерную структуру функции точки входа DLL.
BOOL WINAPI DllMain(
    HINSTANCE hinstDLL,  // дескриптор модуля DLL
    DWORD fdwReason,     // причина вызова функции
    LPVOID lpReserved )  // параметр с дополнительной информацией
{
    // выполнить некие действия в зависимости от причины вызова функции
    switch( fdwReason ) 
    { 
        case DLL_PROCESS_ATTACH:
         // инициализация для каждого нового процесса;
         // возврат значения FALSE, если не получается загрузить DLL
            break;

        case DLL_THREAD_ATTACH:
         // выполнить инициализацию для потока выполнения
            break;

        case DLL_THREAD_DETACH:
         // произвести очистку для потока выполнения
            break;

        case DLL_PROCESS_DETACH:
         // выполнить любую необходимую очистку
            break;
    }
    return TRUE;  // успешная отработка функции по причине DLL_PROCESS_ATTACH
}

Возвращаемое значение функции точки входа

Когда функция точки входа DLL вызывается по причине загрузки процесса, эта функция возвращает значение TRUE, чтобы показать, что она отработала успешно. Для процессов, использующих динамическое связывание во время запуска программы, возврат значения FALSE повлечет за собой неудачу инициализации процесса и его завершение. Для процессов, использующих динамическое связывание во время выполнения программы, возврат значения FALSE повлечет за собой то, что функция LoadLibrary или LoadLibraryEx возвратит значение NULL, указывающее на то, что эта функция свою работу не выполнила по причине некой ошибки. (Операционная система сразу вызовет нашу функцию точки входа с причиной вызова DLL_PROCESS_DETACH и выгрузит DLL.) Возвращаемое значение функции точки входа оставляется без внимания, когда функция вызывается по любой другой причине, кроме DLL_PROCESS_ATTACH.
Tags: Английский язык, Образование, Программирование
Subscribe

  • Post a new comment

    Error

    Anonymous comments are disabled in this journal

    default userpic

    Your IP address will be recorded 

  • 0 comments