"Подводные камни" при работе с макетами типа ActiveDocument 1С 8.3 УФ. Клиент-Сервер. Режимы работы Тонкий, Веб клиенты

20.12.16

Разработка - Механизмы платформы 1С

Обход проблем при работе с макетами типа ActiveDocument (Word,Excel 2016). Версия 1С 8.3, УФ, Клиент-Сервер, Тонкий, Веб клиенты

Итак, в чем суть моей статьи?

1. Есть сервер (x64), с установленным Office 2016 (x64), есть 1С 8.3.8 также x64 (имеется в виду сервер 1С). Пользователи подключаются как через тонкого клиента, так и через веб клиент.

2. Есть куча типовых договоров и накладных счет-фактур, оформленных (подготовленных) отделом продаж. Там шрифт, абзац и т.д., естественно, все оформлено в Word,Excel 2016 (x64), естественно, нет желания все это рисовать в 1С, а есть желание засунуть в макеты формата ActiveDocument

Решение банальное, вроде бы :), не буду описывать весь механизм, как это делается, как заполняются данные из 1С в макет ActiveDocument - думаю, вы это знаете прекрасно и без меня. Но в ходе работы выяснилось, что не все так гладко в царстве COM объектов, а именно:

1. Как сохранить файл Word,Excel и передать этот файл клиенту (клиент тонкий и веб)?

2. Какой вариант лучше выбрать: ActiveDocument или ДвоичныеДанные? Хотя для меня это звучит примерно как, что выбрать Водку С Пивом или Пиво С Водкой :). Один фиг, надо забить документы данными из 1С и передать их Клиенту.

Ну да ладно пропустим лирику, я выбираю ActiveDocument, не буду описывать весь алгоритм, просто перечислю "подводные" камни и их решение. Все нижеизложенное это мои личные измышления и в никакой мере не претендуют на истину в последней инстанции. Возможно, вы решили эти проблемы или решите по другому.

1. Камень "первый". Не работает метод SaveAs (как для MSWord, так и для MSExcel). При попытке записать ДвоичныеДанные 1С просто вылетает. Смотрим фрагмент листинга:

MSWord = Макет.Получить();

Попытка

Документ = MSWord.Application.Documents(1);

Документ.Activate();

//Далее каким-либо образом получаем данные и заполняем Word-овский документ

//Получим путь во временной директории для сохранения туда файла

ИмяВрем = ПолучитьИмяВременногоФайла(".docx");

Документ.SaveAs(ИмяВрем); //здесь начинается камень

МойДокументВДвоичныхДанных = Новый ДвоичныеДанные(ИмяВрем); //ну а здесь этот камень нафиг выбивает 1С :)

MSWord.Application.Quit();

Ссылочка //infostart.ru/public/407448/ (выдержка из статьи "Как передать документ Word (ActiveDocument или ДвоичныеДанные) с сервера на клиент"

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

На сервере 1С:Предприятия произошла неисправимая ошибка. Приложение будет закрыто

Я пытался побороть эту ошибку, помещая во временное хранилище двоичные данные, переводя двоичные данные в Неопределено, но победить не удалось. Стоит заметить, что временный файл на сервере 1С-ка со временем сама удаляет корректно.

Возможная ошибка на сервере при вызове метода SaveAs:


Ошибка при вызове метода контекста (SaveAs) 
Документ.SaveAs(ИмяВрем); 
по причине: 
Произошла исключительная ситуация (Microsoft Word): Ошибка команды


Мне поначалу эту ошибку не удалось победить, поэтому я стал использовать ДвоичныеДанные. Позже нашёл решение проблемы: необходимо по пути C:\Windows\SysWOW64\config\systemprofile\ и C:\Windows\System32\config\systemprofile\ создать папки Desktop. Туда никаких файлов никто не пишет, похоже, программе важен факт наличия этой папки. Решение нашёл по ссылке: http://devtrainingforum.v8.1c.ru/forum/thread.jsp?id=581998&threadtype=0

Спасибо огромное.

Создание папок
C:\Windows\SysWOW64\config\systemprofile\Desktop
C:\Windows\System32\config\systemprofile\Desktop
проблему решило. Тема закрыта.

В чем причина? Причина в том, что код

MSWord = Макет.Получить();

