Что такое завершающий буфер 1с

Обновлено: 06.07.2024

Длительные операции на сервере

Область применения: управляемое приложение.

1. При разработке конфигураций следует избегать длительных вызовов из клиентского кода в серверный. Все длительные серверные вызовы, которые могут выполняться более 8 секунд в обычных сценариях работы пользователя, следует выполнять асинхронно, с помощью фонового задания.

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

В противном случае такие вызовы могут привести к потере работоспособности приложения или затруднению работы с ним:

  • браузер может предложить прекратить длительно выполняющийся сценарий, после чего приложение станет неработоспособным;
  • веб сервер может прервать длительное обращение к серверу 1С:Предприятия и вернуть ошибку 504 (шлюз не отвечает);
  • в случае длительного выполнения операции, у пользователя нет возможности отменить ее.

2.1. Общий подход к асинхронному выполнению длительных серверных операций с помощью фонового задания:

    Код, выполняющий длительную обработку данных, располагается в модуле менеджера объекта* или в общем модуле. Результат своей работы он помещает во временное хранилище;


а для прочих мест – выводится блокирующая форма ( РежимОткрытияОкна = БлокироватьОкноВладельца ), на которой размещена декорация с анимированной картинкой и кнопка «Отмена» :

2.2. Асинхронное формирование отчета требуется только для тех отчетов, которые

  • разработаны без использования СКД или с использованием СКД, но с переопределенной процедурой формирования отчета (переопределен обработчик кнопки «Сформировать» или в обработчике модуля отчета ПриКомпоновкеРезультата устанавливается СтандартнаяОбработка = Ложь ).
  • и формирование которых, как правило, занимает длительное время.

Поведение таких отчетов должно быть максимально похожим на поведение отчетов на базе СКД, а именно:

  • форму отчета не следует блокировать на время его формирования;
  • пользователь может изменить настройки и переформировать отчет, не дожидаясь окончания его формирования;
  • при закрытии формы отчета, формирование отчета прерывается.

3. При использовании в конфигурации Библиотеки стандартных подсистем в распоряжении разработчика имеются вспомогательные функции и процедуры общих модулей ДлительныеОперации , ДлительныеОперацииКлиент , а также процедура УстановитьСостояниеПоляТабличногоДокумента общего модуля ОбщегоНазначенияКлиентСервер .

Пример выполнения функции в фоновом задании при использовании в конфигурации Библиотеки стандартных подсистем. В модуле менеджера объекта размещена функция, которая выполняет поиск настроек и возвращает их:

Функция ОпределитьНастройкиУчетнойЗаписи(АдресЭлектроннойПочты, Пароль) Экспорт
.
Возврат Настройки;
КонецФункции

В форме объекта выполняется вызов этой функции в фоновом задании в три этапа:
1) запуск фонового задания на сервере,
2) подключение обработчика завершения фонового задания на клиенте,
3) обработка результата выполнения фонового задания.

&НаКлиенте
Процедура НастроитьПараметрыПодключенияАвтоматически()
// 1. Запуск фонового задания на сервере.
ДлительнаяОперация = НачатьПоискНастроекУчетнойЗаписи();

// 2. Подключение обработчика завершения фонового задания.
ПараметрыОжидания = ДлительныеОперацииКлиент.ПараметрыОжидания(ЭтотОбъект);
Оповещение = Новый ОписаниеОповещения("ПриЗавершенииПоискаНастроек", ЭтотОбъект);
ДлительныеОперацииКлиент.ОжидатьЗавершение(ДлительнаяОперация, Оповещение, ПараметрыОжидания);
КонецПроцедуры

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

// 3. Обработка результата выполнения фонового задания.
&НаКлиенте
Процедура ПриЗавершенииПоискаНастроек(Результат, ДополнительныеПараметры) Экспорт

Если Результат = Неопределено Тогда // Пользователь отменил задание.
Возврат;
КонецЕсли;

Если Результат.Статус = "Ошибка" Тогда
ВызватьИсключение Результат.КраткоеПредставлениеОшибки;
КонецЕсли;

Настройки = ПолучитьИзВременногоХранилища(Результат.АдресРезультата);
УдалитьИзВременногоХранилища(Результат.АдресРезультата);
УстановитьНастройкиУчетнойЗаписи(Настройки);

Методическая рекомендация (полезный совет)

3.1. При каждом выполнении фонового задания его результат помещается во временное хранилище на время жизни формы:

ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияФункции(УникальныйИдентификатор);
ДлительныеОперации.ВыполнитьФункцию(ПараметрыВыполнения, ПараметрФоновогоЗадания);

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

Настройки = ПолучитьИзВременногоХранилища(Результат.АдресРезультата);
УдалитьИзВременногоХранилища(Результат.АдресРезультата); // Данные во временном хранилище больше не требуются.

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

