Почему при попытке удалить DLL возникает ошибка Отказано в доступе 5(0x00000005) и как это исправить?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

Разберем типичный сценарий работы с внешними библиотеками: разработчик извлекает из макета двоичные данные DLL во временный каталог, подключает библиотеку в качестве внешней компоненты или COM-объекта, выполняет целевой метод, после чего пытается удалить файл с диска. В момент вызова метода УдалитьФайлы() операционная система возвращает критическую ошибку: 5(0x00000005): Отказано в доступе. Попытка предварительно присвоить объекту значение Неопределено не дает результата. Выясним причину этой проблемы и рассмотрим надежные варианты её решения.

Причина блокировки файла в операционной системе

Проанализируем механику работы операционной системы Windows с динамическими библиотеками. Когда платформа 1С загружает DLL (через функции WinAPI семейства LoadLibrary), операционная система создает отображение файла в виртуальную память процесса 1cv8.exe (или rphost.exe на стороне сервера). До тех пор пока файл отображен в память процесса, файловая система накладывает эксклюзивную блокировку на удаление.

Рассмотрим, почему обнуление переменной в коде 1С не разблокирует файл сразу:

  1. Задержка сборщика мусора. Присвоение Объект = Неопределено; лишь удаляет ссылку в контексте встроенного языка. Внутренний вызов IUnknown::Release() COM-объекта или очистка ресурсов Native-компоненты происходят с задержкой, зависящей от сборщика мусора платформы.
  2. Кэширование COM-библиотек. Даже если счетчик ссылок COM-объекта стал равен нулю, сама библиотека (InprocServer32) остается загруженной в адресное пространство процесса. За её выгрузку отвечает функция CoFreeUnusedLibraries, однако во встроенном языке 1С нет прямых методов принудительной выгрузки загруженных модулей или вызова FreeLibrary. Библиотека освобождается только при полном завершении сеанса 1С.

Вариант 1. Переход на версионный кэш библиотек (рекомендуемый подход)

Попытка постоянного создания и удаления файлов библиотек внутри одного сеанса считается некорректным архитектурным подходом. Оптимальным решением, применяемым также в библиотеке стандартных подсистем (БСП), является сохранение компоненты в постоянный локальный каталог кэша пользователя с контролем версии или контрольной суммы (хэша).

Посмотрим на пример правильной организации логики подключения без постоянного удаления файла:


ИмяФайла = КаталогВременныхФайлов() + "MyCustomComponent_v1_0.dll";
Файл = Новый Файл(ИмяФайла);

Если Не Файл.Существует() Тогда
    МакетДвоичныеДанные = ПолучитьМакет("МояБиблиотека");
    МакетДвоичныеДанные.Записать(ИмяФайла);
КонецЕсли;

ПодключитьВнешнююКомпоненту(ИмяФайла, "MyComponent", ТипВнешнейКомпоненты.Native);
Компонента = Новый ("AddIn.MyComponent.Extension");
Компонента.ВыполнитьДействие();

При таком подходе файл извлекается из макета только при первом обращении или при обновлении версии. Он остается в каталоге пользователя и не требует немедленного удаления, что полностью исключает ошибку доступа.

Вариант 2. Изоляция выполнения в отдельном процессе

Если специфика бизнес-логики или требования безопасности строго обязывают удалять библиотеку сразу после выполнения метода, необходимо вынести обращение к ней за пределы процесса 1cv8.exe. Когда библиотека загружается дочерним процессом, её блокировка снимается автоматически сразу после завершения этого процесса.

Разберем по шагам реализацию через запуск внешнего консольного скрипта или утилиты-обертки:

  1. Сохраняем DLL и вспомогательный исполняемый сценарий (например, PowerShell или VBScript) во временную папку.
  2. Запускаем скрипт синхронно через метод платформы ЗапуститьПриложение() с параметром ожидания завершения.
  3. После выхода из вызванного процесса операционная система освобождает дескриптор файла библиотеки.
  4. Вызываем метод УдалитьФайлы() для временной папки — теперь операция завершится успешно.

КаталогСкрипта = КаталогВременныхФайлов() + "TaskDir\";
СоздатьКаталог(КаталогСкрипта);

ПутьКDll = КаталогСкрипта + "worker.dll";
ДвоичныеДанныеDll = ПолучитьМакет("worker_dll");
ДвоичныеДанныеDll.Записать(ПутьКDll);

// Вызов внешней утилиты с ожиданием завершения (КодВозврата)
Команда = "powershell.exe -ExecutionPolicy Bypass -File """ + КаталогСкрипта + "run.ps1""";
КодВозврата = 0;
ЗапуститьПриложение(Команда, , Истина, КодВозврата);

// После закрытия стороннего процесса файл разблокирован
УдалитьФайлы(КаталогСкрипта);

Вариант 3. Использование суррогатного процесса COM+ (dllhost.exe)

Если используется именно COM-компонента, эффективным решением является регистрация библиотеки в системной службе «Службы компонентов» (Component Services) в качестве серверного приложения (Server Application). В этом случае библиотека физически загружается не в 1cv8.exe, а в системный суррогатный процесс dllhost.exe. После завершения вызовов и закрытия суррогата файл библиотеки разблокируется без необходимости перезапуска сеанса 1С.

← На главную