gifts2017

Как передать документ Word (ActiveDocument или ДвоичныеДанные) с сервера на клиент

Опубликовал Валерий К (klinval) в раздел Программирование - Практика программирования

Когда мы работаем в файловом варианте и заполняем Word программно, можно не заморачиваться с передачей Word-овского документа на клиент, т.к. после методов <MSWord.Application.Visible = Истина; и  MSWord.Activate();> выполненных НаСервере Word-овский документ отобразится на экране монитора пользователя, но в клиент-серверном варианте так просто поступить не получится.

Метод ПолучитьМакет работает только на сервере. Методы работы или заполнения Word-а работают как на клиенте, так и на сервере. Показывать пользователю документ (Visible = Истина; Activate();) нужно на клиенте. Получается, нам нужно получить документ на сервере и передать его на клиент, чтобы показать. В данной статье будет рассмотрено 2 способа передачи с сервера на клиент. 1 способ для ActiveDocument, второй для ДвоичныеДанные.

Немного об ActiveDocument

 

Что пишет о ActiveDocument 1С:


Технология ActiveDocument предназначена для редактирования документов внешними по отношению к 1С:Предприятию 8 редакторами.
Эта технология позволяет редактировать документы визуально (например, Word или Excel) непосредственно в окне 1С:Предприятия 8, при этом элементы пользовательского интерфейса (меню, панели команд и т.д.) заменяются на предоставляемые редактором. Документы могут быть предварительно отредактированы и сохранены в макетах конфигурации, а затем макеты могут использоваться пользователями как основы для создания окончательных версий документов.
Следует заметить, что использование макетов ActiveDocument в режиме Предприятия осуществляется только программно - визуальное редактирование возможно только на этапе создания и редактирования конфигурации.
Эта технология применяется в случае, когда в конфигурации необходимо хранить данные, редактируемые другим приложением - такие, например, как шаблоны для факсов или деловых писем, созданные в Microsoft Word, или шаблоны прайс-листов в Microsoft Excel. Такая необходимость возникает, как правило, при регламентировании формата документов (как во внутреннем документообороте, так и при обмене документами со сторонними организациями и клиентами), однако при отсутствии ограничений на формат документа рекомендуется использовать существующие в 1С:Предприятии 8 возможности по оформлению электронных и печатных документов.


Другими словами: в ДвоичныеДанные вы можете только «Загрузить из файла» и «Выгрузить в файл»

 

А ActiveDocument можно прямо в конфигураторе редактировать в привычном интерфейсе Microsoft Word.  Выглядит это так:

Я писал базу с нуля, где было много шаблонов Word, которые постоянно приходилось переписывать. Было очень удобно в конфигураторе открывать фактически Word, редактировать, перезапускать отладку и смотреть, что получилось. Если у вас шаблон не так часто меняется, то можно в принципе использовать «Двоичные данные».

 

Передача ActiveDocument с сервера на клиент

 

Просто получить ActiveDocument на сервере и передать его на клиент не получится. Но можно документ полностью заполнить на сервере, на клиент передать уже полностью готовый документ, и на клиенте его просто запустить.
Пример кода:

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

//Если функция ПечатьПисьма в модуле менеджера то нужно как-то к ней обратиться 
&НаСервере
Функция ПечатьПисьмаНаСервереНаФорме(МассивОбъектов)
	Возврат Документы.ПисьмоНаОплату.ПечатьПисьма(МассивОбъектов);
КонецФункции

&НаСервере
Функция ПечатьПисьма(МассивОбъектов) Экспорт

	//Возможно какой-то запрос 
	
	//Пока Выборка.Следующий() Цикл
		
		//Каким-либо образом получаем макет Active Document
		//Если код в модуле менеджера то нужен код:
		Макет = ПолучитьМакет("ПисьмоНаОплатуWord");
		
		MSWord = Макет.Получить();
		Попытка
			Документ = MSWord.Application.Documents(1);
			Документ.Activate();
			
			//Далее каким-либо образом получаем данные и заполняем Word-овский документ
			//Например
			Selection = Документ.Content;
			Selection.Find.Replacement.Font.Size = 14;
			Selection.Find.Execute("[Название]",Истина,Истина,Ложь,,,Истина,,Истина,Выборка.НаименованиеПолное,2);
			
			
			//Получим путь во временной директории для сохранения туда файла
			ИмяВрем = ПолучитьИмяВременногоФайла(".docx");
			//Сохраним туда файл
			Документ.SaveAs(ИмяВрем);
			//Преобразуем файл в двоичные данные для передачи на клиент
			МойДокументВДвоичныхДанных = Новый ДвоичныеДанные(ИмяВрем);
			//Если код исполняется в цикле можно заполнить структуру:
			//СтруктураВсехДокументов.Вставить("Номер"+Строка(НомерПП),МойДокументВДвоичныхДанных);
			//Закроем Word
			MSWord.Application.Quit();
			//УдалитьФайлы(ИмяВрем);
		Исключение
			// Если произойдет ошибка, выводятся данные об ошибке
			// и объект закрывается.
			Сообщение = Новый СообщениеПользователю();
			Сообщение.Текст = ОписаниеОшибки();
			Сообщение.Сообщить();
			Если MSWord<>Неопределено Тогда
				MSWord.Application.Quit();
			КонецЕсли;
		КонецПопытки;
		
	//КонецЦикла;
	
	Возврат МойДокументВДвоичныхДанных;
	//Возврат СтруктураВсехДокументов;
	
