Как сегодня уже все знают, вам не следует делать ничего даже отдалённо интересного в своей функции DllMain (*). Олег Львович написал об этом две очень хорошие статьи: одна о том, как всё это работает, а вторая - что может пойти не так (**).
А вот и ещё одна причина, почему в DllMain не следует делать ничего интересного: часто библиотеку загружают только временно, без вызова её полной функциональности. Например, кто-нибудь может использовать вашу библиотеку вот так:
// Проверка ошибок убрана для простоты чтения Lib := LoadLibrary('you'); Ico := LoadIcon(Lib, MAKEINTRESOURCE(5)); FreeLibrary(Lib);Этот код просто хочет от вас иконку. Он был бы весьма удивлён (а, может быть, даже расстроен), если бы ваша DLL в это время начала делать что-то тяжеловесное. Например, запускать таймер или поток.
(Да, конкретно этого можно избежать, используя LoadLibraryEx с LOAD_LIBRARY_AS_DATAFILE, но сейчас речь не об этом).
Другим случаем, когда библиотека загружается, но не используется - когда она загружается как чья-либо зависимость. Предположим, что 'middle' - это имя какой-то промежуточной DLL, которая статически ссылается на вашу DLL:
Lib := LoadLibrary('middle'); Proc := GetProcAddress(Lib, 'SomeFunction'); Proc(...); FreeLibrary(Lib);Когда загружается библиотека 'middle', ваша DLL будет также загружена и инициализирована. Получается, что ваша инициализация выполняется, даже если процедура SomeFunction не использует вашу DLL.
Этот сценарий "кратковременной загрузки DLL" вообще-то довольно распространён. Например, когда кто-то вызывает "Regsvr32 middle.dll", то при этом загружается DLL middle для вызова её функции DllRegisterServer, которая обычно не выполняет никакой работы, кроме как несколько манипуляций с реестром. Почти наверняка она не вызывает вашу DLL.
Ещё один пример - открытие Панели Управления. Панель Управления загружает каждый *.cpl файл для вызова их функций CplApplet для определения иконки для отображения. Опять-таки, обычно для этого ваша DLL не нужна.
И ни при каких обстоятельствах вы не должны создавать объекты с привязкой к потокам (thread-affinity) в своём обработчике DLL_PROCESS_ATTACH (***). Вы не сможете проконтролировать, какой поток отправит DLL_PROCESS_ATTACH, а какой - DLL_PROCESS_DETACH. Поток, который отправлял сообщение DLL_PROCESS_ATTACH, может завершиться сразу после загрузки вашей DLL. Тогда любой объект с привязкой к нему перестанет работать, потому что его владелец завершил работу.
А даже, если поток продолжит работу, то нет никаких гарантий, что FreeLibrary вызовет тот же поток, который вызывал LoadLibrary. Поэтому вы не сможете очистить объекты, привязанные к потоку в обработчике DLL_PROCESS_DETACH, поскольку вы будете оперировать не с тем потоком.
И абсолютно ни при каких обстоятельствах вам не следует делать ничего такого сумасшедшего, как создание окна внутри DLL_PROCESS_ATTACH. В дополнение к проблемам привязок к потокам, тут добавляется проблема глобальных ловушек (global hooks). Хуки, работающие при блокировке загрузчика ОС, - это верный рецепт катастрофы. Поэтому не удивляйтесь мёртвым блокировкам на своей машине после такого.
Завтра будут ещё примеры.
Примечания переводчика:
(*) В Delphi DllMain доступна вам через переменные DllProc(Ex). Заметим, что в DLL Delphi в DllMain выполняются секции initialization и finalization всех модулей (unit), а также код между begin и end в dpr файле. Пакеты Delphi работают по-другому.
(**) Ещё можно почитать mgrier's WebLog.
(***) Например, создание объекта в глобальной threadvar-переменной.
Комментариев нет:
Отправить комментарий
Можно использовать некоторые HTML-теги, например:
<b>Жирный</b>
<i>Курсив</i>
<a href="http://www.example.com/">Ссылка</a>
Вам необязательно регистрироваться для комментирования - для этого просто выберите из списка "Анонимный" (для анонимного комментария) или "Имя/URL" (для указания вашего имени и ссылки на сайт). Все прочие варианты потребуют от вас входа в вашу учётку.
Пожалуйста, по возможности используйте "Имя/URL" вместо "Анонимный". URL можно просто не указывать.
Ваше сообщение может быть помечено как спам спам-фильтром - не волнуйтесь, оно появится после проверки администратором.
Примечание. Отправлять комментарии могут только участники этого блога.