При формировании печатных форм и договоров в формате Microsoft Word через механизм COM-соединения разработчики часто сталкиваются с нестабильной работой системы. В многопользовательском режиме (особенно при работе в клиент-серверном варианте или через веб-клиент) периодически возникают исключения: «Метод объекта не обнаружен (Variables)», «Интерфейс не поддерживается» или «Сервер занят». При этом на сервере начинают накапливаться десятки или сотни фоновых процессов WINWORD.EXE, блокирующих шаблон Normal.dotm и оперативную память.
Разберем детально, почему возникают подобные сбои, как правильно управлять COM-объектом Microsoft Word из 1С и какими способами можно полностью исключить зависание процессов.
Проанализируем цепочку событий, приводящую к сбою:
"tmp" + Секунда(ТекущаяДата()) при одновременной работе десятков пользователей приводит к коллизиям. Несколько сеансов пытаются открыть или перезаписать один и тот же файл, что вызывает сбой приложения Word.Normal.dotm. Когда на сервере одновременно стартуют параллельные процессы под одной системной учетной записью, возникает состояние гонки за доступ к шаблону. Это приводит к его блокировке или повреждению, о чем Word пытается сообщить через модальное диалоговое окно.WINWORD.EXE бесконечно ожидает реакции пользователя на диалоговое окно, блокируя выполнение кода 1С.Документ.Variables вызывает исключение в 1С.Рассмотрим правильный способ генерации путей для промежуточных файлов. Никогда не используйте ручную конкатенацию на основе системного времени. В платформе 1С предусмотрена специализированная функция ПолучитьИмяВременногоФайла(), гарантирующая уникальность GUID-имени в рамках операционной системы.
Посмотрим на пример исправления кода:
// Неправильный подход:
// ИмяФайла = ВременныйПуть + "tmp_" + Минута(ТекущаяДата()) + Секунда(ТекущаяДата()) + ".doc";
// Правильный подход:
ИмяВременногоФайла = ПолучитьИмяВременногоФайла("docx");
Разберем по шагам, как правильно инициализировать COM-соединение, отключить вывод системных окон и гарантированно освободить ресурсы даже в случае возникновения программного сбоя.
Посмотрим на пример безопасного алгоритма:
WordПриложение = Неопределено;
Документ = Неопределено;
Попытка
WordПриложение = Новый COMОбъект("Word.Application");
// Отключаем отображение окон и системных предупреждений
WordПриложение.Visible = Ложь;
WordПриложение.DisplayAlerts = 0; // wdAlertsNone
// Запрещаем запрос на сохранение изменений в Normal.dotm
WordПриложение.Options.SaveNormalPrompt = Ложь;
// Создаем или открываем документ
Документ = WordПриложение.Documents.Add(ПутьКШаблону);
// Заполняем переменные документа
Документ.Variables.Item("НомерДоговора").Value = "123/45";
Документ.Fields.Update();
// Сохраняем результат
Документ.SaveAs2(ИмяФайлаРезультата);
Исключение
// Записываем информацию об ошибке в журнал регистрации
ЗаписьЖурналаРегистрации("ПечатьДоговора", УровеньЖурналаРегистрации.Ошибка,,, ОписаниеОшибки());
ВызватьИсключение;
КонецПопытки;
// Блок гарантированного закрытия ресурсов
Если Документ <> Неопределено Тогда
Попытка
Документ.Close(0); // 0 = wdDoNotSaveChanges
Исключение
КонецПопытки;
КонецЕсли;
Если WordПриложение <> Неопределено Тогда
Попытка
WordПриложение.Quit(0); // 0 = wdDoNotSaveChanges
Исключение
КонецПопытки;
КонецЕсли;
Документ = Неопределено;
WordПриложение = Неопределено;
Если сервер 1С работает под учетной записью службы (например, usr1cv8), компоненты Microsoft Office могут работать некорректно из-за отсутствия папок системного профиля.
Выполним следующие системные настройки на сервере:
C:\Windows\System32\config\systemprofile\Desktop
C:\Windows\SysWOW64\config\systemprofile\Desktop
Normal.dotm в каталоге профиля службы (%appdata%\Microsoft\Templates) и установите в его свойствах атрибут «Только для чтения» (Read-only). Это предотвратит попытки одновременной перезаписи шаблона параллельными сеансами.Автоматизация Microsoft Office на стороне сервера не является рекомендуемым сценарием для высоконагруженных систем с сотнями пользователей. Рассмотрим более надежные архитектурные альтернативы:
ТабличныйДокумент, сохраняйте его напрямую без использования COM:
ТабличныйДокумент.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.DOCX);
// Либо сохранение в формате PDF:
// ТабличныйДокумент.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.PDF);
.docx представляет собой ZIP-архив с XML-документами. Вы можете использовать объекты 1С ЧтениеZipФайла и ЗаписьZipФайла для распаковки шаблона, прямой текстовой подстановки значений в файл word/document.xml и обратной упаковки. Этот способ работает быстрее COM-соединения в десятки раз, не требует установленного Microsoft Office и полностью кроссплатформенен.