Существуют простые и даже не очень простые обстоятельства, при которых вызов LoadLibrary из DllMain совершенно безопасен.Но дизайн заключается в том, что DllMain доверяют не изменять список загруженных модулей.
Хотя наличие блокировки загрузчика действительно ограничивает то, что можно сделать в DllMain, это только косвенно относится к правилу LoadLibrary.Соответствующим назначением блокировки загрузчика является последовательный доступ к списку загруженных модулей.Хотя NTDLL работает с этим списком в одном потоке, владение блокировкой загрузчика гарантирует, что список не будет изменен кодом NTDLL, который выполняется в другом потоке.Однако блокировка загрузчика является критическим разделом.Это не делает ничего, чтобы остановить тот же поток от повторного получения блокировки загрузчика и изменения списка.
Это не имело бы значения, если бы NTDLL держался полностью при работе со списком.Однако NTDLL предусматривает вовлечение в эту работу другого кода, как при инициализации вновь загруженной DLL.Каждый раз, когда NTDLL вызывает вне себя во время работы со списком, есть выбор для дизайна.В общем, есть два варианта.Одним из них является стабилизация списка и снятие блокировки загрузчика, вызов снаружи, затем получение блокировки загрузчика и возобновление работы со списком, как с нуля, потому что внешний вызов мог изменить его.Другой - сохранить блокировку загрузчика и доверять вызываемому коду, чтобы он не делал ничего, что изменяет список.И, таким образом, LoadLibrary становится недоступным в DllMain.
Дело не в том, что блокировка загрузчика делает что-либо для остановки DllMain от вызова LoadLibrary или даже в том, что сама блокировка загрузчика делает такой вызов небезопасным.Вместо этого NTDLL доверяет DllMain удерживать блокировку загрузчика, чтобы не вызывать LoadLibrary.
Для контраста рассмотрим правило DllMain о том, что не нужно ожидать объекты синхронизации.Здесь блокировка загрузчика играет непосредственную роль в том, чтобы сделать это небезопасным.Ожидание объекта синхронизации в DllMain устанавливает возможность взаимоблокировки.Все, что нужно, - это то, что другой поток уже содержит ожидаемый объект, а затем этот другой поток вызывает любую функцию, которая будет ожидать блокировки загрузчика (например, LoadLibrary, но также и такие функции, как, казалось бы, безобидный GetModuleHandle).
Желание растянуть или нарушить правила DllMain может быть вредным или даже глупым.Тем не менее, я должен отметить, что Microsoft, по крайней мере, частично виновата в том, что люди спрашивают, насколько сильны или значимы эти правила.В конце концов, некоторые из них не всегда были задокументированы четко и убедительно, и когда я смотрел в последний раз, они все еще не были задокументированы во всех ситуациях, где они, безусловно, необходимы.(Исключение, которое я имею в виду, заключается в том, что по крайней мере до Visual Studio 2005 программистам MFC, пишущим библиотеки DLL, было приказано поместить свой код инициализации в CWinApp :: InitInstance, но не сказали, что этот код подчиняется правилам DllMain.)
Более того, для кого-то из Microsoft было бы немного богато говорить так, как будто правила DllMain должны соблюдаться без вопросов.Существуют примеры, когда собственные программисты Microsoft нарушают правила, и продолжают это делать даже после того, как считается, что нарушение правил привело к серьезным реальным проблемам.