КонецФункции // ПечатьПисьма()

При использовании этого кода на сервере должен быть установлен офисный пакет. Представленный код является шаблоном (примером) для дальнейшего программирования. Краткий анализ кода:

1. НаКлиенте мы просто получаем файл в виде двоичных данных с сервера, записываем во временную папку и открываем оттуда файл.
2. НаСервере мы получаем макет, его заполняем (дан пример заполнения через метод Find. Execute), сохраняем во временную папку (это будет папка на сервере вида: C:\Users\<Имя пользователя, под которым запущена служба 1С>\AppData\Local\Temp\v8_F0FB_7f3.docx), переводим файл в двоичные данные и передаём на клиент. Для сохранения документа можно использовать методы SaveAs, Save, SaveAs2, SaveAs2000.

 

В справке к методу ПолучитьИмяВременногоФайла написано:

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

Опытном путём выявлено, что 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
проблему решило. Тема закрыта.

 

Передача макет Word (Двоичные данные) с сервера на клиент

 

В случае передачи двоичных данных представлю практически весь код. 

//Код в модуле менеджера

// Заполняет список команд печати.
// 
// Параметры:
//   КомандыПечати - ТаблицаЗначений - состав полей см. в функции УправлениеПечатью.СоздатьКоллекциюКомандПечати
//
Процедура ДобавитьКомандыПечати(КомандыПечати) Экспорт
	
	// Письмо на оплату
	КомандаПечати = КомандыПечати.Добавить();
	КомандаПечати.МенеджерПечати = "Документ.ПисьмоНаОплату";
	КомандаПечати.Идентификатор = "ПисьмоНаОплату";
	КомандаПечати.Представление = НСтр("ru = 'Письмо на оплату'");
	
	КомандаПечати = КомандыПечати.Добавить();
	КомандаПечати.МенеджерПечати = "Документ.ПисьмоНаОплату";
	КомандаПечати.Идентификатор = "ПисьмоНаОплатуWord";
	КомандаПечати.Представление = НСтр("ru = 'Письмо на оплату (Word)'");
	КомандаПечати.СписокФорм = "ФормаДокумента";
	
КонецПроцедуры

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

// Формирует печатную форму письма
//
// Параметры:
//  
//
Функция ПечатьПисьма(МассивОбъектов, ОбъектыПечати, Word) Экспорт

	Запрос = Новый Запрос;
	Запрос.Текст =
	//Какой-то текст	
	
	Выборка = Запрос.Выполнить().Выбрать();
	
	ПервыйДокумент = Истина;
	СтруктураВсехДокументов = Новый Структура;
	НомерПП = 0;
	
	Пока Выборка.Следующий() Цикл
		
		НомерПП = НомерПП+1;
		
		//...
		
		Если НЕ ПервыйДокумент Тогда
			ТабДокумент.ВывестиГоризонтальныйРазделительСтраниц();
		КонецЕсли;
		
		СтруктураПараметров = Новый Структура;
		//В переменную СтруктураПараметров попадают все значения которые надо вывести на печать
		//...

		Если Word Тогда
			Для Каждого Параметр из СтруктураПараметров Цикл
				СтруктураПараметров.Вставить(Параметр.Ключ,СтрЗаменить(Параметр.Значение,Символы.Таб,""));
			КонецЦикла;
			СтруктураВсехДокументов.Вставить("Номер"+Строка(НомерПП),СтруктураПараметров);
		Иначе
			// тут должен быть код для обычного макета.
		КонецЕсли;
		
	КонецЦикла;
	
	Если Word Тогда
		//Помещать во временное хранилище не обязательно
		Возврат ПоместитьВоВременноеХранилище(СтруктураВсехДокументов);
	Иначе
		Возврат ТабДокумент;
	КонецЕсли;

