Как исправить ошибку «Метод объекта не обнаружен (Печать)» и зависание при печати в Word из 1С 8.3

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

Разработка внешних печатных форм в среде 1С:Предприятие 8.3 с использованием Библиотеки Стандартных Подсистем (БСП) — задача стандартная, но часто преподносящая неприятные сюрпризы. Программисты регулярно сталкиваются с двумя критическими проблемами при попытке сформировать печатную форму в формате MS Word:

  1. Вызов команды печати мгновенно прерывается ошибкой: «Метод объекта не обнаружен (Печать)».
  2. Программа бесконечно показывает окно с сообщением «Команда выполняется...», при этом сам документ Word не открывается, а сеанс зависает.

В этой статье мы подробно проанализируем причины возникновения этих ошибок и разберем по шагам два надежных варианта реализации печати документов Word: классический клиентский метод через COM-соединение и современный кроссплатформенный метод БСП без использования COM-объектов на сервере.

Часть 1. Разбираем ошибку «Метод объекта не обнаружен (Печать)»

Когда мы регистрируем внешнюю печатную форму в справочнике «Дополнительные отчеты и обработки», БСП считывает параметры из экспортной функции СведенияОВнешнейОбработке. Если в параметрах указан вариант запуска ВызовКлиентскогоМетода, система ожидает, что в обработке присутствует основная управляемая форма, а в модуле этой формы объявлена экспортная процедура Печать с строго определенной сигнатурой.

Выясним причину, почему платформа выдает ошибку, даже если код процедуры написан верно:

  1. Форма обработки не назначена основной. Это самая банальная, но очень частая ошибка. Разработчик создает управляемую форму, пишет в ее модуле код, но забывает зайти в свойства самой внешней обработки в конфигураторе и в поле «Основная форма» выбрать созданную форму. В итоге БСП пытается открыть дефолтную пустую форму и, естественно, не находит в ней нужного экспортного метода.
  2. Жесткое кэширование параметров регистрации БСП. Платформа 1С оптимизирует работу с внешними обработками и кэширует структуру параметров регистрации. Если вы сначала зарегистрировали обработку с типом команды ВызовСерверногоМетода, а затем изменили код на ВызовКлиентскогоМетода и просто обновили файл через кнопку «Обновить из файла», изменения могут не примениться. Система продолжит искать метод в модуле объекта, а не в модуле формы. Решение: полностью удалить элемент из справочника дополнительных обработок, закрыть сеанс 1С, запустить его заново и зарегистрировать файл «с нуля».
  3. Неверная сигнатура метода. Процедура обязательно должна быть объявлена на клиенте и содержать экспортный модификатор. Шаблон процедуры должен выглядеть строго следующим образом:

&НаКлиенте
Процедура Печать(ИдентификаторКоманды, МассивОбъектов) Экспорт
    // Код вызова формирования макета
КонецПроцедуры

Часть 2. Почему процесс зависает на сообщении «Команда выполняется...»

Рассмотрим подробнее ситуацию, когда клиентский метод успешно вызывается, но сеанс намертво зависает, выводя сообщение о выполнении команды. Это происходит из-за грубого нарушения клиент-серверной архитектуры при работе с COM-объектами.

Проанализируем типичный сценарий ошибки. В модуле формы разработчик вызывает процедуру СобратьМакет(Элемент). Если эта процедура выполняется на сервере (или вызывает серверный контекст), внутри нее происходит следующая инициализация:


// Ошибка! Создание COM-объекта Word на сервере
MSWord = Новый COMОбъект("Word.Application");

В клиент-серверной версии 1С сервер приложений физически выполняется на удаленном компьютере под управлением службы (обычно под учетной записью usr1cv8). Когда серверный код вызывает конструктор COMОбъект("Word.Application"), происходит следующее:

Золотое правило разработки: создавать COM-объект Word.Application можно строго на клиенте (&НаКлиенте). На сервере допускается лишь подготовка чистых данных (структур, таблиц значений, массивов), которые затем передаются на клиентскую сторону для заполнения шаблона.

Часть 3. Вариант решения №1. Классическая печать Word через COM на клиенте

