Содержание
Все ниже описанное касается управляемого приложения.
Начиная с версии 8.3.8 фирма 1С изменила подход к завершению работы и закрытию приложения. Новомодные веяния – ли, ошибки при работе с WEB интерфейсом – ли, но теперь со всех обработчиков событий, используемых нами при закрытии – «ПередЗавершениемРаботыСистемы» и «ПриЗавершенииРаботыСистемы», сервер не доступен. Рассмотрим, как теперь работают данные обработчики.
Обработчик: ПередЗавершениемРаботыСистемы(Отказ, ТекстПредупреждения)
Как мы помним возникает перед завершением программы, до закрытия главного окна. Если «Отказ» равен «Ложь», то программа просто закрывается, не выдавая никаких вопросов. В нем можно сделать всяческие проверки, все функции должны быть только клиентские (В процессе завершения работы приложения запрещены серверные вызовы и открытие окон.) и может быть установлен отказ от выхода из программы. И вот тут появляется первый подвох. Установление «Отказ» в Истина не отменяет выход из системы, а только указывает, что нужно выдать платформенный диалог, в котором будет предложено выйти или остаться с текстом из параметра «ТекстПредупреждения»:
Диалог завершения программы
И если пользователь нажмет «Завершить работу», все ваши проверки идут лесом. Платформа просто закрывает приложение не зависимо ни от чего. Отловить этот момент в коде нельзя, это все происходит на уровне платформы.
Если же пользователь нажмет на «Продолжить работу», то возможны два варианта событий:
- Если вы внутри вызова функций из данной процедуры подключали обработчик ожидания
Подключаем обработчик 1С (Код)
1 ПодключитьОбработчикОжидания(«ИмяФункцииОбработчикаОжидания», 0.1, Истина); тогда программа перейдет на этот обработчик, и выполнятся все действия которые вы в нем задали, даже вызов серверных функций.
- Если же вы этого не делали, программа просто продолжит работу.
Ответ на первый глобальный русский вопрос «кто виноват?» понятен. Я постараюсь ответить на второй «что делать?». Если нам надо что то зафиксировать в базе, например информацию о работе пользователей в системе. Кто когда заходил, кто когда вышел.
Решение однако лежит вообще в другой плоскости, а именно, в регламентном задании которое периодически будет опрашивать систему на активных пользователей, и фиксировать что такой то пользователь уже отсутствует в списке активных.
Для опроса системы у нас есть функция ПолучитьСеансыИнформационнойБазы(), которая возвращает массив сеансов. А для фиксации, что пользователя в сеансах уже нет нужно создавать новый регистр. Я предлагаю периодический (в пределах секунды) РС с двумя измерениями «Пользователь» и «НомерСеанса», в ресурсах же должно быть как минимум поле «ЗавершениеСеанса» по которому мы будем понимать что пользователь нами не обработан или обработан но активен.
Структура регистра
Процедура для регламентного задания 1С (Код)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 | Процедура ОбработатьСеансыАктивныхПользователей() Экспорт ТекущаяДатаСеанса = ТекущаяДатаСеанса(); ТабАктивныхПользователей = Новый ТаблицаЗначений; ТабАктивныхПользователей.Колонки.Добавить(«Пользователь», Новый ОписаниеТипов(«СправочникСсылка.Пользователи»)); ТабАктивныхПользователей.Колонки.Добавить(«НомерСеанса», Новый ОписаниеТипов(«Число»)); СеансыИнформационнойБазы = ПолучитьСеансыИнформационнойБазы(); Для Каждого СеансИБ Из СеансыИнформационнойБазы Цикл //Фоновые задания или неавторизованных пользователей пропускаем Если СеансИБ.ПолучитьФоновоеЗадание() <> Неопределено ИЛИ (ТипЗнч(СеансИБ.Пользователь) = Тип(«ПользовательИнформационнойБазы») И НЕ ЗначениеЗаполнено(СеансИБ.Пользователь.Имя)) Тогда Продолжить; КонецЕсли; АктивныйПользователь = ДобавитьЗаписьАвторизацииПользователей(СеансИБ, ТекущаяДатаСеанса); Если ЗначениеЗаполнено(АктивныйПользователь) Тогда НоваяСтрока = ТабАктивныхПользователей.Добавить(); НоваяСтрока.Пользователь = АктивныйПользователь; НоваяСтрока.НомерСеанса = СеансИБ.НомерСеанса; КонецЕсли; КонецЦикла; Запрос = Новый Запрос; Запрос.Текст = «ВЫБРАТЬ | ккПериодическиеСведенияОПользователяхСрезПоследних.Период КАК Период, | ккПериодическиеСведенияОПользователяхСрезПоследних.Пользователь КАК Пользователь, | ккПериодическиеСведенияОПользователяхСрезПоследних.НомерСеанса КАК НомерСеанса |ИЗ | РегистрСведений.ккПериодическиеСведенияОПользователях.СрезПоследних( | &ДатаРегистрации, | НЕ (Пользователь, НомерСеанса) В (&ТабРаботающих) | И ЗавершенияСеанса = ДатаВремя(1,1,1)) КАК ккПериодическиеСведенияОПользователяхСрезПоследних»; Запрос.УстановитьПараметр(«ДатаРегистрации», ТекущаяДатаСеанса); Запрос.УстановитьПараметр(«ТабРаботающих», ТабАктивныхПользователей); Результат = Запрос.Выполнить(); Выборка = Результат.Выбрать(); Пока Выборка.Следующий() Цикл НаборЗаписей = РегистрыСведений.ккПериодическиеСведенияОПользователях.СоздатьНаборЗаписей(); НаборЗаписей.Отбор.Период.Установить(Выборка.Период); НаборЗаписей.Отбор.Пользователь.Установить(Выборка.Пользователь); НаборЗаписей.Отбор.НомерСеанса.Установить(Выборка.НомерСеанса); НаборЗаписей.Прочитать(); Для каждого СведенияОПользователе Из НаборЗаписей Цикл СведенияОПользователе.ЗавершенияСеанса = ТекущаяДатаСеанса; СведенияОПользователе.ВремяВСеансе = СведенияОПользователе.ЗавершенияСеанса — СведенияОПользователе.Период; КонецЦикла; Попытка Если НаборЗаписей.Количество() Тогда НаборЗаписей.Записать(); КонецЕсли; Исключение КонецПопытки; КонецЦикла; КонецПроцедуры |
Процедура обработки регистра 1С (Код)
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 | Функция ДобавитьЗаписьАвторизацииПользователей(СеансИБ, ТекущаяДатаСеанса) Экспорт ТекущийПользователь = НайтиСсылкуПоИдентификаторуПользователя(СеансИБ.Пользователь.УникальныйИдентификатор); Если НЕ ЗначениеЗаполнено(ТекущийПользователь) Тогда Возврат Неопределено; КонецЕсли; Запрос = Новый Запрос; Запрос.Текст = «ВЫБРАТЬ ПЕРВЫЕ 1 | 1 КАК Поле |ИЗ | РегистрСведений.ккПериодическиеСведенияОПользователях.СрезПоследних( | &ДатаРегистрации, | Пользователь = &Пользователь | И НомерСеанса = &НомерСеанса | И ЗавершенияСеанса = ДАТАВРЕМЯ(1, 1, 1)) КАК ккПериодическиеСведенияОПользователяхОстатки»; Запрос.УстановитьПараметр(«ДатаРегистрации», ТекущаяДатаСеанса); Запрос.УстановитьПараметр(«Пользователь», ТекущийПользователь); Запрос.УстановитьПараметр(«НомерСеанса», СеансИБ.НомерСеанса); Результат = Запрос.Выполнить(); НаборЗаписей = РегистрыСведений.ккПериодическиеСведенияОПользователях.СоздатьНаборЗаписей(); НаборЗаписей.Отбор.Период.Установить(СеансИБ.НачалоСеанса); НаборЗаписей.Отбор.Пользователь.Установить(ТекущийПользователь); НаборЗаписей.Отбор.НомерСеанса.Установить(СеансИБ.НомерСеанса); НаборЗаписей.Прочитать(); Если НЕ Результат.Пустой() Тогда Если НаборЗаписей.Количество() Тогда СведенияОПользователе = НаборЗаписей; СведенияОПользователе.ВремяВСеансе = ТекущаяДатаСеанса — СведенияОПользователе.Период; КонецЕсли; Иначе СведенияОПользователе = НаборЗаписей.Добавить(); СведенияОПользователе.Период = СеансИБ.НачалоСеанса; СведенияОПользователе.Пользователь = ТекущийПользователь; СведенияОПользователе.НомерСеанса = СеансИБ.НомерСеанса; СведенияОПользователе.Подключение = 1; СведенияОПользователе.ВремяВСеансе = ТекущаяДатаСеанса — СведенияОПользователе.Период; СведенияОПользователе.ИмяПриложения = ПредставлениеПриложения(СеансИБ.ИмяПриложения); СведенияОПользователе.ИмяКомпьютера = СеансИБ.ИмяКомпьютера; КонецЕсли; Попытка НаборЗаписей.Записать(); Исключение КонецПопытки; Возврат ТекущийПользователь; КонецФункции Функция НайтиСсылкуПоИдентификаторуПользователя(Идентификатор) // Нет доступа к разделенному справочнику из неразделенного сеанса. Если ОбщегоНазначения.РазделениеВключено() И Не ОбщегоНазначения.ДоступноИспользованиеРазделенныхДанных() Тогда Возврат Неопределено; КонецЕсли; Запрос = Новый Запрос; ШаблонТекстаЗапроса = «ВЫБРАТЬ | Ссылка КАК Ссылка |ИЗ | %1 |ГДЕ | ИдентификаторПользователяИБ = &Идентификатор»; ТекстЗапросаПоПользователям = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонТекстаЗапроса, Метаданные.Справочники.Пользователи.ПолноеИмя()); ТекстЗапросаПоВнешнимПользователям = СтроковыеФункцииКлиентСервер.ПодставитьПараметрыВСтроку(ШаблонТекстаЗапроса, Метаданные.Справочники.ВнешниеПользователи.ПолноеИмя()); Запрос.Текст = ТекстЗапросаПоПользователям; Запрос.Параметры.Вставить(«Идентификатор», Идентификатор); Результат = Запрос.Выполнить(); Если НЕ Результат.Пустой() Тогда Выборка = Результат.Выбрать(); Выборка.Следующий(); Возврат Выборка.Ссылка; КонецЕсли; Запрос.Текст = ТекстЗапросаПоВнешнимПользователям; Результат = Запрос.Выполнить(); Если НЕ Результат.Пустой() Тогда Выборка = Результат.Выбрать(); Выборка.Следующий(); Возврат Выборка.Ссылка; КонецЕсли; Возврат Справочники.Пользователи.ПустаяСсылка(); КонецФункции |
Обработчик: ПриЗавершенииРаботыСистемы()
Привожу полный текст справки:
«Описание:
Возникает перед завершением работы в режиме управляемого приложения после закрытия главного окна. В данной процедуре могут быть выполнены действия, необходимые при выходе из программы.
Примечание:
В данной процедуре не допускаются:
- открытие форм и других окон,
- использование серверных вызовов,
- использование асинхронных вызовов.
Не поддерживаются:
- выдача сообщений,
- установка текста в панели состояния,
- другие действия, требующие наличия главного окна.»
Вот честно, не понимаю вообще какие действия можно осуществить в данной процедуре? Да мы можем написать кучу кода, что-то сделать с файлами, но ничего сообщить пользователю, что то сделать с базой – мы уже не можем.
Резюме
Резюмируя данную статью хочу поблагодарить фирму 1С, что не дает нам соскучиться и всегда на страже нашего свободного времени.
Описывает процедуру для повторного использования
Синтаксис
Описание процедуры во встроенном языке 1С:Предприятие 7.7 имеет следующий синтаксис:
Процедура ИмяПроцедуры(Параметр1,…,Параметр1]) //блок описания локальных переменных процедуры //блок операторов процедуры КонецПроцедуры
А также альтернативный англоязычный синтаксис:
Procedure ProcedureName(Attr1,…,Attr1]) //local variables definition //function operators EndProcedure
Описание
Конструкция Процедура…КонецПроцедуры позволяет разработчику определить собственный исполняемый оператор, выполняющий вспомогательные вычисления, но не возвращающий результат вычисления в точку вызова. Определение процедуры начинается с ключевого слова Процедура и заканчивается ключевым словом КонецПроцедуры. За ключевым словом Процедура обязательно следует идентификатор процедуры и блок определения формальных параметров. Описывать параметры процедуры вовсе не обязательно. Обязательно лишь указать обязательный атрибут процедуры — круглые скобки.
Ключевое слово Экспорт используется в глобальном модуле для расширения области видимости процедуры. Процедура, определенная как экспортная, будет доступна в любом другом программном модуле конфигурации.
Ключевое слово Далее используется при предварительном объявлении процедуры, чтобы сказать синтаксическому анализатору, что полное определение процедуры располагается дальше по тексту модуля.
Сразу за блоком формальных параметров и до ключевого слова КонецПроцедуры располагается тело процедуры — объявления локальных переменных процедуры и последовательность исполняемых операторов. Переменные можно объявлять до первого исполняемого оператора. В случае нарушения этого требования, синтаксический анализатор выдаст ошибку: «Объявления переменных должны быть расположены в начале модуля, процедуры или функции».
В качестве операторов в теле процедуры можно использовать системные процедуры и функции, а также другие вспомогательные процедуры и функции, определенные программистом в конфигурации.
Также в теле процедуры можно использовать оператор Возврат, который прекращает выполнение процедуры и передает управление в точку вызова процедуры.
Обратите внимание, что после ключевого слова КонецПроцедуры нет разделителя операторов ; (точка с запятой). Так вышло потому, что конструкция Процедура…КонецПроцедуры является по своей природе операторными скобками, а не оператором. Нарушение этого требования может привести к синтаксической ошибке: «Определения процедур и функций должны размещаться перед операторами основной программы».
Время от времени у меня возникает ощущение, что с какой-то проблемой никто, кроме меня, не сталкивался. То ли остальные не считают это проблемой и не видят смысла обсуждать, то ли секретную инструкцию вовремя прочитали и тем более молчат. В общем, никого вокруг проблема не беспокоит, хотя надо бы. А мне и спросить не у кого.
Именно такая ситуация возникла, когда я несколько лет назад занялся администрированием серверов 1С. А конкретно проблема заключалась в потерянных сеансах пользователей, мешающих обслуживать базы на сервере. Коллеги в таком случае просто открывали консоль администрирования и жали мышкой на красный крестик, да и то лишь когда им позвонят и попросят удалить сеансы.
Меня такой подход совершенно не устраивал, тем более что приближались новогодние каникулы. Бухгалтера, как водится, собирались ударно поработать чуть ли не со 2 января, а я вовсе не горел желанием чего-то там удалять мышкой по крестику, отвлекаясь от личных дел.
Средства, предусмотренные на этот случай разработчиками платформы (Конфигуратор – Администрирование – Параметры ИБ – Время засыпания пассивного сеанса, Время завершения спящего сеанса), почему-то работали как хотели и не гарантировали результат. Возможно, они до сих пор точно так же халтурят. Давно не проверял за ненадобностью.
Когда я стал искать в интернете готовый рецепт, довольно легко нашел, как удалять соединения, но не сеансы. Недоумение переросло в беспокойство. У меня-то проблем с соединениями не было, у меня проблема с сеансами! У меня что, платформа какая-то не такая, как у всех?
Но все же через синтакс-помощник и путем экспериментов решение было найдено достаточно быстро. Первый вариант внешней обработки был готов к нужному моменту, и результат стоил того. С тех пор накопился реальный опыт применения, обработка неспешно обросла опциями и в своем нынешнем виде прикреплена к этой статье. А здесь хотелось бы поговорить о том, как этим средством пользоваться.
Алгоритм удаления
Алгоритм, предлагаемый платформой 1С для получения сведений о сеансах, а заодно и удаления, в схематичном виде выглядит так:
Процедура ОбходКластеров(Сервер1С, База, АдминКластера = «», ПарольАдминКластера = «») // Все аргументы — тип Строка Коннектор = Новый COMОбъект(«v83.COMConnector»); Исключение … Агент = Коннектор.ConnectAgent(Сервер1С); Исключение … Кластеры = Агент.GetClusters(); Для каждого Кластер из Кластеры Цикл Агент.Authenticate(Кластер, АдминКластера, ПарольАдминКластера); Исключение … Сеансы(Агент, Кластер, База); КонецЦикла; КонецПроцедуры // ОбходКластеров() Процедура Сеансы(Агент, Кластер, База) Сеансы = Агент.GetSessions(Кластер); Для каждого Сеанс из Сеансы Цикл Если Сеанс.InfoBase.Name = База Тогда Агент.TerminateSession(Кластер, Сеанс); Исключение … КонецЕсли; КонецЦикла; КонецПроцедуры // Сеансы()
Здесь АдминКластера и ПарольАдминКластера – это логин и пароль администратора кластера серверов 1С. На практике их обычно можно не задавать. Значения по умолчанию – пустая строка.
Посмотрите еще раз на процедуру Сеансы(). В свойствах объекта Сеанс содержится все, что нужно, чтобы отличить одни сеансы от других. Ну, кроме того, чего в платформе все равно нет. А нет там хоть какого-то признака потерянных сеансов.
Есть еще свойства, значения которых сообщаются только для толстого клиента, конфигуратора и фонового задания. Это номер процесса (Сеанс.Process.PID) и номер соединения (Сеанс.Connection.ConnID). Учитывая все большее распространение тонкого клиента, эти сведения приходится признать бесполезными. Скорее всего, таким способом вам не удастся выяснить связь конкретного процесса rphost.exe с какой-либо базой. Кстати, в консоли администрирования мы наблюдаем ту же картину.
А еще жаль, что в длинном списке свойств нет явного указания на тот самый сеанс, в котором работает наша программа. Ведь его ни в коем случае нельзя удалять. Значит, придется самим организовать паспортный контроль. Например, вот так:
СтрокаСоединения = СтрокаСоединенияИнформационнойБазы(); СтрокаСоединения = СтрЗаменить(СтрокаСоединения, «;», Символы.ПС); ФлагСерверныйРежим = (Найти(Врег(СтрокаСоединения), «SRVR=») = 1); Если ФлагСерверныйРежим Тогда // Имя базы содержится в подстроке Ref=»имя_базы» внутри СтрокаСоединения // Далее имя базы будет в переменной ТекущийСеанс_ИмяИБ … КонецЕсли; … ТекущийСеанс = ПолучитьТекущийСеансИнформационнойБазы(); … ЭтоТекущийСеанс = Сеанс.InfoBase.Name = ТекущийСеанс_ИмяИБ И (Сеанс.UserName = ТекущийСеанс.Пользователь ИЛИ (Сеанс.UserName = «DefUser» И Строка(ТекущийСеанс.Пользователь) = «»)) И Сеанс.Host = ТекущийСеанс.ИмяКомпьютера И Сеанс.SessionID = ТекущийСеанс.НомерСеанса И Сеанс.StartedAt = ТекущийСеанс.НачалоСеанса И Сеанс.AppID = ТекущийСеанс.ИмяПриложения;
У объекта ТекущийСеанс есть еще свойство НомерСоединения, но надежность этого признака может зависеть от того, когда объекту присваивается значение – в начале работы или непосредственно перед проверкой. Ну, и замечание, сделанное выше, тоже надо иметь в виду.
Впрочем, даже показанный здесь набор условий выглядит избыточным. Наверняка хватило бы имени базы и номера сеанса, ведь остальные свойства – по сути атрибуты сочетания база + сеанс. Но, сдается мне, лучше воздержаться от такой оптимизации. Никто ведь не гарантировал корректность паспортных данных у потерянных сеансов и невозможность случайных совпадений.
Лучше обратите внимание на то, что проверка имени пользователя должна учитывать подвох, возникающий, например, при работе с пустой базой или при запуске фоновых заданий в отсутствие активных пользователей.
Ну, а если кому-то необходимо посмотреть или удалить соединения, то вместо процедуры Сеансы() нужно вызвать процедуру Соединения(), показанную ниже, но тогда еще потребуются логин и пароль администратора информационной базы:
Процедура Соединения(Сервер1С, Коннектор, Агент, Кластер, База, АдминКластера, ПарольАдминКластера, АдминИБ, ПарольАдминИБ) Процессы = Агент.GetWorkingProcesses(Кластер); Для каждого Процесс из Процессы Цикл Порт = Процесс.MainPort; РабПроц = Коннектор.ConnectWorkingProcess(Сервер1С + «:» + Порт); Исключение … РабПроц.AuthenticateAdmin(АдминКластера, ПарольАдминКластера); Исключение … РабПроц.AddAuthentication(АдминИБ, ПарольАдминИБ); Исключение … ИнформационнаяБаза = РабПроц.CreateInfoBaseInfo(); ИнформационнаяБаза.Name = База; СоединенияБазы = РабПроц.GetInfoBaseConnections(ИнформационнаяБаза); Исключение … Для Каждого Соединение Из СоединенияБазы Цикл РабПроц.Disconnect(Соединение); Исключение … КонецЦикла; КонецЦикла; КонецПроцедуры // Соединения()
Разбирая весьма неочевидную последовательность действий, описанную в этой процедуре, можно догадаться, почему в свое время на каком-то форуме нашлась подсказка именно насчет соединений. Для прохождения этого квеста нужен опытный проводник, а с сеансами можно и самому разобраться.
Впрочем, для меня до сих пор остается загадкой, зачем кому-то может потребоваться удалять соединения. Они и сами исправно отваливаются. Лично мне они никогда не мешали.
К тому же, сеанс может быть без соединения, если не нуждается в нем в данный момент. Если сеанс не обращается к кластеру (то есть пользователь бездействует), соединение ему не назначается. Так что для нас объект охоты – сеансы, а не соединения.
Объект охоты
Ну так вот, что, собственно, мы собираемся удалять, если у сеансов нет никакого специально предусмотренного флажка вроде ЭтоПотерянный? Как отличить хороших от плохих?
А никак. Нет ведь флажка. Это и есть правильный ответ. Но меня он совершенно не устраивал.
Поэтому моя обработка удаляет сеансы, которые выглядят потерянными. Первый кандидат на это звание – заснувшие сеансы, созданные тонким или толстым клиентом. Просто с другими клиентами я до сих пор не сталкивался, так что пусть пока будет так.
И пусть такие сеансы удаляются автоматически с некоторой периодичностью, например, раз в минуту, что совсем не трудно реализовать. А также при нажатии кнопки, что еще проще.
И вот тут возникает пара совершенно справедливых вопросов.
Во-первых, как быть с сеансами, заснувшими во время перерыва на обед? Пользователи будут несколько удивлены, вернувшись к рабочим местам. В конце концов им просто надоест каждый день читать сообщения про сеансы, удаленные за ненадобностью.
А во-вторых, как быть с сеансами, которым не спится? Как ни заглянешь в консоль, у них последняя активность вот только что была. Звонишь пользователю – нет никого. Пингуешь компьютер – опять никого. А сеанс все трудится, занят непонятно чем.
В ответ обработка обзавелась парой самых важных опций.
Во-первых, предусмотрен период бездействия, то есть время, в течение которого автоматическое удаление не выполняется. Границы периода задаются с учетом обстоятельств.
Например, если у вас лицензия на 50 подключений, в консоли стабильно наблюдаются 45-48 реальных сеансов, а денег на еще одну лицензию не дают, значит бездействуем только в обед и немного до и после него. Здесь главная задача – обеспечить резерв подключений, чему пользователи будут только рады. Их гораздо больше раздражает невозможность подключиться к базе, когда очень надо.
Если пользователей мало, лицензий гарантированно хватает, к тому же пользователи славятся дружным уходом домой в конце рабочего дня, тогда можно никого не беспокоить весь рабочий день с небольшим запасом.
А если за вашими пользователями замечена склонность работать с утра до ночи, а вам все-таки надо когда-то бэкапить базы, закрывайте интервал с шести утра до трех ночи. С трех до шести спят все, вот тогда и бэкапьте. А кто не спит – пусть получают окошки с сообщениями.
Кроме того, параметр «Время засыпания пассивного сеанса» все-таки чаще работает, чем не работает. Можно увеличить его с традиционных 20 минут до часа, и это сильно сократит количество жалоб.
Вторая важная опция – возможность удалять сеансы, созданные вчера или еще раньше, но не позднее заданного количества часов назад. Последнее условие позволяет не обрушивать ровно в полночь всю работу. По умолчанию задано 12 часов в расчете на тех, кто приступил к работе после обеда.
Не стоит легкомысленно относиться к этому параметру. Мало ли кто засиделся за компом, нервно смотрит на часы и хочет домой. Кофе давно допит, отчетность вот-вот будет сдана, а тут бац – и карета превращается в тыкву. Тут уж не сомневайтесь – утром придет злая мачеха, и вы узнаете о себе много такого, о чем, в принципе, догадывались.
Необходимо сделать важное замечание, связанное с сеансом самой обработки. Очевидно, что ее выполнение не должно зависеть от пользователей. Самый простой способ добиться этого – специально создать пустую базу и запускать обработку поверх нее. Разумеется, в этом случае база с обработкой займет лишний сеанс.
Сеанс, в котором запущена сама обработка, игнорируется. Но тут есть пара нюансов. С одной стороны, ничто не мешает запускать несколько экземпляров обработки с разными настройками, причем каждый в своем сеансе. С другой стороны, при перезапуске сервера сеанс базы с обработкой может восстановиться, но стать недоступным для управления, то есть по сути потерянным.
Из этих и не только этих соображений предусмотрена возможность указывать базы, исключенные из проверки, а для быстрого заполнения списков добавлены галочки «Проверять все базы» и «кроме этой базы». По умолчанию база с обработкой игнорируется.
Со временем появилась еще пара опций, требующихся редко, даже очень редко, но иногда полезных.
Во-первых, иногда коллеги-админы забывают закрыть конфигуратор после обслуживания базы на сервере. Пришлось предусмотреть возможность удалять такие сеансы. Но это только возможность, а вовсе не обязанность!
Только не забудьте, что если вы решили с помощью обработки расчищать дорогу для бэкапа, а бэкап баз 1С делаете запуском конфигуратора из командной строки, то придется как-то развести по времени удаление чужих сеансов конфигуратора и запуск своих. Тут период бездействия будет как раз кстати.
Ну, а во-вторых, один раз за свою не слишком долгую практику я наблюдал потерянные сеансы фоновых заданий. Уж не помню, что там за катаклизм приключился, но сеансы дружно повисли в консоли. Ладно, пусть будет и такая галочка. Опять же только возможность, а не обязанность.
Поскольку здесь обсуждались индивидуальные настройки процесса удаления для каждого конкретного сервера, возникает вопрос, не обзавелась ли моя обработка, как любая уважающая себя программа, файлом конфигурации? Да, есть такой. Назовем его файлом параметров, чтобы не путаться в терминах. Но об этом ниже.
Quick start
В командной строке приложений 1С предусмотрены два очень полезных ключика. Ну, не считая других, разумеется. Это /Execute и /C.
Благодаря им установка и первый запуск моей обработки на сервере выглядят так:
1. Копирую на сервер комплект файлов:
собственно обработка
v8i-файл для ее запуска
файл параметров
cmd-файл для регистрации библиотеки comcntr.dll
2. Создаю пустую базу. Пусть будет emptybase, к примеру.
3. Регистрирую на сервере библиотеку comcntr.dll, если это до сих пор еще не сделано.
4. В меню стартера 1С добавляю готовый v8i-файл запуска базы с обработкой, хотя это можно и не делать.
5. И запускаю.
Где взять файл обработки, сказано в конце статьи.
Файл для регистрации comcntr.dll сделан из файла RegMSC.cmd. В нем просто заменено имя библиотеки. Ну, и запускать его надо в подкаталоге bin каталога нужной версии платформы.
Файл для запуска базы с обработкой выглядит примерно так:
Connect=Srvr=»SRVR»;Ref=»emptybase»; ID=db847d2c-c326-4ece-bc72-0a19833a02dd OrderInList=6359 Folder=/ OrderInTree=393472 External=0 ClientConnectionSpeed=Normal App=Auto WA=1 Version=8.3 AdditionalParameters=/executeD:\EPF\УдалитьПотерянныеСеансы.epf /CD:\EPF\setup.txt
Уверен, вы знаете, как им пользоваться. Самое интересное написано в последней строке.
Ну и наконец, файл параметров. Здесь он называется setup.txt. Одновременно он служит руководством по написанию таких файлов. Вот реальный пример:
// Сервер1С = «SRVR» ИнтервалПовтора = 10 ФлагУдалитьВчерашние = Да СозданНеМенее = 16 ПериодБездействияНачало = 5:00 ПериодБездействияКонец = 2:50 // Если обработка запускается с помощью параметра командной строки /Execute, в // параметре /C можно передать имя файла параметров, содержащего значения // реквизитов формы, отличные от значений по умолчанию // Формат строки файла параметров: // ИмяРеквизита = значение // Значения типа Булево: 1, ДА, ИСТИНА (в любом регистре) — Истина; // 0, НЕТ, ЛОЖЬ (в любом регистре) — Ложь // Значения типа Время: ожидаются форматы ЧЧ:ММ:СС, Ч:ММ:СС, ЧЧ:ММ, Ч:ММ // Пробелы и символы табуляции в начале и конце строки, а также прилегающие к // знаку «=», игнорируются // Строки, начинающиеся не с имени реквизита или не содержащие знак «=» после // имени реквизита, игнорируются, поэтому любая такая строка может быть // комментарием // Строки, содержащие некорректные значения (не соответствуют типу, не входят в // допустимый диапазон, записаны с нарушением формата), игнорируются // Строки, начинающиеся с имен реквизитов, не включенных в список, фактически // игнорируются, так как инициализация этих реквизитов происходит после чтения // файла параметров // Имена и типы значений реквизитов // Сервер1С, Строка — Сервер 1С:Предприятие // СписокБаз, Строка — Информационные базы // ИсключитьБазы, Строка — Базы, исключенные из проверки // Базы можно задать списком, разделенным пробелами, запятыми или точками с // запятой // ФлагЭтотСервер, Булево — Сервер с базой, с которой запущена эта обработка // ФлагВсеБазы, Булево — Проверять все базы, кроме исключенных // ФлагИсключитьЭтуБазу, Булево — Исключить только базу, с которой запущена эта // обработка // ФлагСократитьОтчет, Булево — Сократить отчет // ФлагПовторять, Булево — Автоматически повторять операцию // ИнтервалПовтора, Число, 2, 0, Неотрицательное — Интервал повтора (минут) // ФлагПериодБездействия, Булево — Задан период бездействия // ПериодБездействияНачало, Время — Начало периода бездействия // ПериодБездействияКонец, Время — Конец периода бездействия // ФлагУдалитьВчерашние, Булево — Удалить вчерашние сеансы // СозданНеМенее, Число, 2, 0, Неотрицательное — Сеанс создан не позднее (часов // назад) // ФлагКонфигуратор, Булево — Удалить сеансы конфигуратора // ФлагФоновые, Булево — Удалить сеансы фоновых заданий // АдминКластера, Строка — Администратор кластера серверов 1С // ПарольАдминКластера, Строка — Пароль администратора кластера серверов 1С
Поскольку я в основном работаю в среде Windows, файл сделан в Блокноте в кодировке ANSI. Кто работает в Linux, надеюсь, разберется сам, как тут быть.
Желаю успеха! Мне этот инструмент реально помогает каждый день в течение нескольких лет.