КонецФункции // ПечатьПисьма()




//Код на форме:
&НаКлиенте 
Процедура Подключаемый_ВыполнитьКомандуПечати(Команда)
	
	ОписаниеКоманды = УправлениеПечатьюКлиентПовтИсп.ОписаниеКомандыПечати(Команда.Имя, ЭтаФорма.Команды.Найти("АдресКомандПечатиВоВременномХранилище").Действие);
	
	Если ОписаниеКоманды.Идентификатор = "ПисьмоНаОплатуWord" Тогда
		Если Модифицированность Тогда
			Если Не Записать() Тогда
				Возврат;
			КонецЕсли;
		КонецЕсли;
		СтруктураНаПечать = ПолучитьИзВременногоХранилища(ПечатьПисьма(Объект.Ссылка,Неопределено,Истина));
		
		Для Каждого СтруктураПараметров из СтруктураНаПечать Цикл
			ДвоичныеДанныеМакета = ПолучитьИзВременногоХранилища(ПолучитьМакетСКлиента("ПисьмоНаОплатуWord"));
			ИмяВрем = ПолучитьИмяВременногоФайла(".docx");
			ДвоичныеДанныеМакета.Записать(ИмяВрем);
			Попытка
				Документ = ПолучитьCOMОбъект(ИмяВрем); 
				Для Каждого Параметр из СтруктураПараметров.Значение Цикл
					//В оригинале тут другой код, но для простаты пример:
					Selection = Документ.Content;
					Selection.Find.Execute("["+Параметр.Ключ+"]",Ложь,Истина,Ложь,,,Истина,,Ложь,Параметр.Значение,2);
				КонецЦикла;
				Документ.Application.Visible = Истина;
				Документ.Application.WindowState = 2;
				Документ.Application.WindowState = 1;
				Документ.Activate();
			Исключение
				// Если произойдет ошибка, выводятся данные об ошибке
				// и объект закрывается.
				Сообщение = Новый СообщениеПользователю();
				Сообщение.Текст = ОписаниеОшибки();
				Сообщение.Сообщить();
				Документ.Application.Quit();
			КонецПопытки;
		КонецЦикла;
	Иначе
		УправлениеПечатьюКлиент.ВыполнитьПодключаемуюКомандуПечати(Команда, ЭтаФорма, Объект);
	КонецЕсли;	
	
КонецПроцедуры  

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

&НаСервереБезКонтекста
Функция ПолучитьМакетСКлиента(Имя)
	Возврат ПоместитьВоВременноеХранилище(Документы.ПисьмоНаОплату.ПолучитьМакет("ПисьмоНаОплатуWordДвоичныеДанные"));
КонецФункции

Краткий анализ кода:
Своим кодом я внедрился в СтандартныеПодсистемы.Печать. На форме документа 2 кнопки: «Письмо на оплату» (печать в табличный документ) и «Письмо на оплату (Word)» (печать в Word).
1. НаКлиенте на форме я получаю из модуля менеджера (функция ПечатьПисьма) структуру всех параметров. Далее получаю Word из макета (двоичные данные) и заполняю его на клиенте
2. Задача функции ПечатьПисьма модуля менеджера предназначена только для сбора сведений, которыми потом заполнится Word на клиенте.

Замечание: если вам адрес во временном хранилище нужен не сразу и единожды (как у меня), то корректней помещать во временное хранилище с уникальным идентификатором, дабы значение не удалилось пока не закрыли форму:

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

 

Альтернативы:
1. http://infostart.ru/public/270277/ (ActiveDocument) - в данной статье рассмотрена возможность сохранить docx документ в общую папку на сервере. На клиент передаётся полный путь к файлу, инициализируется COM-объект, заполняется и показывается пользователю.

2. Можно сохранить файл Word в общую папку и с клиента его просто получать. Главное, чтобы файл никто не удалил.

См. также

Подписаться Добавить вознаграждение
Комментарии
1. Франко Деллиани (Franco) 15.10.15 10:34
2. Вася Пупкин (w.r.) 15.10.15 11:08
Один вопрос - когда помещаете во временнное хранилище не указываете адрес:

Если Word Тогда
   //Помещать во временное хранилище не обязательно
   Возврат ПоместитьВоВременноеХранилище(СтруктураВсехДокументов);
Иначе
   Возврат ТабДокумент;
КонецЕсли;
...Показать Скрыть