Всегда вызывает экземпляр объекта COM (x32) независимо от того какой разрядности Office установлен. Вы никогда не задумывались, почему в макет ActoveDocument нельзя вставить файлы с расширением docx,xlsx

это можно проверить и через ДиспетчерЗадач, но факт есть факт - макет ActiveDocument вызывает неявно экземпляр COM (x32) и поэтому все дальнейшие манипуляции нужно делать учитывая это особенность.

1. Либо сервер и все ПО должно быть x32. Тогда ничего делать (в смысле переписывать код) не надо

2. Либо переписать код, таким образом

// получаем имя временного файла
ВремФайл = ПолучитьИмяВременногоФайла("doc");

// этот код точно вызовет экземпляр COM нужной разрядности, в нашем случае x64
Word = Новый COMОбъект("Word.Application");
Word.Displayalerts = 0;
ДокументН = Word.Application.Documents.Add();
ДокументН.SaveAs(ВремФайл,0);				
Word.Quit();
		
// далее все по старому
Макет = УправлениеПечатью.МакетПечатнойФормы("Документ.АктПередачиОборудования."+НазваниеМакета);
MSWord = Макет.Получить();
Попытка
	Документ = MSWord.Application.Documents(1); 
	Документ.Activate();

    // здесь что-то делаем, заполняем данные

    
    // здесь мы пересохраним наш файл из COM x62  в COM x64
    MSWord.Application.Selection.WholeStory();
	MSWord.Application.Selection.Copy();
	ДокументН = MSWord.Application.Documents.Open(ВремФайл);
	ДокументН.Activate();
	MSWord.Application.Selection.Paste();
	ДокументН.SaveAs(ВремФайл,0);
	ДокументН.Close();
	MSWord = Неопределено;
		
Исключение
			
	// Если произойдет ошибка выводятся данные об ошибке и объект закрывается. 
	Информация = ИнформацияОбОшибке();
	ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Ошибка - "+Информация.Описание+" код ошибки - "+СокрЛП(Информация.ИсходнаяСтрока)); 
	MSWord.Application.Quit(); 
			
КонецПопытки;

Думаю, тут все понятно, сначала мы создали экземпляр COM нужной разрядности, создали пустой файл и сохранили во временную папку, далее работает с COM x32, заполняем данными и напоследок копируем содержимое всего документа и сохраняем в ранее подготовленный файл.

Все то же самое, но только для Excel

ВремФайл = ПолучитьИмяВременногоФайла("xls");
Excel = Новый COMОбъект("Excel.Application");
Excel.Displayalerts = 0;
КнигаН = Excel.WorkBooks.Add();
ЛистН = КнигаН.WorkSheets(1);
КнигаН.SaveAs(ВремФайл, -4143);				
Excel.Quit();
		
Макет = УправлениеПечатью.МакетПечатнойФормы("Документ.НакладнаяОборудования."+НазваниеМакета);
MSExcel = Макет.Получить();
КнигаН = MSExcel.Application.Workbooks.Open(ВремФайл);
ЛистН = КнигаН.WorkSheets(1);
		
Попытка
	WBook = MSExcel.Application.Workbooks(1);
	Лист = WBook.WorkSheets(1);
	Лист.Activate();
 
    // что-то делаем, заполняем данными из 1С

	MSExcel.Application.WorkBooks(1).WorkSheets(1).Cells.Copy(ЛистН.Cells);
	КнигаН.Save();				
	КнигаН.Close();
Исключение
			
    // Если произойдет ошибка выводятся данные об ошибке и объект закрывается. 
	Информация = ИнформацияОбОшибке();
	ОбщегоНазначенияКлиентСервер.СообщитьПользователю("Ошибка - "+Информация.Описание+" код ошибки - "+СокрЛП(Информация.ИсходнаяСтрока)); 
	MSExcel.Application.Quit(); 
			
КонецПопытки;

ну вот "первый" камень я решил, на сервере x64 с Office x64, все работает точно как часы, без ошибок и не надо создавать никаких папок и все прочее.

Камень "второй". фрагмент кода

ВремФайл = ПолучитьИмяВременногоФайла("xls");

есть не очень хорошо, потому как идет запись в папку: "c:\Users\ че там....", вообще эта папка всегда в черном списке всех фаерволов, антивирусов и прочее, прочее, хотя бы открыть центр управления безопасностью Word или Excel. Глянем и мы туда