Если вам требуется реализовать интерактивное заполнение шаблона Word на рабочем компьютере пользователя, где установлен Microsoft Office, разберем по шагам корректный алгоритм.

Шаг 1. Пропишем правильную регистрацию в модуле объекта внешней обработки. Обратите внимание, что мы явно указываем вариант запуска ВызовКлиентскогоМетода:


Функция СведенияОВнешнейОбработке() Экспорт
    
    ПараметрыРегистрации = Новый Структура;
    МассивНазначений = Новый Массив;
    МассивНазначений.Добавить("Справочник.Сотрудники");
    
    ПараметрыРегистрации.Вставить("Вид", "ПечатнаяФорма");    
    ПараметрыРегистрации.Вставить("Назначение", МассивНазначений);
    ПараметрыРегистрации.Вставить("Наименование", "Трудовой договор с сотрудником (WORD)");                       
    ПараметрыРегистрации.Вставить("БезопасныйРежим", Ложь); // Для работы с COM-объектами на клиенте
    ПараметрыРегистрации.Вставить("Версия", "1.0");    
    ПараметрыРегистрации.Вставить("Информация", "Печать трудового договора в формате Word через COM");   
    
    ТаблицаКоманд = ПолучитьТаблицуКоманд();
    ДобавитьКоманду(ТаблицаКоманд, 
                    "Трудовой договор (WORD)", 
                    "ТрудовойДоговор", 
                    "ВызовКлиентскогоМетода", // Вызываем форму обработки
                    Истина, 
                    ""); // Модификатор оставляем пустым
                    
    ПараметрыРегистрации.Вставить("Команды", ТаблицаКоманд);
    
    Возврат ПараметрыРегистрации;
    
КонецФункции

Функция ПолучитьТаблицуКоманд()
    Команды = Новый ТаблицаЗначений;
    Команды.Колонки.Добавить("Представление", Новый ОписаниеТипов("Строка"));
    Команды.Колонки.Добавить("Идентификатор", Новый ОписаниеТипов("Строка"));
    Команды.Колонки.Добавить("Использование", Новый ОписаниеТипов("Строка"));
    Команды.Колонки.Добавить("ПоказыватьОповещение", Новый ОписаниеТипов("Булево"));
    Команды.Колонки.Добавить("Модификатор", Новый ОписаниеТипов("Строка"));
    Возврат Команды;
КонецФункции

Процедура ДобавитьКомунду(ТаблицаКоманд, Представление, Идентификатор, Использование, ПоказыватьОповещение, Модификатор)
    НоваяКоманда = ТаблицаКоманд.Добавить();
    НоваяКоманда.Представление = Представление;
    НоваяКоманда.Идентификатор = Идентификатор;
    НоваяКоманда.Использование = Использование;
    НоваяКоманда.ПоказыватьОповещение = ПоказыватьОповещение;
    НоваяКоманда.Модификатор = Модификатор;
КонецПроцедуры

Шаг 2. Разработаем модуль основной управляемой формы нашей обработки. Здесь мы разделим логику: на сервере получим данные для заполнения договора и двоичные данные макета, а на клиенте — создадим процесс Word, сохраним макет во временный файл и произведем автозамену параметров.