ПоместитьВоВременноеХранилище(<Данные>, <Адрес>)
Параметры:

<Данные> (обязательный)

Тип: Произвольный.
Данные, которые необходимо поместить во временное хранилище.
<Адрес> (необязательный)

Тип: УникальныйИдентификатор; Строка.
Уникальный идентификатор формы, во временное хранилище которой надо поместить данные и вернуть новый адрес. Или адрес во временном хранилище, по которому надо поместить данные. Адрес должен получен ранее с помощью данного метода.
В случае, если передается УникальныйИдентификатор формы или адрес в хранилище, то значение будет автоматически удалено после закрытия этой формы.
Если передан УникальныйИдентификатор, не являющийся уникальным идентификатором формы, то значение будет удалено после завершения сеанса пользователя.
Если параметр не указан, помещенное значение будет удалено после очередного запроса сервера из общего модуля, при контекстном и неконтекстном серверном вызове из формы, при серверном вызове из модуля команды или при получении формы.
3. Валерий К (klinval) 15.10.15 11:37
(1) Franco, в "Альтернативы:" я уже дал ссылку на эту статью
(2) w.r., А в чём вопрос то? Боитесь, что значение будет удалено раньше чем вы успеете его использовать?
Кстати для 2-го примера <Передача макет Word (Двоичные данные) с сервера на клиент> можно и не писать
Возврат ПоместитьВоВременноеХранилище(СтруктураВсехДокументов);
//Можно писать:
Возврат СтруктураВсехДокументов;

,т.к. в СтруктураВсехДокументов попадает СтруктураПараметров, в которую в свою очередь попадает только строковые значения (какое значение на какое заменить). Для строковых значений не нужно сериализовать значение. Именно эта строка написана для примера. Если вы так-же как и я в СтруктураПараметров будете писать только строковые значения, то можно и не помещать во временное хранилище.
4. Вася Пупкин (w.r.) 15.10.15 12:07
(3) klinval,

Да, будет удалено.

Вы, кстати, еще используете без адреса временное хранилище здесь:

&НаСервере
Функция ПолучитьМакетСКлиента(Имя)
    Возврат ПоместитьВоВременноеХранилище(Документы.ПисьмоНаОплату.ПолучитьМакет("ПисьмоНаОплатуWordДвоичныеДанные"));
КонецФункции
...Показать Скрыть


И вот тут лучше будет передавать в качестве адреса для временного хранилища идентификатор формы. Те вот так:


&НаСервере
Функция ПолучитьМакетСКлиента(Имя)
    Возврат ПоместитьВоВременноеХранилище(Документы.ПисьмоНаОплату.ПолучитьМакет("ПисьмоНаОплатуWordДвоичныеДанные"), УникальныйИдентификатор);
КонецФункции
...Показать Скрыть
5. Вася Пупкин (w.r.) 15.10.15 12:19
(3) klinval,

Оффтоп. Еще совет - если не используете форму на сервере, тогда лучше использовать &НаСервереБезКонтекста
6. Валерий К (klinval) 15.10.15 12:39
(4) w.r., ну в общем случае вы конечно правы и вообще правильно написать:
&НаСервере
Функция ПолучитьМакетСКлиента(Имя)
    Возврат ПоместитьВоВременноеХранилище(Документы.ПисьмоНаОплату.ПолучитьМакет("ПисьмоНаОплатуWordДвоичныеДанные"), УникальныйИдентификатор);
КонецФункции

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

У меня даже нет переменной где бы я запомнил адрес во временном хранилище, т.к. как только я делаю "ПоместитьВоВременноеХранилище" потом сразу делаю "ПолучитьИзВременногоХранилища". Между этими двумя действиями нет ни строчки кода, поэтому пропасть значение до получения не может. Кстати код у меня установлен в рабочей базе и всё работает.
Если же кому-то исходя из специфики задачи нужен будет этот адрес не единожды и он его будет потом использовать вплоть до закрытия формы, то конечно корректней использовать УникальныйИдентификатор.

Другими словами: замечание хорошее, но в моей конкретной реализации задачи ничего не потеряется даже без уникального идентификатора.
Спасибо за замечание, статью позже чуть допишу (вдруг кому-то и правда адрес во временном хранилище нужен будет не единожды и сразу).
7. Вася Пупкин (w.r.) 15.10.15 13:23
(6) klinval,

У меня даже нет переменной где бы я запомнил адрес во временном хранилище, т.к. как только я делаю "ПоместитьВоВременноеХранилище" потом сразу делаю "ПолучитьИзВременногоХранилища". Между этими двумя действиями нет ни строчки кода, поэтому пропасть значение до получения не может.