придется возится с этим, иначе есть вероятность появления "странных" ошибок. Поэтому я предлагаю следующее:

1. Открываем Конфигуратор и добавляем новый РегистрСведений

здесь мы будем хранить наши готовые Word, Excel файлы уже заполненные, конечно:

Объект - Тип Документ.Ссылка

НазваниеМакета - Идентификатор макета

ДокументOffice - ХранилищеЗначений, здесь мы и держим наш готовый файл

2. Дописываем выше написанный код следующим образом:

МЗ = РегистрыСведений.ВременноеХранилищеOffice.СоздатьМенеджерЗаписи();
МЗ.Объект = Выборка.Ссылка;
МЗ.НазваниеМакета = НазваниеМакета;
МЗ.Прочитать();
МЗ.Объект = Выборка.Ссылка;
МЗ.НазваниеМакета = НазваниеМакета;
МЗ.ДокументOffice = Новый ХранилищеЗначения(Новый ДвоичныеДанные(ВремФайл));
МЗ.Записать();
	
УдалитьФайлы(ВремФайл);

Что мы делаем, мы записываем готовый файл в регистр сведений и затем удаляем сам временный файл, решаем проблему "Центра безопасности Word,Excel". Осталось только одно показать этот готовый файл Клиенту (клиент тонкий и веб)

3. Камень "третий" - передача файла клиенту, тут просто выложу весь код, что-то взял из БСП, что-то из Демонстрационная конфигурация "Управляемое приложение", что-то из Инета, но в общем вот код (целиком)

////////////////////////////////////////////////////////////////////////////////
// СЛУЖЕБНЫЕ ПРОЦЕДУРЫ И ФУНКЦИИ БСП

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

&НаКлиенте
Процедура ПослеЗапускаПриложения(КодВозврата, ИмяПриложения) Экспорт
	; // 
КонецПроцедуры

&НаКлиенте
Процедура ПослеПолученияФайлов(ПереданныеФайлы, ДополнительныеПараметры) Экспорт
	Если НЕ ПереданныеФайлы=Неопределено Тогда
		Для каждого Описание Из ПереданныеФайлы Цикл
			ОпПослеЗапускаПриложения = Новый ОписаниеОповещения("ПослеЗапускаПриложения", ЭтотОбъект, Описание.Имя);
			НачатьЗапускПриложения(ОпПослеЗапускаПриложения, Описание.Имя);
		КонецЦикла;
	КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ПослеВыбораКаталога(ВыбранныеФайлы, ИмяКоманды) Экспорт
	Если ВыбранныеФайлы = Неопределено Тогда
		Возврат;
	КонецЕсли;
	
	Каталог = ВыбранныеФайлы[0];
	ОбщегоНазначенияВызовСервера.СохранитьРабочийКаталог(Каталог);
	
	Если ИмяКоманды = "Накладная" Тогда
		НазваниеМакета = "Накладная"
	КонецЕсли;
	
	СтруктураДанных.Вставить("Каталог", Каталог);
	ПодключитьОбработчикОжидания("Подключаемый_ПередатьФайлКлиенту",5,Истина);
КонецПроцедуры

&НаКлиенте
Процедура ОткрытьФайлыЧерезРасширение(ИмяКоманды) 
	ОпПослеВыбораКаталога = Новый ОписаниеОповещения("ПослеВыбораКаталога", ЭтотОбъект, ИмяКоманды);
	
	Каталог = ОбщегоНазначенияВызовСервера.ПолучитьРабочийКаталог();
	Если Каталог = Неопределено ИЛИ Каталог = "" Тогда
		Диалог = Новый ДиалогВыбораФайла(РежимДиалогаВыбораФайла.ВыборКаталога);
		Диалог.Заголовок = НСтр("ru = 'Выбор каталога временного хранения файлов'", "ru");
		Диалог.Показать(ОпПослеВыбораКаталога);
	Иначе
		ВыбранныеФайлы = Новый Массив;
		ВыбранныеФайлы.Добавить(Каталог);
		ВыполнитьОбработкуОповещения(ОпПослеВыбораКаталога, ВыбранныеФайлы);
	КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура ОбработатьПодключениеРасширенияРаботыСФайлами(РасширениеПодключено,ДополнительныеПараметры) Экспорт
	Если РасширениеПодключено Тогда
		ОткрытьФайлыЧерезРасширение(ДополнительныеПараметры.ИмяКоманды);
	КонецЕсли;
