При интеграции двух информационных баз 1С:Предприятия 7.7 через OLE-соединение часто возникает необходимость запускать специализированные алгоритмы исключительно в момент внешнего вызова, не затрагивая обычный интерактивный сеанс пользователей. Разберем особенности платформы 1С 7.7, проанализируем ограничения процедуры ПриНачалеРаботыСистемы() и рассмотрим надежные способы решения этой задачи.
В отличие от платформы 1С:Предприятие 8, где модули четко разделены на «Модуль управляемого приложения», «Модуль обычного приложения» и «Модуль внешнего соединения», в 1С 7.7 существует только общий глобальный модуль. Процедура ПриНачалеРаботыСистемы() выполняется всегда: как при обычном интерактивном входе пользователя, так и при программной инициализации базы через OLE-метод Initialize().
При этом возникают критические сложности:
Предупреждение() или Вопрос()) приводят к мертвому зависанию скрытого OLE-процесса.СтатусВозврата(0) в процедуре ПриНачалеРаботыСистемы(), запуск системы прерывается немедленно. В вызывающей базе метод Initialize() возвращает 0, объект соединения не инициализируется до конца, и прочитать возвращаемые свойства напрямую через OLE становится невозможно.Рассмотрим популярный подход, предложенный в обсуждении: передача пользовательского флага в параметрах запуска с последующим чтением строки через внешнюю компоненту FormEx.
Выясним причину работы данного метода: при вызове метода Initialize() мы передаем строку параметров подключения, куда можно дописать произвольный ключ, например /ThisIsAnOLEConnection.
Посмотрим на пример формирования строки подключения в базе «А»:
БазаОЛЕ = СоздатьОбъект("V77.Application");
КаталогБД = "C:\Bases\Base_EP";
ИмяПользователя = "РоботОЛЕ";
Пароль = "1601";
// Добавляем собственный ключ запуска в конец строки
СтрокаПодключения = "/d""" + КаталогБД + """ /n""" + ИмяПользователя + """ /p""" + Пароль + """ /ThisIsAnOLEConnection";
Если БазаОЛЕ.Initialize(БазаОЛЕ.RMTrade, СтрокаПодключения, "NO_SPLASH_SHOW") = 0 Тогда
Предупреждение("Ошибка подключения к базе Б!");
КонецЕсли;
Теперь разберем, как принять и обработать этот ключ в базе «Б» внутри процедуры ПриНачалеРаботыСистемы() глобального модуля:
Процедура ПриНачалеРаботыСистемы()
// Загружаем внешнюю компоненту для доступа к расширенным функциям
Если ЗагрузитьВнешнююКомпоненту("formex.dll") = 1 Тогда
Сервис = СоздатьОбъект("Сервис");
СтрокаПараметров = Сервис.КоманднаяСтрока;
Если Найти(СтрокаПараметров, "/ThisIsAnOLEConnection") > 0 Тогда
// Выполняем алгоритм только для OLE-соединения
РезультатАлгоритма = ВыполнитьСпециальныйАлгоритм();
// Если нужно прервать запуск и передать строку:
// записываем результат во временный файл обмена
Если ПустаяСтрока(РезультатАлгоритма) = 0 Тогда
ТекстФайл = СоздатьОбъект("Текст");
ТекстФайл.ДобавитьСтроку(РезультатАлгоритма);
ТекстФайл.Записать("C:\Bases\Exchange\result.txt");
// Прерываем запуск базы "Б"
СтатусВозврата(0);
Возврат;
КонецЕсли;
КонецЕсли;
КонецЕсли;
КонецПроцедуры
Проанализируем ситуацию, когда требуется гарантированно отличить OLE-сеанс без передачи дополнительных параметров. Операционная система Windows при создании COM-объекта всегда запускает исполняемый файл платформы (1cv7.exe или 1cv7s.exe) со специальным системным ключом -Embedding (или /Automation).
С помощью внешних компонент или Windows API (функция GetCommandLine) можно проанализировать полную системную командную строку процесса. Наличие подстроки -Embedding со 100% точностью указывает на программный OLE-запуск.
Дополнительным признаком является видимость главного окна программы: при OLE-подключении окно запускается в скрытом режиме (свойство Visible = 0, а функция API IsWindowVisible возвращает ложь).
Наиболее надежным и стабильным решением задачи возврата строки является отказ от выполнения логики внутри процедуры ПриНачалеРаботыСистемы(). Разберем по шагам, почему это эффективнее:
ПриНачалеРаботыСистемы() в базе «Б» завершается в штатном режиме без ошибок.EvalExpr(), получает результат и сама корректно закрывает соединение.Посмотрим на реализацию экспортируемой функции в глобальном модуле базы «Б»:
Функция глПолучитьДанныеДляБазыА() Экспорт
Перем ТекстОтвета;
// Выполняем нужный алгоритм
ТекстОтвета = "Успешная обработка данных";
Возврат ТекстОтвета;
КонецФункции
Посмотрим на код вызова в базе «А»:
БазаОЛЕ = СоздатьОбъект("V77.Application");
СтрокаПодключения = "/d""C:\Bases\Base_EP"" /n""РоботОЛЕ"" /p""1601""";
Если БазаОЛЕ.Initialize(БазаОЛЕ.RMTrade, СтрокаПодключения, "NO_SPLASH_SHOW") = 1 Тогда
// Вызываем функцию и получаем строковый результат
РезультатСтрока = БазаОЛЕ.EvalExpr("глПолучитьДанныеДляБазыА()");
Если ПустаяСтрока(РезультатСтрока) = 0 Тогда
Сообщить("Получен ответ от базы Б: " + РезультатСтрока);
КонецЕсли;
// Корректно завершаем сеанс базы "Б"
БазаОЛЕ.ExecuteBatch("ЗавершитьРаботуСистемы(0)");
БазаОЛЕ = 0;
Иначе
Сообщить("Не удалось установить OLE-соединение");
КонецЕсли;
Важно помнить: в 1С 7.7 обычного присвоения БазаОЛЕ = 0; часто недостаточно для выгрузки процесса. Исполняемый файл 1cv7s.exe может остаться висеть в диспетчере задач, блокируя каталог пользователя и файлы базы данных.
Для гарантированного освобождения памяти соблюдаем следующие правила:
БазаОЛЕ.ExecuteBatch("ЗавершитьРаботуСистемы(0)");.БазаОЛЕ = 0;.