Вы не внимательно читали видимо описание. Написано:
значение будет удалено после очередного запроса сервера из общего модуля

Т.е. любое выполнение регламентного задания или иных действий параллельно работающих пользователей, использующих общие модули, в промежутке выполнения вашего кода на форме очистит временное хранилище, куда вы поместили макет еше до получения из хранилища
8. Вася Пупкин (w.r.) 15.10.15 13:42
(6) klinval,

Еще могут отработать подписки на события. Сам сталкивался давным давно с этим, когда начинал работать со временным хранилищем

Вообще, передача объектов с клиента на сервер у 1С реализвона хреново. Например, из клиентского общего модуля нельзя получить имена реквизитов формы, тк нельзя передать форму с клиента на сервер, если ты не находишься собственно в самой форме. А процедура ПолучитьРеквизиты для УФ работает только на сервере. И тут никакие временные хранилища не помогут уже
9. Валерий К (klinval) 15.10.15 15:27
(7) w.r., Провожу следующий эксперимент:
Запускаю отладку.
На коде "Возврат Документы.ПисьмоНаОплату.ПечатьПисьма(МассивОбъектов, ОбъектыПечати, Word);" нажимаю F11. Захожу в модуль менеджера, через "Возврат ПоместитьВоВременноеХранилище(СтруктураВсехДокументов);) успешно возвращаюсь обратно на форму в функцию:
&НаСервере
Функция ПечатьПисьма(МассивОбъектов, ОбъектыПечати, Word)
	Возврат Документы.ПисьмоНаОплату.ПечатьПисьма(МассивОбъектов, ОбъектыПечати, Word);
КонецФункции
...Показать Скрыть

Я НаСервере в форме в процедуре ПечатьПисьма (см. скрин). Во временное хранилище произошло помещение объекта, но на клиент ещё не передано (и соответственно не получено из временного хранилища).
Жду 10 минут. Рабочая база, около 100 активных пользователей. За эти 10 минут было выполнено как минимум 4 фоновых задания (часть из них ни один раз) (см. скрин). Я открыл 2 сеанс, там вывел на печать письмо в Word. Ну явно за 10 минут одно из выше перечисленных условий для удаления файла было выполнено (если вы эти условия читаете в контексте всех пользователей и всех сеансов).
Далее 10 минут спустя я жму F11 выхожу на клиент и у меня он спокойно получает из временного хранилища и выходит файл Word на экран моего монитора.

В ИТС нашёл в теме"Передача файлов между клиентом и сервером"
При одновременной работе с файлом на клиенте и на сервере необходимо использовать передачу файла через временное хранилище (методы ПоместитьФайл, ПоместитьФайлы, ПолучитьФайл, ПолучитьФайлы, НачатьПомещениеФайла, ПоместитьВоВременноеХранилище, ПолучитьИзВременногоХранилища)...
....
Для сохранения данных во временном хранилище между несколькими серверными вызовами, при помещении его в хранилище необходимо использовать параметр УникальныйИдентификаторФормы метода ПоместитьФайл, передав в него идентификатор текущей формы. Такие значение будут удалены их временного хранилища только при закрытии указанной формы.

Там конечно говорится про другой метод, но всё-таки имеется ввиду серверный вызов в пределах одного сеанса / одного кода, а не всех пользователей! Для указанного там в примере метода "Переместитьфайл" то-же самое написано в СП как и для ПоместитьВоВременноеХранилище
Если параметр не указан, помещенное значение будет удалено, после очередного запроса сервера из общего модуля, при контекстном и неконтекстном серверном вызове из формы, при серверном вызове из модуля команды или при получении формы.

Там даже приведён код с пометкой "Правильно:" вызов без уникального идентификатора. После этого вызова идёт всего один серверный вызов. И прямо указывается, что код корректный, но если нужно сделать несколько серверных вызова с сохранением адреса, то нужно использовать уникальный идентификатор! Почитайте ссылку.

Пока набирал сообщение уходил на обед и оставил точку останова в том же месте (значение уже помещено во временное хранилище, но на клиенте ещё не получено). Вернулся с обеда, нажал F5 - всё работает.
Прикрепленные файлы:
10. Валерий К (klinval) 15.10.15 15:37
(8) w.r., Ну уж за час у нас точно куча подписок было отработано в рабочей базе))
Давайте на минуту представим, что
Если параметр не указан, помещенное значение будет удалено, после очередного запроса сервера из общего модуля, при контекстном и неконтекстном серверном вызове из формы, при серверном вызове из модуля команды или при получении формы.

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