КонецПроцедуры

&НаКлиенте
Процедура Подключаемый_ПередатьФайлКлиенту()
	Адрес = ПолучитьМакет();
	Если Адрес<>Неопределено Тогда
		ОтключитьОбработчикОжидания("Подключаемый_ПередатьФайлКлиенту");
		НомерДокумента = СтруктураДанных.НомерДокумента;
		Каталог = СтруктураДанных.Каталог;
		НазваниеМакета = СтруктураДанных.НазваниеМакета;
		
		ПутьКфайлу = Каталог+"\"+НазваниеМакета+"_№"+НомерДокумента+".xls";
		Описание = Новый ОписаниеПередаваемогоФайла(ПутьКфайлу, Адрес);
		ПередаваемыеФайлы = Новый Массив;
		ПередаваемыеФайлы.Добавить(Описание);
		
		НачатьПолучениеФайлов(Новый ОписаниеОповещения("ПослеПолученияФайлов", ЭтотОбъект), ПередаваемыеФайлы, "", Ложь);
	КонецЕсли;
КонецПроцедуры

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

&НаКлиенте
Процедура Подключаемый_ВыполнитьПечать()
	ВыполнитьПечатьСервер();
КонецПроцедуры

// СтандартныеПодсистемы.Печать
&НаКлиенте
Процедура Подключаемый_ВыполнитьКомандуПечати(Команда)
	
	Ссылка = Элементы.Список.ТекущиеДанные.Ссылка;
	СтруктураДанных = Новый Структура;
	СтруктураДанных.Вставить("Объект", Ссылка);
	СтруктураДанных.Вставить("НазваниеМакета", "Накладная");
	ПодключитьОбработчикОжидания("Подключаемый_ВыполнитьПечать", 1, Истина);
	
	ОписаниеКоманды = УправлениеПечатьюКлиент.ОписаниеКомандыПечати(Команда.Имя,ИмяФормы);
	НачатьУстановкуРасширенияРаботыСФайлами();
	НачатьПодключениеРасширенияРаботыСФайлами(Новый ОписаниеОповещения("ОбработатьПодключениеРасширенияРаботыСФайлами",ЭтотОбъект,Новый Структура("ИмяКоманды",ОписаниеКоманды.Идентификатор)));
	
КонецПроцедуры
// Конец СтандартныеПодсистемы.Печать

Немного пояснений:

1. Во-первых, клиент у нас работает как через Тонкий, так и через Веб режимы, поэтому заранее в свойствах Конфигуратор ставим следующие значения:

Чтобы не было проблем при работе с браузером

2. Используем обработчики ожидания, чтобы избежать проблем с синхронностью вызов (это касается только режим Веб)

3. И последнее, подключаем Расширение для работы с Файлами (помним что в режиме Тонкий клиент, это расширение включено всегда). И через код:

    Адрес = ПолучитьНавигационнуюСсылку(КлючЗаписи,"ДокументOffice");

передаем файл Клиенту используя механизм НавигационнаяСсылка, получаем следующие сообщения в браузере (Тонкий само собой работает):

ну вот, кажется, все. Надеюсь, это поможет кому-нибудь...

P.S.

По поводу Word, Excel файлы вставлять в виде ДвоичныхДанные? проблема-то в чем?

1. Мы либо должны вытащить из макета эти ДвоичныеДанные и заполнить данными из 1С и ВНИМАНИЕ снова записать в виде ДвоичныхДанных (Водка С Пивом или Пиво С Водкой)

2. Либо мы должно получить макет ДвоичныеДанные на стороне Клиента и там заполнить его, НО COM объект поддерживается только браузером IE и то с танцами с настройками ActiveX, другие браузеры давно отказались от использования ActiveX

Word Excel Office 2016 ActiveDocument Веб клиент Тонкий клиент

См. также

Механизмы платформы 1С Программист Стажер Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Эта небольшая статья - некоторого рода шпаргалка по файловым потокам: как и зачем с ними работать, какие преимущества это дает.

