>
>
>
V718. The 'Foo' function should not be …


V718. The 'Foo' function should not be called from 'DllMain' function.

Внутри функции DllMain() нельзя вызывать многие функции, так как это может привести к зависанию приложения или иным ошибкам. Именно такой опасный вызов функции и был обнаружен анализатором.

Ситуация с DllMain хорошо описана в статье на сайте MSDN: Dynamic-Link Library Best Practices. Процитируем из неё некоторые фрагменты:

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

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

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

В любом случае никогда не выполняйте следующие задачи в пределах функции DllMain:

  • Вызов LoadLibrary или LoadLibraryEx (напрямую или косвенно). Это может привести к взаимной блокировке потоков или аварийному завершению программы.
  • Вызов GetStringTypeA, GetStringTypeEx или GetStringTypeW (напрямую или косвенно). Это может привести к взаимной блокировке потоков или аварийному завершению программы.
  • Синхронизация с другими потоками. Это может привести к их взаимной блокировке.
  • Захват объекта синхронизации, принадлежащего коду, который находится в ожидании захвата блокировки загрузчика. Это может привести к взаимной блокировке потоков.
  • Инициализация COM-потоков с помощью CoInitializeEx. При определенных условиях данная функция может вызвать LoadLibraryEx.
  • Вызов реестровых функций. Данные функции реализованы в Advapi32.dll. Если Advapi32.dll не была инициализирована раньше пользовательской DLL-библиотеки, последняя может обратиться к неинициализированной области памяти, что приведет к аварийному завершению процесса.
  • Вызов CreateProcess. Создание процесса может повлечь за собой загрузку другой DLL-библиотеки.
  • Вызов ExitThread. Выход из потока во время отсоединения DLL-библиотеки может повлечь за собой повторный захват блокировки загрузчика, что приведет к взаимной блокировке потоков или аварийному завершению программы.
  • Вызов CreateThread. Если создаваемый поток не синхронизируется с другими потоками, то такая операция допустима, хотя и рискованна.
  • Создание именованного конвейера или другого именованного объекта (только для Windows 2000). В Windows 2000 именованные объекты предоставляются библиотекой Terminal Services DLL. Если данная библиотека не инициализирована, ее вызовы могут привести к аварийному завершению процесса.
  • Использование функций управления памятью из динамической библиотеки C Run-Time (CRT). Если данная библиотека не инициализирована, вызовы этих функций могут привести к аварийному завершению процесса.
  • Вызов функций из библиотек User32.dll или Gdi32.dll. Некоторые функции загружают другие DLL-библиотеки, которые могут быть не инициализированы.
  • Использование управляемого кода.

Взгляните на примеры ошибок, обнаруженных с помощью диагностики V718.