Первый вопрос: почему у меня тогда ничего не удалилось ни в течении часа ни в течении 10 минут на рабочей базе БП 3.0?

Второе: методы "ПоместитьФайл, ПоместитьФайлы, ПоместитьВоВременноеХранилище" получается вообще некорректно использовать без Уникального идентификатора, т.к. любое действие любого пользователя базы может привести к очистке адреса хранилища. На большом количестве народу (1000+ одновременно работающих пользователей) данные методы будут давать корректный (не очищенный) адрес только в 50% случаев, а то и меньше. Не думаю, что это так, тем более я на практике привёл пару исчерпывающих тестов.
У меня: Бухгалтерия предприятия, редакция 3.0 (3.0.41.64) , 1С:Предприятие 8.3 (8.3.6.2332).
11. Франко Деллиани (Franco) 16.10.15 09:46
(3) klinval, прошу прощения за невнимательность
12. Вася Пупкин (w.r.) 16.10.15 10:08
(10) klinval,

Протестировал. Правда не очищается временное хранилище причем даже с вызовом сервера, если находишься в текущей форме.

У меня между методами
ПоместитьВоВременноеХранилище()
и
ПолучитьИзВременногоХранилища()
, выполнился метод
СоединенияИБВызовСервера.ПараметрыБлокировкиСеансов()
. Может 8.3.6 так работает, на 8.2 точно очищалось. Попробую протестировать на базе с 8.2 и отпишусь
13. Вася Пупкин (w.r.) 16.10.15 10:14
(10) klinval,

у вас вариант базы клиент-серверный или файловый?
14. Валерий К (klinval) 16.10.15 10:18
(13) w.r., конечно клиент-серверный. Как я написал в статье для файловой базы можно вообще не заморачиваться, выполнил на сервере запрос, чтобы получить данные, получил Word (не важно откуда) заполнил и показал пользователю. Ничего передавать на клиент не надо, лепота))
15. Kirill_K 05.11.15 15:51
А как у вас в варианте с ActiveDocument работает на сервере эта конструкция?

Макет = ПолучитьМакет("ПисьмоНаОплатуWord");
        
        MSWord = Макет.Получить();


В макет у меня попадает ОболочкаActiveDocument, а Получить() падает с ошибкой вызова метода контекста.
В СП написано, что для этого типа метод Получить() доступен только на толстом клиенте.
16. Валерий К (klinval) 05.11.15 18:22
(15) Kirill_K, у меня рабочего кода на руках нет, ибо сейчас в отпуске. Сделал обработку (см вложение). где всего пару строк кода. У меня дома на 8.2 (файловая база) не падает на методе "Получить()"! На работе практически самая свежая 8.3 и там тоже никакой ошибки на этой строчке (файловая или клиент-серверная).
Тупо в обработке нажмите на "Команда1" - будет ошибка?
Прикрепленные файлы:
СоздатьТестWord.epf
17. Kirill_K 06.11.15 10:41
(16) 8.2 клиент-серверная падает, а в файловой 8.3 ком-объект создается.
Значит, обновляться нужно, спасибо.
18. Валерий К (klinval) 06.11.15 11:37
(17) Kirill_K, код который написан у меня в статье я писал для клиент-серверной 8.3.6. Всё работало. Поэтому после обновления возможно и у вас заработает. Но для чистоты эксперимента я когда выйду на работу попробую на клиент-серверной обработку из 16 сообщения.
19. Валерий К (klinval) 09.11.15 15:34
(17) Kirill_K, попробовал в серверном режиме на 8.3.6.2332: ничего не вырубается. После строчки:
MSWord = Макет.Получить();

в переменной MSWord появляется COMОбъект. Походу Вам правильный совет - это обновиться.
Кстати в справке даже в 8.3.6.2332 написано, что метод Получить() якобы работает только в толстом клиенте:)
21. Михаил Горячев (gmtv) 20.12.15 21:28
Не пойму в чем дело?
ошибка при вызове метода контекста save типы не совпадают
22. Валерий К (klinval) 22.12.15 09:17
(21) gmtv, есть пример-обработка, выслал её в личку.
23. Михаил Михалыч (micha26) 03.04.16 12:09
(18) klinval, доброго дня - у меня 8.3.6 клиент сервер - запустил вашу обработку стопорится на Получить(). В чем может быть проблема?
24. Валерий К (klinval) 04.04.16 09:12
(23) micha26,
1. У вас офис на сервере установлен?
2. Если да, то на пользователе под которым работает агент 1С нормально запускается?
3. Обработка из 16-го сообщения работает?
25. Михаил Михалыч (micha26) 04.04.16 12:40
(24) klinval, офис установлен, пользователю даны права на запуск офиса, нет стопорится также.
26. Валерий К (klinval) 04.04.16 13:28
(25) micha26, обработка маленькая, на какой из строк стопорится?:
	Word = Новый COMОбъект("Word.Application");
	Word.Visible=1;
	Документ = Word.Documents.Add();
	ИмяВрем = ПолучитьИмяВременногоФайла(".docx");
	Документ.SaveAs(ИмяВрем);
	Сообщить(ИмяВрем);
