Как отработать ошибку запроса 1С 7.7 при подключении из 1С 8.3 и избежать зависания процессов

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

При интеграции современных конфигураций на платформе 1С:Предприятие 8.3 со старыми базами 1С:Предприятие 7.7 разработчики часто используют механизм COM-соединения (OLE). Это удобный способ «на лету» прочитать данные из исторической системы. Однако данная технология таит в себе серьезную опасность: если при выполнении запроса в контексте 1С 7.7 происходит синтаксическая или логическая ошибка, COM-сервер переходит в аварийное состояние.

В этой ситуации стандартная конструкция Попытка...Исключение в 1С 8.3 перехватывает ошибку, но процесс 1cv7.exe (или 1cv7s.exe) зависает в памяти операционной системы. Попытка принудительно завершить этот процесс через Диспетчер задач приводит к блокировке таблиц базы данных, и при следующем запуске система 1С 7.7 в обязательном порядке потребует монопольной переиндексации (восстановления индексных файлов .CDX). Если база данных файловая и имеет большой объем, этот процесс может парализовать работу пользователей на несколько часов.

В данной статье мы подробно разберем, почему возникает эта проблема, и рассмотрим несколько эффективных и безопасных способов её решения.

Выясняем причину: почему процесс 1С 7.7 зависает в памяти

Для понимания сути проблемы проанализируем механизм управления памятью при COM-соединении. Когда мы пишем в 1С 8.3 следующий код:


В77 = Новый COMОбъект("V77.Application");
Подключено = В77.Initialize(В77.RMTrade, ПараметрыПодключения, "NO_SPLASH_SHOW");

в операционной системе запускается полноценный экземпляр приложения 1С 7.7 в режиме OLE-сервера. Когда мы создаем подчиненные объекты, например:

ЗапросВ77 = В77.CreateObject("Запрос");

в памяти операционной системы создается дерево зависимых OLE-ссылок. Ветка 1С 8.3 удерживает ссылку на объект ЗапросВ77, который, в свою очередь, жестко связан с родительским объектом В77.

Если во время выполнения метода ЗапросВ77.Выполнить() происходит сбой (например, в тексте запроса указан несуществующий реквизит справочника), OLE-сервер 1С 7.7 генерирует критическое исключение. Связь между объектами нарушается, но счетчик ссылок Windows COM не обнуляется. Даже если мы присвоим переменным значения Неопределено:


ЗапросВ77 = Неопределено;
В77 = Неопределено;

процесс 1cv7.exe все равно останется активным в памяти, так как операционная система считает, что на дочерние элементы OLE все еще есть ссылки в адресном пространстве 1С 8.3. Из-за этого повторное подключение становится невозможным, а файлы базы данных остаются заблокированными.

Рассмотрим детальные сценарии обхода и решения этой проблемы.

Решение 1. Создание функции-обертки в глобальном модуле 1С 7.7

Это наиболее надежный и простой способ, если у нас есть возможность вносить изменения в конфигурацию 1С 7.7. Суть метода заключается в том, чтобы полностью изолировать выполнение запроса внутри контекста самой «семерки». В этом случае любые синтаксические ошибки будут обработаны локально, а COM-соединение не пострадает.

Выполним это решение по шагам:

  1. Откроем конфигуратор 1С 7.7 и перейдем в Глобальный модуль.
  2. Добавим в самый конец модуля экспортную функцию-обертку. Эта функция будет принимать текст запроса, безопасно выполнять его в блоке Попытка...Исключение и возвращать готовую таблицу значений в случае успеха или число 0 в случае ошибки.

Код функции в Глобальном модуле 1С 7.7:


Функция глПолучитьРезультатЗапросаДля83(ТекстЗапроса) Экспорт
    Запрос = СоздатьОбъект("Запрос");
    Попытка
        Если Запрос.Выполнить(ТекстЗапроса) = 0 Тогда
            Возврат 0;
        КонецЕсли;
        
        ТЗ = СоздатьОбъект("ТаблицаЗначений");
        Запрос.Выгрузить(ТЗ, 0, 0);
        Возврат ТЗ;
    Исключение
        // Записываем информацию об ошибке в журнал регистрации 7.7
        ЗаписьЖурналаРегистрации("Ошибка запроса из 8.3: " + ОписаниеОшибки(), "Запрос", "Ошибка", , 3);
        Возврат 0;
    КонецПопытки;