&НаКлиенте
Процедура Печать(ИдентификаторКоманды, МассивОбъектов) Экспорт
    
    Для Каждого ТекущийОбъект Из МассивОбъектов Цикл
        
        // Получаем структуру данных сотрудника с сервера
        ДанныеДляЗаполнения = ПолучитьДанныеСотрудникаНаСервере(ТекущийОбъект);
        
        // Получаем двоичные данные шаблона Word с сервера
        ДвоичныеДанныеМакета = ПолучитьМакетНаСервере();
        
        Если ДвоичныеДанныеМакета = Неопределено Тогда
            ВызватьИсключение "Не удалось получить макет договора из двоичных данных!";
        КонецЕсли;
        
        // Сохраняем макет во временный файл на диске клиента
        ИмяВременногоФайла = ПолучитьИмяВременногоФайла("docx");
        ДвоичныеДанныеМакета.Записать(ИмяВременногоФайла);
        
        Попытка
            // Инициализируем COM-объект строго на клиенте
            WordПриложение = Новый COMОбъект("Word.Application");
        Исключение
            Сообщить("Не удалось запустить MS Word. Проверьте, установлена ли программа на вашем ПК!", СтатусСообщения.Важное);
            Возврат;
        КонецПопытки;
        
        Попытка
            // Открываем сохраненный временный файл
            Документ = WordПриложение.Documents.Open(ИмяВременногоФайла);
            WordПриложение.Visible = Ложь; // Скрываем окно на время автозамены
            
            // Заменяем теги в тексте документа
            ЗаменитьПараметрВДокументе(WordПриложение, "<КраткоеНаименованиеОрганизации>", ДанныеДляЗаполнения.Организация);
            ЗаменитьПараметрВДокументе(WordПриложение, "<ДолжностьРуководителя>", ДанныеДляЗаполнения.ДолжностьРуководителя);
            ЗаменитьПараметрВДокументе(WordПриложение, "<Руководитель>", ДанныеДляЗаполнения.ФИОРуководителя);
            ЗаменитьПараметрВДокументе(WordПриложение, "<Сотрудник>", ДанныеДляЗаполнения.ФИОСотрудника);
            
            // Делаем окно Word видимым пользователю
            WordПриложение.Visible = Истина;
            WordПриложение.Activate();
            
        Исключение
            // В случае ошибки обязательно закрываем фоновые процессы Word
            Если ЗначениеЗаполнено(Документ) Тогда
                Документ.Close(Ложь);
            КонецЕсли;
            WordПриложение.Quit();
            Сообщить("Произошла ошибка при формировании документа: " + ОписаниеОшибки(), СтатусСообщения.Важное);
        КонецПопытки;
        
    КонецЦикла;
    
КонецПроцедуры

&НаКлиенте
Процедура ЗаменитьПараметрВДокументе(WordПриложение, ИмяПараметра, ЗначениеПараметра)
    
    Поиск = WordПриложение.Selection.Find;
    Поиск.ClearFormatting();
    Поиск.Text = ИмяПараметра;
    Поиск.Replacement.ClearFormatting();
    Поиск.Replacement.Text = Строка(ЗначениеПараметра);
    
    // Параметры метода Execute: FindText, MatchCase, MatchWholeWord, MatchWildcards, 
    // MatchSoundsLike, MatchAllWordForms, Forward, Wrap, Format, ReplaceWith, Replace
    Поиск.Execute(,,,,,,,,,, 2); // 2 означает заменить все вхождения
    
КонецПроцедуры

&НаСервере
Функция ПолучитьДанныеСотрудникаНаСервере(СсылкаСотрудник)
    
    Данные = Новый Структура;
    Данные.Вставить("Организация", СсылкаСотрудник.Организация.НаименованиеСокращенное);
    Данные.Вставить("ФИОСотрудника", СсылкаСотрудник.Наименование);
    
    // Получаем руководителя организации
    ОтветственныеЛица = ОтветственныеЛицаБП.ОтветственныеЛица(СсылкаСотрудник.Организация, ТекущаяДата());
    Данные.Вставить("ФИОРуководителя", ФизическиеЛица.ФамилияИнициалыФизЛица(ОтветственныеЛица.Руководитель));
    Данные.Вставить("ДолжностьРуководителя", ОтветственныеЛица.РуководительДолжность);
    
    Возврат Данные;
    
КонецФункции

&НаСервере
Функция ПолучитьМакетНаСервере()
    // Получаем макет из метаданных обработки по его имени ("ДвоичныеДанные")
    ОбработкаОбъект = РеквизитФормыВЗначение("Объект");
    Попытка
        Возврат ОбработкаОбъект.ПолучитьМакет("ДоговорШаблон");
    Исключение
        Возврат Неопределено;
    КонецПопытки;
    
КонецФункции

Часть 4. Вариант решения №2. Современный и надежный подход БСП (Печать DOCX без COM)

Использование COM-технологий постепенно уходит в прошлое. Этот способ медленный, требует обязательной установки Microsoft Office на компьютерах пользователей, не работает под Linux и часто блокируется политиками безопасности ОС. В современных версиях БСП (3.0 и выше) есть великолепный кроссплатформенный механизм генерации офисных документов, который собирает файлы на стороне сервера напрямую на уровне ZIP-архивов и XML.

