Почему падает платформа 1С при вызове сервера из события ПриАктивизацииСтроки и как это исправить

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

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

Суть проблемы: почему происходит дамп?

Рассмотрим типовой сценарий: в форме есть ДинамическийСписок или программно созданная ТаблицаФормы. В модуле формы прописан обработчик события активации строки, который обращается к серверной процедуре с директивой &НаСервере. Платформа может завершить работу без сообщений об ошибках сразу после выполнения процедуры. Рассмотрим причины подробнее.

1. Бесконечная рекурсия событий интерфейса. Это самая частая причина. Когда мы вызываем контекстный серверный метод (&НаСервере), платформа передает весь контекст формы на сервер и возвращает его обратно. После возврата контекста клиентское приложение обязано синхронизировать состояние элементов управления. В процессе этой синхронизации таблица может «перерисоваться» или обновить фокус, что снова инициирует событие ПриАктивизацииСтроки. В итоге получается замкнутый цикл, который переполняет стек вызовов на уровне системных библиотек отрисовки (C++), что и приводит к дампу.

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

3. Прямой запрет в документации. Фирма 1С прямо указывает, что в обработчике ПриАктивизацииСтроки нельзя изменять свойства, которые могут привести к серверному вызову, и не рекомендуется использовать контекстные серверные методы.

Решение 1: Использование обработчика ожидания

Проанализируем самый надежный способ — разрыв потока выполнения. Вместо прямого вызова сервера мы будем «заказывать» этот вызов с небольшой задержкой. Это позволит текущему событию ПриАктивизацииСтроки корректно завершиться, и только после этого выполнится наш код.

Разберем реализацию по шагам:

  1. В обработчике события ПриАктивизацииСтроки подключаем обработчик ожидания.
  2. Создаем отдельную процедуру, которая будет выполнять серверную работу.
  3. Обязательно проверяем, изменилась ли строка на самом деле, чтобы избежать лишних срабатываний при потере фокуса формой.

Посмотрим на пример кода:


&НаКлиенте
Перем ТекущийИдентификаторСтроки; // Переменная для кэширования

&НаКлиенте
Процедура СписокПриАктивизацииСтроки(Элемент)
    
    // 1. Проверяем, что строка действительно сменилась
    Если ТекущийИдентификаторСтроки = Элемент.ТекущаяСтрока Тогда
        Возврат;
    КонецЕсли;
    
    ТекущийИдентификаторСтроки = Элемент.ТекущаяСтрока;
    
    // 2. Отключаем старый обработчик (если пользователь быстро листает список)
    ОтключитьОбработчикОжидания("Подключаемый_ОбновитьДанныеНаСервере");
    
    // 3. Подключаем новый вызов с минимальной задержкой
    ПодключитьОбработчикОжидания("Подключаемый_ОбновитьДанныеНаСервере", 0.1, Истина);
    
КонецПроцедуры

&НаКлиенте
Процедура Подключаемый_ОбновитьДанныеНаСервере()
    // Теперь вызываем сервер безопасно
    ОбновитьДанныеНаСервере();
КонецПроцедуры

&НаСервере
Процедура ОбновитьДанныеНаСервере()
    // Логика обновления связанных данных
КонецПроцедуры

Решение 2: Проверка «дребезга» через флаги

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


&НаКлиенте
Процедура ТаблицаПриАктивизацииСтроки(Элемент)
    
    Если ЭтаФорма.ВыполняетсяОбновление Тогда
        Возврат;
    КонецЕсли;
    
    ЭтаФорма.ВыполняетсяОбновление = Истина;
    
    Попытка
        ВыполнитьСервернуюЗадачу();
    Исключение
        // Обработка ошибок
    КонецПопытки;
    
    ЭтаФорма.ВыполняетсяОбновление = Ложь;
    
КонецПроцедуры

Важно: Этот способ менее надежен, чем обработчик ожидания, так как он не решает проблему «перерисовки» элементов управления платформой после возврата контекста.

Решение 3: Серверные вызовы без контекста

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

Проанализируем пример:


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

&НаСервереБезКонтекста
Функция ПолучитьИнформациюНаСервере(ТоварСсылка)
    // Здесь выполняем запрос к БД
    Возврат "Остаток: " + ТоварСсылка.Остаток;
КонецФункции

Подведем итоги

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

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

← На главную