...Показать Скрыть

И ещё: а если в обработке переделать процедуру с серверной на клиентскую: тоже не работает?
27. Михаил Михалыч (micha26) 06.04.16 18:10
На SaveAs, без разницы клиент или серверная.
28. Валерий К (klinval) 06.04.16 21:33
(27) micha26,
Это решение пробовали?:
Возможная ошибка на сервере при вызове метода 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
29. Михаил Михалыч (micha26) 07.04.16 16:01
(28) klinval, да ходил по этой ссылке, не помогло. Макет с двоичными данными делал та же фигня получается.
30. Валерий К (klinval) 08.04.16 09:07
(29) micha26, даже не знаю что тогда... Такое ощущение, что проблема не на стороне 1С. Может антивирус или брандмауэр что-то блокирует? Попробуйте хотя-бы локально все по-вырубать и на клиенте SaveAS() сделать.
31. Михаил Михалыч (micha26) 08.04.16 15:12
(30) klinval, отрубал все для примера сделал следующее:

&НаСервере
Функция ПолучитьСтруктуруДанных()
    ФайлWord = Справочники.Партнеры.ПолучитьМакет("РазовыйДоговорДв"); //двоичные данные на актив забил болт
    АдресФайла = ПоместитьВоВременноеХранилище(ФайлWord);
    Возврат АдресФайла;
КонецФункции
&НаКлиенте
Процедура ПечатьРазовогоДоговора(Команда)
          ПутьКФайлу = "D:\Doc\Разовый договор.doc";
          СтруктураДанных = ПолучитьСтруктуруДанных(); // значение = e1cib/tempstorage/7230a82d-ab89-4e08-b05a-e7122515ab93
          ФайлWord = ПолучитьИзВременногоХранилища(ПутьКФайлу); // Ошибка при вызове метода контекста (ПолучитьИзВременногоХранилища)
//всякая ересь по получению данных
...Показать Скрыть

Один хрен не работает...куда копать уже не представляю.
32. Валерий К (klinval) 09.04.16 23:34
(31) micha26, похоже у вас ошибка в строчке:
//На момент исполнения кода в переменной ПутьКФайлу содержится значение:"D:\Doc\Разовый договор.doc"
ФайлWord = ПолучитьИзВременногоХранилища(ПутьКФайлу);

Вместо ПутьКФайлу вам нужно использовать СтруктураДанных
33. Михаил Михалыч (micha26) 10.04.16 10:15
(32) klinval, да похоже верно - проверю.
34. Михаил Михалыч (micha26) 10.04.16 12:24
(33) micha26,
&НаКлиенте
Процедура ПечатьРазовогоДоговора(Команда)
          ПутьКФайлу = "D:\Doc\Разовый договор.doc";
          СтруктураДанных = ПолучитьСтруктуруДанных(); 
		  ФайлWord = ПолучитьИзВременногоХранилища(СтруктураДанных);
		  ФайлWord.Записать(ПутьКФайлу); // Файл записывается в папку
		  Попытка
		  Word = Новый COMОбъект("Word.Application");
	      Исключение
		  Сообщить(ОписаниеОшибки());
		  Возврат;
	      КонецПопытки;
	      Договор = Word.Open(ПутьКФайлу); // Здесь останавливается Word.Open - метод объекта не обнаружен в принципе можно убрать на фиг.
КонецПроцедуры
...Показать Скрыть

Что-то я нить потерял, как теперь данные заполнить в документе.
35. Михаил Михалыч (micha26) 10.04.16 21:39
(34) Вообщем переделал все на фиг - сохраняю шаблоны в выделенную папку на сервере, делаю запрос на сервере, сохраняю значения в реквизиты для печати и на клиенте заменой загоняю значения в шаблон. Шустро работает.
36. Валерий К (klinval) 11.04.16 09:17
(34) micha26,
Вместо Word = Новый COMОбъект("Word.Application"); и Open(ПутьКФайлу); можно использовать Документ = ПолучитьCOMОбъект(ИмяВрем); (как в принципе и написано у меня в статье)
          ФайлWord.Записать(ПутьКФайлу); // Файл записывается в папку
          Попытка
          Word = Новый COMОбъект("Word.Application");
          Исключение
          Сообщить(ОписаниеОшибки());
          Возврат;
          КонецПопытки;
          Договор = Word.Open(ПутьКФайлу);