КонецФункции

Теперь на стороне 1С 8.3 нам больше не нужно создавать объект Запрос через COM. Мы просто вызываем нашу глобальную функцию напрямую через объект подключения. Рассмотрим пример безопасного вызова в коде 1С 8.3:


&НаСервере
Процедура ВыполнитьЗапросК77()
    ПараметрыПодключения = " /D""" + ПутьКБазе77 + """ /N" + Пользователь77 + " /P" + Пароль77;
    В77 = Новый COMОбъект("V77.Application");
    
    Если Не В77.Initialize(В77.RMTrade, ПараметрыПодключения, "NO_SPLASH_SHOW") Тогда
        ВызватьИсключение "Не удалось подключиться к базе 1С 7.7";
    КонецЕсли;
    
    ТекстЗапроса = "//{{ЗАПРОС(Контрагенты)
    |Без групп;
    |Клиент = Справочник.Контрагенты.ТекущийЭлемент;
    |Группировка Клиент;
    |//}}ЗАПРОС";
    
    Попытка
        // Вызываем функцию-обертку из глобального модуля 7.7
        РезультатТаблица = В77.глПолучитьРезультатЗапросаДля83(ТекстЗапроса);
        
        Если ТипЗнч(РезультатТаблица) = Тип("Число") И РезультатТаблица = 0 Тогда
            ОбщегоНазначения.СообщитьПользователю("Запрос не выполнен из-за внутренней ошибки в 1С 7.7.");
        Иначе
            // Успешно получили Таблицу Значений, обрабатываем ее в 8.3
            ПроверитьИОбработатьДанные(РезультатТаблица);
        EndIf;
    Исключение
        ОбщегоНазначения.СообщитьПользователю("Произошел критический сбой COM-соединения: " + ОписаниеОшибки());
    ВосстановитьСоединение:
        // Вызываем процедуру корректного закрытия сессии
        ЗакрытьСессию77(В77);
    КонецПопытки;
КонецПроцедуры

Этот подход гарантирует, что даже если в тексте запроса допущена грубая ошибка, она локализуется внутри базы 7.7, а управление штатно вернется в 1С 8.3.

Решение 2. Корректное программное закрытие OLE-сессии при возникновении сбоев

Если мы не можем менять конфигурацию 1С 7.7 (например, она находится на поддержке или закрыта для редактирования), нам придется обрабатывать исключения целиком на стороне 1С 8.3. Чтобы при этом избежать «зависания» процесса 1cv7.exe, необходимо соблюдать строгую последовательность освобождения ресурсов.

Проанализируем алгоритм безопасного закрытия зависшего OLE-соединения:

  1. Принудительное закрытие дочерних ссылок. Перед уничтожением основного объекта подключения необходимо явно присвоить значение Неопределено всем переменным, которые ссылаются на объекты, созданные внутри 7.7 (запросы, справочники, документы, таблицы значений).
  2. Отключение подтверждения выхода. Чтобы 1С 7.7 при попытке закрытия не показала модальное окно с вопросом «Вы действительно хотите выйти?», необходимо выключить это подтверждение программно.
  3. Штатный вызов завершения работы. Вместо жесткого убийства процесса мы вызываем штатный метод завершения работы системы 1С 7.7 с помощью конструкции ExecuteBatch("ЗавершитьРаботуСистемы(0);"). В этом случае база данных корректно закроет все DBF-файлы и запишет индексы.

Посмотрим на пример реализации процедуры безопасного разрыва соединения в 1С 8.3:


&НаСервере
Процедура БезопасноеЗавершениеРаботы(В77, ЗапросВ77 = Неопределено)
    
    // Шаг 1: Очищаем ссылки на дочерние объекты
    Если ЗапросВ77 <> Неопределено Тогда
        ЗапросВ77 = Неопределено;
    КонецЕсли;
    
    // Шаг 2: Проверяем активность основного COM-объекта
    Если В77 <> Неопределено Тогда
        Попытка
            // Отключаем запрос на подтверждение выхода в 1С 7.7
            В77.EvalExpr("ВосстановитьЗначение(""НеЗапрашиватьПодтверждениеПриВыходе"", 1)");
            
            // Вызываем штатное завершение работы системы 1С 7.7
            В77.ExecuteBatch("ЗавершитьРаботуСистемы(0);");
        Исключение
            // Если OLE-сервер совсем не отвечает, пишем лог
            ЗаписьЖурналаРегистрации("Интеграция77", УровеньЖурналаРегистрации.Предупреждение, , , 
                "Не удалось отправить команду завершения работы OLE: " + ОписаниеОшибки());
        КонецПопытки;
        
        // Шаг 3: Полностью уничтожаем COM-объект в памяти 8.3
        В77 = Неопределено;
    КонецЕсли;
    
КонецПроцедуры

Применяя эту процедуру в блоке Исключение, мы гарантируем, что процесс 1С 7.7 будет выгружен из памяти операционной системы в штатном режиме, не вызывая повреждения индексов базы данных.

Решение 3. Полный отказ от OLE в пользу прямого доступа через ADO (рекомендуется)

Если перед нами стоит задача регулярного получения данных из базы 1С 7.7 с высокой скоростью и абсолютной отказоустойчивостью, использование COM-соединения через V77.Application является архитектурной ошибкой. Гораздо эффективнее использовать прямой доступ к таблицам базы данных (DBF или SQL) посредством технологии ADO (ActiveX Data Objects).

Разберем преимущества этого подхода:

  1. Скорость работы. Прямой запрос через ADO к файлам .DBF выполняется в десятки, а иногда и в сотни раз быстрее, чем аналогичный запрос через движок 1С 7.7.
  2. Стабильность. В операционной системе вообще не запускается тяжеловесный процесс 1С 7.7. Следовательно, исключена ситуация его зависания, блокировки файлов или требования переиндексации.
  3. Безопасность ошибок. Любая синтаксическая ошибка в SQL-запросе мгновенно перехватывается стандартным блоком Попытка...Исключение в 1С 8.3. Подключение закрывается вызовом одного простого метода Connection.Close().

Рассмотрим пример реализации чтения данных из файлового справочника «Контрагенты» базы 1С 7.7 напрямую через Microsoft OLE DB Provider для Visual FoxPro (VFPOLEDB):


&НаСервере
Функция ПолучитьДанныеКонтрагентовЧерезADO(ПутьКБазе77) Экспорт
    
    // Создаем объект подключения ADO
    Connection = Новый COMОбъект("ADODB.Connection");
    Recordset = Новый COMОбъект("ADODB.Recordset");
    
    // Формируем строку подключения для FoxPro OLE DB
    ConnectionString = "Provider=VFPOLEDB.1;Data Source=" + ПутьКБазе77 + ";Collating Sequence=MACHINE";
    
    Попытка
        Connection.Open(ConnectionString);
    Исключение
        ОбщегоНазначения.СообщитьПользователю("Не удалось подключиться к файлам DBF: " + ОписаниеОшибки());
        Возврат Неопределено;
    КонецПопытки;
    
    // Пишем прямой SQL-запрос. 
    // Физическое имя таблицы справочника Контрагенты (например, SC124) смотрим в файле 1cv7.dds
    ТекстSQL = "SELECT CODE AS Код, DESCR AS Наименование, SP125 AS ИНН FROM SC124 WHERE ISFOLDER = 2";
    
    ТаблицаРезультат = Новый ТаблицаЗначений;
    ТаблицаРезультат.Колонки.Добавить("Код");
    ТаблицаРезультат.Колонки.Добавить("Наименование");
    ТаблицаРезультат.Колонки.Добавить("ИНН");
    
    Попытка
        Recordset.Open(ТекстSQL, Connection, 1, 1); // CursorType = adOpenKeyset, LockType = adLockReadOnly
        
        Пока Recordset.EOF = 0 Цикл
            НоваяСтрока = ТаблицаРезультат.Добавить();
            НоваяСтрока.Код = СокрЛП(Recordset.Fields("Код").Value);
            НоваяСтрока.Наименование = СокрЛП(Recordset.Fields("Наименование").Value);
            НоваяСтрока.ИНН = СокрЛП(Recordset.Fields("ИНН").Value);
            
            Recordset.MoveNext();
        КонецЦикла;
        
        Recordset.Close();
        Connection.Close();
    Исключение
        // Ошибка в SQL-запросе безопасно обрабатывается без зависания процессов
        ОбщегоНазначения.СообщитьПользователю("Ошибка выполнения SQL-запроса: " + ОписаниеОшибки());
        
        Если Recordset.State <> 0 Тогда
            Recordset.Close();
        КонецЕсли;
        Если Connection.State <> 0 Тогда
            Connection.Close();
        КонецЕсли;
    КонецПопытки;
    
    Возврат ТаблицаРезультат;
    
КонецФункции

Единственная сложность данного метода заключается в том, что вам потребуется сопоставить понятные объекты метаданных 1С 7.7 с реальными именами файлов таблиц (например, SC124.dbf) и полей (например, SP125). Сделать это очень просто: достаточно открыть текстовый файл 1cv7.dds (или 1cv7.dd), который лежит в корне каталога базы данных 1С 7.7, и найти там описание нужной таблицы.

Решение 4. Использование механизма «Внешние источники данных» платформы 1С 8.3

Если база 1С 7.7 работает на SQL-версии, или если на сервере 1С 8.3 установлен качественный ODBC-драйвер для работы с DBF, мы можем задействовать мощный встроенный инструмент платформы 8.3 — Внешние источники данных.

Рассмотрим пошагово, как настроить этот механизм:

  1. В конфигураторе 1С 8.3 перейдем в ветку метаданных «Внешние источники данных» и добавим новый источник.
  2. Настроим подключение к базе 1С 7.7 через стандартную строку соединения ODBC.
  3. Запустим мастер импорта таблиц и выберем необходимые физические таблицы (например, SC124 для справочника контрагентов или DH223 для табличной части документов).
  4. После сохранения настроек мы сможем обращаться к данным 1С 7.7 с помощью стандартных запросов 1С 8.3:

Запрос = Новый Запрос;
Запрос.Текст = 
    "ВЫБРАТЬ
    |   ВнешнийИсточник.Код КАК Код,
    |   ВнешнийИсточник.Наименование КАК Наименование
    |ИЗ
    |   ВнешнийИсточникДанных.База77.Таблица.SC124 КАК ВнешнийИсточник";

Результат = Запрос.Выполнить().Выгрузить();

При таком подходе платформа 1С 8.3 самостоятельно транслирует запрос на уровень СУБД, берет на себя все заботы по открытию и закрытию соединений, кэшированию и преобразованию типов данных. Любая синтаксическая ошибка в таком запросе вызовет стандартное исключение в 1С 8.3, которое никак не повлияет на работоспособность базы-источника 1С 7.7.

Сравнительный анализ решений

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

  1. Метод функции-обертки в 7.7: Требует изменения конфигурации 1С 7.7, имеет среднюю скорость работы, но полностью решает проблему зависания OLE при ошибках за счет локального перехвата исключений.
  2. Безопасное завершение OLE в 8.3: Не требует изменения 1С 7.7, скорость работы средняя. Требует написания аккуратного кода очистки памяти и явной отправки команды выхода.
  3. Прямой доступ через ADO: Не требует изменения 1С 7.7, обладает максимальной скоростью работы и абсолютной надежностью. Минус — необходимость разбираться в физической структуре таблиц базы данных 1С 7.7 по файлу DDS.
  4. Внешние источники данных в 8.3: Отличный современный вариант, не требующий изменения конфигурации 1С 7.7. Обладает высокой скоростью и безопасностью, но требует настройки ODBC-драйверов на сервере 1С 8.3.

Используйте предложенные методики, и вы сможете построить надежные, быстрые и отказоустойчивые мосты интеграции между различными поколениями платформы 1С:Предприятие!

← На главную