Для реализации этого метода нам потребуется:

  1. Создать шаблон в формате .docx (обычный файл Word).
  2. Вместо старых символов замены <Параметр> использовать специальные теги БСП прямо в тексте документа:
    • Границы областей: {v8 Область.Шапка} ... {/v8 Область.Шапка}.
    • Параметры замены: {v8 ФИОСотрудника}, {v8 Организация}.
  3. Загрузить этот файл в макет внешней обработки с типом макета Двоичные данные.
  4. В сведениях о регистрации указать вариант запуска ВызовСерверногоМетода.

Посмотрим на пример реализации кода в Модуле объекта нашей внешней печатной формы. Клиентский метод и форма в данном случае вообще не требуются!


Процедура Печать(МассивОбъектов, КоллекцияПечатныхФорм, ОбъектыПечати, ПараметрыВывода) Экспорт
    
    // 1. Извлекаем макет двоичных данных из тела внешней обработки
    ДвоичныеДанныеМакета = ПолучитьМакет("ТрудовойДоговорDOCX");
    
    // 2. Инициализируем макет программными средствами БСП
    Макет = УправлениеПечатью.ИнициализироватьМакетОфисногоДокумента(ДвоичныеДанныеМакета, "DOCX");
    
    // 3. Создаем структуру для будущей печатной формы
    ПечатнаяФорма = УправлениеПечатью.ИнициализироватьПечатнуюФормуОфисногоДокумента("DOCX", Макет);
    
    Для Каждого СсылкаИзМассива Из МассивОбъектов Цикл
        
        // Получаем структуру данных сотрудника для заполнения
        ДанныеДляЗаполнения = ПолучитьДанныеДляДоговора(СсылкаИзМассива);
        
        // Выбираем область макета "Шапка"
        ОбластьШапка = УправлениеПечатью.ОбластьМакета(Макет, "Область.Шапка");
        
        // Присоединяем область и автоматически заполняем параметры
        УправлениеПечатью.ПрисоединитьОбластьИЗаполнитьПараметры(ПечатнаяФорма, ОбластьШапка, ДанныеДляЗаполнения);
        
    КонецЦикла;
    
    // 4. Генерируем финальный архив документа во временном хранилище
    АдресВоВременномХранилище = УправлениеПечатью.СформироватьДокумент(ПечатнаяФорма);
    
    // 5. Передаем сформированный документ в общую коллекцию печати БСП
    УправлениеПечатью.ВывестиОфисныйДокументВКоллекцию(КоллекцияПечатныхФорм, 
                                                       "ТрудовойДоговор", 
                                                       "Трудовой договор с сотрудником", 
                                                       АдресВоВременномХранилище);
    
    // Освобождаем временные файлы и очищаем память
    УправлениеПечатью.ОчиститьСсылкиНаМакетыОфисныхДокументов(Макет);
    
КонецПроцедуры

Функция ПолучитьДанныеДляДоговора(СсылкаСотрудник)
    
    Данные = Новый Структура;
    Данные.Вставить("Организация", СсылкаСотрудник.Организация.НаименованиеСокращенное);
    Данные.Вставить("ФИОСотрудника", СсылкаСотрудник.Наименование);
    
    ОтветственныеЛица = ОтветственныеЛицаБП.ОтветственныеЛица(СсылкаСотрудник.Организация, ТекущаяДата());
    Данные.Вставить("ФИОРуководителя", ФизическиеЛица.ФамилияИнициалыФизЛица(ОтветственныеЛица.Руководитель));
    Данные.Вставить("ДолжностьРуководителя", ОтветственныеЛица.РуководительДолжность);
    
    Возврат Данные;
    
КонецФункции

При использовании этого серверного метода БСП самостоятельно упакует сформированный DOCX-файл, передаст его на сторону клиента и откроет у пользователя в той офисной программе, которая установлена по умолчанию для документов этого расширения (MS Word, LibreOffice, МойОфис). Это полностью решает проблему зависания фонового процесса WINWORD.EXE и гарантирует быструю и отказоустойчивую работу печатной формы в любых операционных системах.

← На главную