При автоматизации задач и интеграции различных систем программисты часто сталкиваются с необходимостью подключения к базам данных старых версий, в частности к 1С:Предприятие 7.7. Одним из самых популярных способов взаимодействия является использование технологии OLE (COM-соединения). Однако при работе с файловыми базами данных (DBF-версиями) разработчики нередко обнаруживают неприятную проблему: после успешного выполнения OLE-запроса и завершения работы вызывающей программы, при попытке следующего входа пользователей в базу система выдает предупреждение об аварийном завершении и требует монопольной переиндексации.
В этой статье мы подробно разберем, почему возникает эта ошибка, как устроены механизмы блокировки в 1С 7.7, и как правильно завершать OLE-сессию, чтобы избежать повреждения и «слета» индексов.
Для того чтобы понять природу проблемы, проанализируем внутреннее устройство файловой базы данных 1С 7.7. В отличие от современных СУБД, в DBF-версии нет единого сервера транзакций, который бы управлял сессиями пользователей. Программа определяет наличие активных пользователей в базе с помощью специальной сигнальной таблицы.
Давайте разберем этот механизм пошагово:
_1SUSERS.DBF. Эта таблица предназначена для хранения информации обо всех подключенных к базе пользователях._1SUSERS.DBF. Если из базы выходит последний пользователь, эта таблица становится полностью пустой (ее размер составляет 0 байт или она очищается)._1SUSERS.DBF остается._1SUSERS.DBF непустая, система делает вывод: «Предыдущий сеанс был завершен аварийно». Чтобы гарантировать целостность данных, платформа блокирует вход пользователей и требует запустить систему в монопольном режиме для полной реиндексации файлов базы данных (восстановления файлов *.cdx).Таким образом, когда мы подключаемся через OLE, выполняем нужные действия и просто «бросаем» переменную подключения, операционная система уничтожает COM-объект, но сам исполняемый процесс 1С 7.7 (1cv7.exe или 1cv7s.exe) может завершиться некорректно. Запись в таблице _1SUSERS.DBF зависает, что и приводит к требованию реиндексации.
Рассмотрим стандартный шаблон подключения к базе 1С 7.7 через OLE, который часто используют разработчики:
Функция УстановитьПодключение(ПутьКБазе, ЮзерВБазе, ПарольВБазе)
Состояние("Подключение...");
БазаОле = CreateObject("V77.Application");
Если БазаОле.Initialize(БазаОле.RMTrade, "/d" + СокрЛП(ПутьКБазе) + " /n" + СокрЛП(ЮзерВБазе) + " /p" + СокрЛП(ПарольВБазе), "NO_SPLASH_SHOW") = 0 Тогда
Сообщить("Ошибка открытия информационной базы!", "!");
Возврат 0;
Иначе
Возврат БазаОле;
КонецЕсли;
КонецФункции
На первый взгляд код выглядит абсолютно рабочим. Подключение успешно устанавливается, данные считываются или записываются. Проблема кроется в том, как завершается работа с переменной БазаОле. Если в конце работы вызывающей программы мы просто пишем:
БазаОле = ""; // или БазаОле = 0; или БазаОле = Неопределено;
этого оказывается недостаточно. Внешнее приложение просто обнуляет ссылку на COM-объект. Платформа 1С 7.7 при этом не получает явного сигнала на корректный выход из системы с очисткой временных файлов и таблицы _1SUSERS.DBF. Процесс «убивается» операционной системой на низком уровне как потерявший связь с клиентом, что приравнивается к аварийному завершению.
Чтобы решить проблему раз и навсегда, мы должны принудительно заставить 1С 7.7 выполнить процедуру штатного выхода из системы. Для этого используется специализированный метод OLE-объекта — ExecuteBatch. Этот метод позволяет выполнить произвольную команду на встроенном языке 1С 7.7 внутри контекста подключенной базы.
Проанализируем правильный алгоритм завершения работы с OLE-объектом:
Если ПустоеЗначение(БазаОле) = 0 Тогда
Попытка
// Принудительно вызываем штатную процедуру завершения работы системы
БазаОле.ExecuteBatch("ЗавершитьРаботуСистемы(0)");
Исключение
// Обработка возможных ошибок при вызове метода
Сообщить("Не удалось корректно завершить работу OLE-сессии: " + ОписаниеОшибки());
КонецПопытки;
// Только после этого полностью очищаем переменную ссылки
БазаОле = 0;
КонецЕсли;
Использование метода ЗавершитьРаботуСистемы(0) гарантирует, что 1С 7.7 выполнит все необходимые внутренние регламенты: закроет открытые транзакции, корректно запишет кэш на диск, удалит временные файлы блокировок и, самое главное, корректно очистит запись о сессии в таблице _1SUSERS.DBF.
К сожалению, даже при использовании конструкции ЗавершитьРаботуСистемы(0) иногда могут возникать ситуации, когда процесс 1С 7.7 продолжает «висеть» в памяти компьютера и блокировать файлы базы. Разберем неочевидные причины такого поведения и способы борьбы с ними.
1. Наличие активных ссылок на подчиненные OLE-объекты
Если в процессе работы с базой вы создавали промежуточные OLE-объекты, например:
ВыборкаТоваров = БазаОле.CreateObject("Справочник.Номенклатура");
то до тех пор, пока переменная ВыборкаТоваров активна в вашей вызывающей программе, основной процесс 1С 7.7 не завершится. COM-сервер отслеживает счетчик ссылок на все дочерние объекты. Пока этот счетчик не станет равен нулю, вызов ЗавершитьРаботуСистемы может игнорироваться или приводить к зависанию процесса в памяти. Поэтому перед закрытием основного подключения обязательно обнуляйте все дочерние объекты:
ВыборкаТоваров = 0;
БазаОле.ExecuteBatch("ЗавершитьРаботуСистемы(0)");
БазаОле = 0;
2. Интерактивные вопросы при выходе (ConfirmExit)
В глобальном модуле конфигурации 1С 7.7 в предопределенной процедуре ПриЗавершенииРаботыСистемы разработчики часто размещают интерактивные диалоги (например, «Вы действительно хотите выйти?» или «Выполнить резервное копирование?»). При OLE-подключении эти окна пытаются открыться в невидимом (скрытом) режиме. В результате процесс зависает в ожидании ответа пользователя, который физически невозможно дать.
Чтобы этого избежать, перед закрытием базы программно отключите подтверждение выхода:
БазаОле.ConfirmExit = 0; // или БазаОле.ПодтверждатьВыход = 0;
БазаОле.ExecuteBatch("ЗавершитьРаботуСистемы(0)");
3. Использование англоязычных синонимов методов
Для повышения надежности работы интеграционных механизмов, особенно при вызове 1С 7.7 из внешних сценариев (VBScript, JavaScript, C# или Python), настоятельно рекомендуется использовать англоязычные аналоги методов 1С. Это исключает проблемы с кодировкой и региональными настройками операционной системы:
БазаОле.ExecuteBatch("ExitSystem(0)");
4. Влияние рабочего каталога пользователя
Если для OLE-подключения используется учетная запись пользователя, у которого в Конфигураторе 1С 7.7 прописан индивидуальный «Рабочий каталог», система создает в нем дополнительные файлы блокировок (например, 1SUserLst.tmp). В случае аварийного завершения сессии этот каталог блокируется. Рекомендуется завести в Конфигураторе **специального технического пользователя** для OLE-интеграций, ограничить его права только необходимым минимумом и **обязательно оставить поле «Рабочий каталог» пустым**.
5. Особенности работы под 64-битными операционными системами (x64)
Если вызывающее приложение (например, сервер 1С 8.3) работает в 64-битном режиме, а подключаемая 1С 7.7 является строго 32-битным приложением, их взаимодействие происходит через системный компонент Windows — службу DCOM (процесс dllhost.exe). При недостатке прав у системной учетной записи (например, USR1CV8) или сбоях DCOM операционная система может принудительно завершить процесс 1cv7.exe, не давая ему выполнить процедуру закрытия файлов. В этом случае необходимо детально настроить права доступа в компонентах службы DCOM (утилита dcomcnfg).
Если OLE-подключение используется для выполнения регулярных ночных регламентных задач (например, выгрузка данных по расписанию), и вы хотите полностью застраховаться от зависаний системы, можно использовать внешние административные сценарии автоматизации.
Перед запуском сеанса интеграции командный файл (например, .bat или PowerShell-скрипт) может выполнять следующие превентивные действия:
taskkill /F /IM 1cv7.exe
1susers.dbf и индексные файлы *.cdx непосредственно перед запуском обмена.
"C:\Program Files (x86)\1Cv77\bin\1cv7.exe" enterprise /M /d"C:\PathToYourBase" /n"Robot" /p"Password"
/M (монопольно) платформа автоматически перестроит индексы без лишних диалоговых окон и вопросов к пользователю.
Использование комбинации правильного программного закрытия OLE-сессии через ExecuteBatch("ExitSystem(0)") и грамотной настройки учетной записи пользователя позволит вам полностью избавиться от проблемы «слета» индексов и обеспечит стабильную, бесперебойную работу интеграционных механизмов.