...Показать Скрыть

ИмяВрем = ПолучитьИмяВременногоФайла(".docx");
            ДвоичныеДанныеМакета.Записать(ИмяВрем);
            Попытка
                Документ = ПолучитьCOMОбъект(ИмяВрем); 

Что касается метода Open: он вроде используется так: Word.Documents.Open(ПутьКФайлу).

37. Михаил Михалыч (micha26) 12.04.16 09:24
(36) klinval, я таким макаром сделал:
&НаКлиенте
	    Попытка
	        Word = Новый COMОбъект("Word.Application"); 
	    Исключение
	        Сообщить("Ошибка: " + ОписаниеОшибки(), СтатусСообщения.Внимание);
	    КонецПопытки;
	    ЗаполнитьДанныеДоговора(); //запрос на сервере
	    //Путь к шаблону
	    Word.Documents.Add("D:\Doc\РазовыйДоговор.doc");
	    ШаблонWord = Word.ActiveDocument;
            //далее заполнение заменой
...Показать Скрыть

Как бы заработало и шаблоны шустро править можно
38. Валерий К (klinval) 12.04.16 09:57
(37) micha26, ну в принципе как вариант. Я в альтернативах в конце статьи об этом варианте тоже упомянул:
2. Можно сохранить файл Word в общую папку и с клиента его просто получать. Главное, чтобы файл никто не удалил.
39. Михаил Михалыч (micha26) 12.04.16 12:16
(38) klinval, за что вам премного благодарен ))))
40. Михаил Михалыч (micha26) 14.04.16 14:59
А кстати если кому интересен полный код стучитесь в личку.
41. Арсений Прялкин (CeHbKA) 10.08.16 10:02
Прекрасная статья! Всё понятно и доходчиво написано.

По правде говоря мне пришло уведомление на гневный комментарий в моей теме со ссылкой на вашу статью где меня "ну просто опустили" (дословная цитата). Странно, прочитал всё от корки до корки, нигде никто меня не опускал)))) Гневный комментарий тоже удалён))) Видимо его автор сильно неуверен в себе, раз позволяет себе такое.

Насчёт кода на сервере, которым я записывал файл, вот он (в начале лета приводил в порядок всю свою базу наработок):
//Получает макет ActiveDocument и записывает его в файл
Функция ПолучитьМакет(Документ) Экспорт
	
	Каталог = ПараметрыСеанса.ТекущийПользователь.РабочийКаталог;
	Каталог = ?(Прав(Каталог,1) = "\", Каталог, Каталог+"\");
	Если ЗначениеЗаполнено(Каталог) Тогда
		Попытка
			ПолноеИмяФайла = Каталог+Документ.Номер+".doc";
			Макет = Документы.ДоговорыКонтрагентов.ПолучитьМакет("ActiveDocument");
			Макет.Записать(ПолноеИмяФайла);
			
			Возврат ПолноеИмяФайла;						
		Исключение
			Сообщение = Новый СообщениеПользователю;
			Сообщение.Текст = "Не удалось записать файл "+ОписаниеОшибки();		
			Сообщение.Сообщить(); 
			Возврат Неопределено;			
		КонецПопытки; 			
	Иначе
		Сообщение = Новый СообщениеПользователю;
		Сообщение.Текст = "Не указан каталог. Сохранение файла невозможно";
		Сообщение.Сообщить(); 
		Возврат Неопределено;
	КонецЕсли; 	
КонецФункции // ()
...Показать Скрыть


Спасибо, что поставили ссылку на статью. Рад, что хоть чем-то смог помочь. Сейчас отредактирую её и приведу данный листинг кода.
42. Валерий К (klinval) 10.08.16 10:19
(41) CeHbKA,
Макет = Документы.ДоговорыКонтрагентов.ПолучитьМакет("ActiveDocument");
Макет.Записать(ПолноеИмяФайла);

Так вот как оказывается можно ещё сохранить! Не заметил, что у ОболочкаActiveDocument есть метод Записать. Обязательно обновите статью, т.к. многие сваливаются на методе SaveAs, а Записать() у ОболочкаActiveDocument фактически является альтернативой