Разберем типичный сценарий работы с внешними библиотеками: разработчик извлекает из макета двоичные данные DLL во временный каталог, подключает библиотеку в качестве внешней компоненты или COM-объекта, выполняет целевой метод, после чего пытается удалить файл с диска. В момент вызова метода УдалитьФайлы() операционная система возвращает критическую ошибку: 5(0x00000005): Отказано в доступе. Попытка предварительно присвоить объекту значение Неопределено не дает результата. Выясним причину этой проблемы и рассмотрим надежные варианты её решения.
Проанализируем механику работы операционной системы Windows с динамическими библиотеками. Когда платформа 1С загружает DLL (через функции WinAPI семейства LoadLibrary), операционная система создает отображение файла в виртуальную память процесса 1cv8.exe (или rphost.exe на стороне сервера). До тех пор пока файл отображен в память процесса, файловая система накладывает эксклюзивную блокировку на удаление.
Рассмотрим, почему обнуление переменной в коде 1С не разблокирует файл сразу:
Объект = Неопределено; лишь удаляет ссылку в контексте встроенного языка. Внутренний вызов IUnknown::Release() COM-объекта или очистка ресурсов Native-компоненты происходят с задержкой, зависящей от сборщика мусора платформы.CoFreeUnusedLibraries, однако во встроенном языке 1С нет прямых методов принудительной выгрузки загруженных модулей или вызова FreeLibrary. Библиотека освобождается только при полном завершении сеанса 1С.Попытка постоянного создания и удаления файлов библиотек внутри одного сеанса считается некорректным архитектурным подходом. Оптимальным решением, применяемым также в библиотеке стандартных подсистем (БСП), является сохранение компоненты в постоянный локальный каталог кэша пользователя с контролем версии или контрольной суммы (хэша).
Посмотрим на пример правильной организации логики подключения без постоянного удаления файла:
ИмяФайла = КаталогВременныхФайлов() + "MyCustomComponent_v1_0.dll";
Файл = Новый Файл(ИмяФайла);
Если Не Файл.Существует() Тогда
МакетДвоичныеДанные = ПолучитьМакет("МояБиблиотека");
МакетДвоичныеДанные.Записать(ИмяФайла);
КонецЕсли;
ПодключитьВнешнююКомпоненту(ИмяФайла, "MyComponent", ТипВнешнейКомпоненты.Native);
Компонента = Новый ("AddIn.MyComponent.Extension");
Компонента.ВыполнитьДействие();
При таком подходе файл извлекается из макета только при первом обращении или при обновлении версии. Он остается в каталоге пользователя и не требует немедленного удаления, что полностью исключает ошибку доступа.
Если специфика бизнес-логики или требования безопасности строго обязывают удалять библиотеку сразу после выполнения метода, необходимо вынести обращение к ней за пределы процесса 1cv8.exe. Когда библиотека загружается дочерним процессом, её блокировка снимается автоматически сразу после завершения этого процесса.
Разберем по шагам реализацию через запуск внешнего консольного скрипта или утилиты-обертки:
ЗапуститьПриложение() с параметром ожидания завершения.УдалитьФайлы() для временной папки — теперь операция завершится успешно.
КаталогСкрипта = КаталогВременныхФайлов() + "TaskDir\";
СоздатьКаталог(КаталогСкрипта);
ПутьКDll = КаталогСкрипта + "worker.dll";
ДвоичныеДанныеDll = ПолучитьМакет("worker_dll");
ДвоичныеДанныеDll.Записать(ПутьКDll);
// Вызов внешней утилиты с ожиданием завершения (КодВозврата)
Команда = "powershell.exe -ExecutionPolicy Bypass -File """ + КаталогСкрипта + "run.ps1""";
КодВозврата = 0;
ЗапуститьПриложение(Команда, , Истина, КодВозврата);
// После закрытия стороннего процесса файл разблокирован
УдалитьФайлы(КаталогСкрипта);
Если используется именно COM-компонента, эффективным решением является регистрация библиотеки в системной службе «Службы компонентов» (Component Services) в качестве серверного приложения (Server Application). В этом случае библиотека физически загружается не в 1cv8.exe, а в системный суррогатный процесс dllhost.exe. После завершения вызовов и закрытия суррогата файл библиотеки разблокируется без необходимости перезапуска сеанса 1С.