23.06.2024    7512    bayselonarrend    20    

154

Механизмы платформы 1С Программист Стажер Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

Пример использования «Сервисов интеграции» без подключения к Шине и без обменов.

13.03.2024    5968    dsdred    16    

80

Механизмы платформы 1С Программист Стажер Платформа 1С v8.3 Бесплатно (free)

Все мы используем массивы в своем коде. Это один из первых объектов, который дают ученикам при прохождении обучения программированию. Но умеем ли мы ими пользоваться? В этой статье я хочу показать все методы массива, а также некоторые фишки в работе с массивами.

24.01.2024    17801    YA_418728146    26    

71

Механизмы платформы 1С Программист Бесплатно (free)

Язык программирования 1С содержит много нюансов и особенностей, которые могут приводить к неожиданным для разработчика результатам. Сталкиваясь с ними, программист начинает лучше понимать логику платформы, а значит, быстрее выявлять ошибки и видеть потенциальные узкие места своего кода там, где позже можно было бы ещё долго медитировать с отладчиком в поисках источника проблемы. Мы рассмотрим разные примеры поведения кода 1С. Разберём результаты выполнения и ответим на вопросы «Почему?», «Как же так?» и «Зачем нам это знать?». 

06.10.2023    23786    SeiOkami    48    

135

WEB-интеграция Универсальные функции Механизмы платформы 1С Программист Платформа 1С v8.3 Конфигурации 1cv8 Бесплатно (free)

При работе с интеграциями рано или поздно придется столкнуться с получением JSON файлов. И, конечно же, жизнь заставит проверять файлы перед тем, как записывать данные в БД.

28.08.2023    14779    YA_418728146    7    

166
Комментарии
Подписаться на ответы Инфостарт бот Сортировка: Древо развёрнутое
Свернуть все
1. yukon 153 20.12.16 16:16 Сейчас в теме
Есть замечательная статья на сайте Microsoft "Вопросы серверной автоматизации Office" https://support.microsoft.com/ru-ru/kb/257757

Но там есть такое предупреждение, которое, возможно, необходимо поместить в начало статьи:
Корпорация Microsoft на сегодняшний день не рекомендует производить и не поддерживает автоматизацию программ из пакета Microsoft Office с помощью автоматических, неинтерактивных клиентских приложений или компонентов (включая ASP, DCOM и службы NT), поскольку при запуске в этом окружении программы пакета Office могут работать нестабильно или зависать.
CratosX; zebr; Ali1976; +3 Ответить
2. spy-83 199 20.12.16 22:14 Сейчас в теме
про СОМ вообще пора забыть, так как это только в винде доступно.
Многие ставят сервер на линукс х64, клиенты на маках.
вот и кончился СОМ.

если ошибаюсь, поправьте
pop_akaki; charushkin; +2 Ответить
3. zebr 32 27.10.17 11:22 Сейчас в теме
(2)
про СОМ вообще пора забыть, так как это только в винде доступно.
Многие ставят сервер на линукс х64, клиенты на маках.


Ошибаетесь в том, что про COM можно забыть.
Пока есть молоток, можно забивать гвозди. Вопрос выбора - каким молотком пользоваться
4. izidakg 172 12.02.19 12:11 Сейчас в теме
и какая альтернатива СОМ? только если двоичные данные
галить может любой вариант соединения, последнее время предпочитаю ставить 64х сервер 1С и конечно масса бывает проблем
так печать некоторых регламентных отчетов с ШК говорят работает только на 32х, также СОМ говорят по умолчанию 32х вызов идет
я всего не знаю и я более практик чем теоретик - как говорила бабушка, инженер не тот кто все знает, а тот кто может решить поставленную задачу используя справочную литературу. А она не один завод спроектировала и построила
Вот пример из практики (говорят так работать не должно):
у клиента 2013 офис и 1С 8.3.14.1565 на 64х - сом работает только если запущено приложение 32х
есть клиент 1С 8.3.13.хххх на 64х - печать регламентных отчетов с ШК не работает, только если приложение запущено на 32х

у меня, на моем сервере оба варианта работают на 2013 офисе и 1С 8.3.14.1565 на 64х
проверял через диспетчер задач, какая именно битность платформы 1С - 100% 64х
Оставьте свое сообщение