&НаСервере
Процедура ПриСозданииНаСервере(Отказ)
АдресРезультатаФоновогоЗадания = ПоместитьВоВременноеХранилище(Неопределено, УникальныйИдентификатор); // Резервируем адрес временного хранилища
КонецПроцедуры

&НаСервере
Функция НачатьПоискНастроекУчетнойЗаписи()
ПараметрыВыполнения = ДлительныеОперации.ПараметрыВыполненияФункции(УникальныйИдентификатор);
ПараметрыВыполнения.АдресРезультата = АдресРезультатаФоновогоЗадания; // всегда используем одно и то же временное хранилище

Возврат ДлительныеОперации.ВыполнитьФункцию(ПараметрыВыполнения,
"Справочники.УчетныеЗаписиЭлектроннойПочты.ОпределитьНастройкиУчетнойЗаписи",
АдресЭлектроннойПочты, Пароль);
КонецФункции

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

Если МонопольныйРежим() Тогда
Возврат;
КонецЕсли;

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

Обновление данных ИБ при первом интерактивном запуске программы после обновления конфигурации; Выгрузка данных информационной базы в файл для перехода в сервис; Использования монопольного режима для снижения времени выполнения массовых операций по изменению данных;

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

На время выполнения этого фонового задания следует блокировать весь интерфейс приложения, открывая форму ожидания завершения операции в режиме РежимОткрытияОкна = БлокироватьВесьИнтерфейс. Блокировать интерфейс приложения требуется потому, что на время выполнения задания полноценная работа пользователя с приложением уже невозможна:

  • Если пользователь(*) попытается записать какой-либо объект, это приведет к ошибке (из-за установленного монопольного режима);
  • В ряде случаев могут запускаться фоновые задания в качестве реакции на действия пользователя случае (при поиске в динамическом списке, при вводе по строке, формировании отчетов и пр.), которые также завершатся с ошибкой.
    Кроме того, на самой форме ожидания длительной операции не следует размещать элементы управления, которые могут приводить к запуску таких фоновых заданий. Например: поля ввода, динамические списки и отчеты.

* Примечание: ошибки записи также возникают в тех случаях, когда объекты записываются программно, например, из обработчиков ожидания. В них также следует проверять монопольный режим согласно п.5.

При копировании данных из Excel методом Ctrl+C (ячейки) всегда скопированная строка содержит в конце символ переноса строки. Хочу при активации окна 1С обрабатывать буфер обмена и удалять конечный символ переноса строки и/или таб/пробел.

Варианты которые пришли в голову:
-Обработать форму поиска - не докопался до нее
-Обработать Ctrl+V - без каких-то dll-ок думаю не получится (по крайней мере, у меня не получилось и обработать, и не перебить саму вставку из буфера в нужное место)
-Обработать событие активации самой 1-ски.

Может есть решение у кого-то или идеи - подскажите :)
Спасибо!

(0) может проще обрабатывать уже приОкончРедактирования в поле 1С?
(1) Для примера, может быть поиск по номеру документа в форме списка (в одной из over дофига).
То есть в Excel скопировали номер - в 1С нажали Ctrl+F и Ctrl+V. То есть Никакого "При окончании редактирования" думаю не получится сделать..
(0) согласен даже dll-ки навешать, но только грамотные, потому как сам их писать не шарю :(
Если есть проверенные - скиньте, пожалуйста, буду очень благодарен!

первая же ссылка по большой букве Я вверху

(1) ну если в поиске то да, не получится. Можно простенькое приложение на любом известном вам языке, пусть себе висит в автозагрузке и режет.

Autohotkey поможет.
Простенький скрипт, написанный на Autohotkey.
Следит за изменениями буфера обмена и собирает текст, о чём выводит TrayTip.
При нажатии Win+Ctrl+V и Win+Shift+Insert записывает в буфер обмена всё, что насобирал, «отправляет» активному приложению Ctrl+V или Shift+Insert в соответствии с тем, что было нажато, и выходит.

OnClipboardChange:
; MsgBox clipboard:`n`n%clipboard%
If ( A_EventInfo == 1 and ClipboardCaptureOn) <
; ToolTip % "" ClipboardsCaptured . ": Clipboard changed`nType: " . ClipboardType%A_EventInfo% . "`nContents added: " . clipboard,
return

RemoveToolTip:
ToolTip
return

Но я бы еще попросил, если есть идеи как без установки левого софта сделать. максимум длл готов поставить(

Буфер обмена Windows поддерживает несколько стандартных форматов данных. Например текст и точечные рисунки. Кроме того, приложение может зарегистрировать свой собственный формат и скорее всего 1С:Предприятие пользуется этой возможностью.

Давайте проверим это! Узнать, какие форматы в данный момент содержит буфер обмена, можно с помощью стандартной утилиты Windows 2k/XP clipbrd.exe

Запускаем утилиту и давим ctrl+c на любом объекте дерева метаданных в конфигураторе:

clipbrd.exe

Я копировал реквизиты документа, и платформа поместила в буфер текст "Реквизиты". Кроме того, в меню "Вид" помимо стандартных форматов присутствуют 4 формата с префиксом "1С". Видимо это то что нам нужно :)

Плохо только, что эти форматы недоступны для просмотра. Что в общем логично, т.к. внутреннее их устройство "знает" только платформа 1С.

CLCL при запуске сворачивается в трей с иконкой в виде канцелярской скрепки. Открыть основное окно утилиты можно щелчком на этой скрепке.

ОК. Что нам показывает CLCL?

CLCL

Наиболее интересен формат "1C:MD8 Data". У него говорящее название и данных он содержит больше, чем все остальные форматы, вместе взятые. Внутри явно текст, только CLCL не знает кодировку. Щелкаем правой кнопкой мыши на подопытном формате, выбираем "сохранить как", и сохраняем в текстовый файл.

1С:MD8 Data

Открываем в любимом текстовом редакторе (на скрине Sublime Text 2) и видим, что это действительно текст. Кодировка "родная" для платформы 1С UTF-8 с маркером порядка байтов (BOM)

Итак, перед нами метаданные (в данном случае реквизиты документа). Судя по наличию фигурных скобочек можно догадаться, что это результат работы внутреннего сериализатора платформы 1С. Такой же формат возвращает метод глобального контекста ЗначениеВСтрокуВнутр.

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

Довольно легко можно написать парсер. Например так:

Функция РазобратьТекст ( Источник ) Экспорт
Дерево = Новый ДеревоЗначений ;
Дерево . Колонки . Добавить ( "Значение" );
ТекущаяСтрока = Дерево . Строки . Добавить ();
ТекущаяСтрока = ТекущаяСтрока . Строки . Добавить ();
ТекущийРодитель = ТекущаяСтрока . Родитель ;
ИсходныйТекст = Источник . ПолучитьТекст ();
КоличествоСимволов = СтрДлина ( ИсходныйТекст );
ТекущийСимвол = "" ;
Позиция = 0 ;
Пока ТекущийСимвол <> " Позиция = Позиция + 1 ;
ТекущийСимвол = Сред ( ИсходныйТекст , Позиция , 1 );
КонецЦикла;
Если ТекущийСимвол = " Буфер = "" ;
РежимЧтенияСтроки = Ложь;
Пока Позиция < КоличествоСимволов Цикл
Позиция = Позиция + 1 ;
ТекущийСимвол = Сред ( ИсходныйТекст , Позиция , 1 );
Если НЕ РежимЧтенияСтроки И ТекущийСимвол = " ТекущийРодитель = ТекущаяСтрока ; ТекущийРодитель . Значение = "<. >" ;
ТекущаяСтрока = ТекущаяСтрока . Строки . Добавить ();
ИначеЕсли НЕ РежимЧтенияСтроки И ТекущийСимвол = "," Тогда
Если Буфер <> "" Тогда
ТекущаяСтрока . Значение = Буфер ;
Буфер = "" ;
КонецЕсли;
ТекущаяСтрока = ТекущийРодитель . Строки . Добавить ();
ИначеЕсли НЕ РежимЧтенияСтроки И ТекущийСимвол = ">" Тогда
Если Буфер <> "" Тогда
ТекущаяСтрока . Значение = Буфер ;
Буфер = "" ;
КонецЕсли;
ТекущаяСтрока = ТекущийРодитель ;
ТекущийРодитель = ТекущаяСтрока . Родитель ;
ИначеЕсли ТекущийСимвол = """" Тогда
РежимЧтенияСтроки = НЕ РежимЧтенияСтроки ;
Буфер = Буфер + ТекущийСимвол ;
ИначеЕсли РежимЧтенияСтроки Тогда
Буфер = Буфер + ТекущийСимвол ;
ИначеЕсли НЕ ПустаяСтрока ( ТекущийСимвол )
ИЛИ ( ТекущийСимвол = " " ) Тогда
Буфер = Буфер + ТекущийСимвол ;
КонецЕсли;
КонецЦикла;
КонецЕсли;
Возврат Дерево ;
КонецФункции

Парсер1С8х

Теперь у нас есть возможность "пощупать" структуру файла :)

На скрине видно, что корневой список содержит два вложенных списка. В первом из них хранится количество (первый элемент) и список реквизитов. Каждый реквизит предсталяет из себя структуру свойств. Т.е. все свойства реквизита, которые можно изменить в конфигураторе. Расположение свойств в структуре можно выяснить простым сравнением файлов до и после изменения этих самых свойств.

Пришло время воспользоваться нашими знаниями!

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

Кроме того нам нужна библиотека для работы с буфером обмена: http://files.luaforge.net/releases/jaslatrix/clipboard/1.0.0
(установить можно простым копированием clipboard.dll в каталог ". \Lua\5.1\clibs\" )

Язык Lua был выбран не просто так. Дело в том, что в Lua основным типом данных является хэш-таблица (в 1С аналогом является соответствие), которая имеет очень похожий на нашу структуру конструктор. Выглядит это так:

Таблицу можно обойти циклом:

На языке 1С это выглядело бы так:

Таблица = Новый Соответствие ;
Таблица [ 1 ] = 1 ;
Таблица [ 2 ] = 2 ;
Таблица [ 3 ] = "three" ;
Таблица [ 4 ] = Новый Соответствие ;
Таблица [ 4 ][ 1 ] = 4 ;
Таблица [ 4 ][ 2 ] = 5 ;

Для Каждого Элемент Из Таблица Цикл
Сообщить ( "" + Элемент . Ключ + " " + Элемент . Значение );
КонецЦикла;

Т.е. Lua автоматически назначает целочисленные ключи, начиная с единицы. Таким образом, луашная таблица может эмулировать массив (настоящих массивов в Lua нет). Ключи можно и явно указывать, но нам сейчас это не нужно. Интересующиеся могут почитать документацию: Итак, давайте для начала вытащим из буфера обмена текст в формате 1С:MD8 Data

require 'clipboard' -- подключаем библиотеку
format = < >-- создаем пустую таблицу для хранения соответствия [ИмяФормата - КодФормата]
for k,v in ipairs ( clipboard.getformats ( ) or < >) do
formatname = clipboard.formatname ( v )
if formatname then
format [ formatname ] = v
end
end

data = clipboard.getdata ( format [ "1C:MD8 Data" ] ) -- получаем данные

if data then
print ( data )
end

Теперь нужно заставить Lua работать с этими данными как с родной хэш-таблицей. В этом деле нам поможет одна луашная фича. У нас есть возможность динамически загрузить функцию из строки содержащей луашный код. Например так:

f = loadstring ( "i = i + 1" ) -- получаем функцию
i = 0
f ( ) ; print ( i ) --> выводит 1
f ( ) ; print ( i ) --> выводит 2

Чтобы функция возвращала значение, нужно добавить return:

f = loadstring ( "return i + 1" ) -- получаем функцию возвращающую значение
i = 0
i = f ( ) ; print ( i ) --> выводит 1
i = f ( ) ; print ( i ) --> выводит 2

Также функция может создать и вернуть таблицу:

f = loadstring ( "return >" )
tbl = f ( )
for key, value in pairs ( tbl ) do
print ( key, value )
end

Думаю, вы уже догадались что мы будем делать :)

f = loadstring ( "return" ..data ) -- прим.: две точки означают конкатенацию строк
tbl = f ( )

Но это не будет работать. :(

Lua не понимает UID'ы в тексте и кроме того она не знает UTF-8. К счастью, обе проблемы легко победить. Все UID'ы в тексте можно найти с помощью регулярного выражения и заменить на пустые таблицы <> например.
А кодировка UTF-8 по большому счету вообще не проблема, т.к. все важные для разбора символы кодируются в ней одним байтом и не отличаются от таковых в ASCII. Нам будет мешать только маркер порядка байтов (BOM), но мы можем просто проигнорировать первые три байта:

s = "123456"
print ( s:sub ( 4 ) ) --> 456

Когда мы наконец получим метаданные в виде луашной таблицы, можно будет обращаться к любому элементу примерно так:

print ( tbl [ 1 ] [ 2 ] [ 2 ] [ 1 ] [ 2 ] [ 2 ] [ 2 ] [ 3 ] )

Этот код выведет на экран имя первого реквизита.

Чтобы узнать "адрес" нужного вам элемента, можете воспользоваться этим принтером таблиц:

function print_r ( t, indent, done )
done = done or < >
indent = indent or ''
local nextIndent -- Storage for next indentation value
for key, value in pairs ( t ) do
if type ( value ) == "table" and not done [ value ] then
nextIndent = nextIndent or
( indent .. string.rep ( ' ' , string.len(tostring ( key ) ) + 2 ) ) -- Shortcut conditional allocation
done [ value ] = true
print ( indent .. "[" .. tostring ( key ) .. "] => Table" ) ;
print_r ( value, nextIndent .. string.rep ( ' ' , 2 ) , done )
else
print ( indent .. "[" .. tostring ( key ) .. "] => " .. tostring ( value ) .. "" )
end
end
end

Эта функция выводит таблицу в таком виде (жирным выделен путь к имени первого реквизита):

Читайте также: