Новый лист в книге Excel из кода 1С: COM, Add, Move и Copy

· 9 мин

Содержание

Чтобы добавить лист в Excel из 1С, подключитесь к Excel через Новый COMОбъект("Excel.Application"), откройте или создайте книгу и вызовите у коллекции листов метод Add. Получите объект нового листа, задайте ему имя через свойство Name, а в конце закройте книгу и выйдите из Excel. Всё остальное в теме (позиция листа, копирование шаблона, скорость) — детали вокруг этого вызова.

Частая ошибка новичка: он выгружает данные в первый лист книги и потом удивляется, что «лист по умолчанию» называется Лист1, а при втором запуске данные затирают старые. Один лист на отчёт — не всегда удобно. Ниже разберём, как создавать листы осознанно: с нужным именем, на нужном месте и без зависших процессов Excel.

Что нужно, чтобы 1С вообще могла управлять Excel

COM-подключение работает только там, где установлен Excel. Проверьте четыре вещи до написания кода:

  • 🖥️ Где выполняется код. На клиенте — нужен Excel на компьютере пользователя. На сервере 1С — на сервере, а это отдельная головная боль (см. раздел про ошибки).
  • 🧩 Разрядность. Если платформа 64-разрядная, Excel тоже должен быть 64-разрядным, и наоборот. Несовпадение даёт ошибку создания COM-объекта.
  • 🔑 Права. Пользователь, под которым запущена 1С, должен иметь право запускать Excel.
  • 📄 Что вам на самом деле нужно. Если достаточно просто выгрузить таблицу в файл, обойдитесь без COM: табличный документ 1С умеет сохраняться в формат xlsx методом Записать(). Excel при этом не нужен, и работает это на сервере. Но с готовым файлом такой способ не дружит: добавить лист в существующую книгу им не получится.

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

Подключение: запуск Excel и создание книги

Минимальный каркас, от которого отталкиваются все остальные примеры:

Попытка
    Excel = Новый COMОбъект("Excel.Application");
Исключение
    ВызватьИсключение "Не удалось запустить Excel: " + ОписаниеОшибки();
КонецПопытки;

Excel.Visible = Ложь;          // не показывать окно
Excel.DisplayAlerts = Ложь;    // не задавать вопросов пользователю

Книга = Excel.Workbooks.Add();  // новая пустая книга
// или открыть существующую:
// Книга = Excel.Workbooks.Open("C:\Отчёты\Реестр.xlsx");

Что здесь важно:

  1. Visible = Ложь прячет окно. Пока вы отлаживаете, поставьте Истина — так видно, что происходит.
  2. DisplayAlerts = Ложь отключает диалоги («Файл уже существует, заменить?»). Без этого код может повиснуть на скрытом окне вопроса, и вы этого не увидите.
  3. Новая книга создаётся с несколькими пустыми листами. Сколько их — зависит от настройки самого Excel (параметр «число листов в новой книге»), в коде на это рассчитывать нельзя. Если нужны только свои листы, лишние удалите, а не гадайте, сколько их.

Как добавить новый лист: метод Add

Коллекция листов — Книга.Worksheets. Метод Add принимает четыре необязательных параметра: Before (перед каким листом), After (после какого), Count (сколько листов) и Type (тип листа). Позицию задают одним из первых двух параметров, не обоими сразу.

Добавление в начало

Листы = Книга.Worksheets;
НовыйЛист = Листы.Add(Листы.Item(1));  // перед первым листом
НовыйЛист.Name = "Сводка";

Добавление в конец

В 1С пропущенный параметр COM-метода оставляют пустым — просто запятая:

Листы = Книга.Worksheets;
НовыйЛист = Листы.Add(, Листы.Item(Листы.Count));  // после последнего
НовыйЛист.Name = "Продажи";

Без параметров Листы.Add() вставит лист перед активным, а не в конец. Это самая частая причина «листы идут в обратном порядке»: активным остаётся тот, что добавили последним, и каждый следующий встаёт перед ним.

Таблица: какой вызов за что отвечает

Что нужноВызовРезультат
Лист в началоAdd(Листы.Item(1))Новый лист первый
Лист в конецAdd(, Листы.Item(Листы.Count))Новый лист последний
Лист после нужногоAdd(, Листы.Item("Итоги"))Сразу за листом «Итоги»
Лист без указанийAdd()Перед активным листом

Обращаться к листу можно по номеру (Item(1)) или по имени (Item("Итоги")). Номер отсчитывается с единицы, а не с нуля, как привыкли в массивах 1С. Если листа с таким именем нет, получите ошибку COM.

Имя, позиция и порядок листов

Сразу после добавления лист называется Лист2, Лист3 и так далее. Дайте ему осмысленное имя:

НовыйЛист.Name = "Март";

У имени листа в Excel есть жёсткие ограничения:

  • 📏 не длиннее 31 символа;
  • 🚫 нельзя символы \ / ? * [ ] :;
  • 🔁 имя уникально в пределах книги, регистр не различается («Март» и «МАРТ» — одно и то же);
  • ⛔ имя не может быть пустым и не может начинаться или заканчиваться апострофом.

Поэтому имя, собранное из данных 1С (наименование организации, номер документа), нужно подчистить:

Функция ИмяЛиста(Знач Имя)
    Для Каждого Символ Из СтрРазделить("\ / ? * [ ] :", " ") Цикл
        Имя = СтрЗаменить(Имя, Символ, "_");
    КонецЦикла;
    Возврат Лев(СокрЛП(Имя), 31);
КонецФункции

Если листов с одинаковыми именами может быть несколько (например, по одному на контрагента), перед присвоением проверьте, свободно ли имя:

Функция ЛистСуществует(Книга, Имя)
    Для Н = 1 По Книга.Worksheets.Count Цикл
        Если ВРег(Книга.Worksheets.Item(Н).Name) = ВРег(Имя) Тогда
            Возврат Истина;
        КонецЕсли;
    КонецЦикла;
    Возврат Ложь;
КонецФункции

Если такой лист есть — добавьте к имени суффикс (Март (2)) в цикле, пока не найдёте свободное.

Переставить лист: метод Move

Уже созданный лист переносят методом Move, у которого тоже есть Before и After:

// В самое начало
НовыйЛист.Move(Книга.Worksheets.Item(1));
// В конец
НовыйЛист.Move(, Книга.Worksheets.Item(Книга.Worksheets.Count));

Если вызвать Move без параметров, Excel создаст новую книгу и перенесёт лист туда. Если вы этого не хотели — вы потеряете лист из исходной книги. Всегда передавайте хотя бы один параметр.

Удалить лишние листы

После Workbooks.Add() в книге остаются заготовки. Удалите ненужные, предварительно выключив вопрос о подтверждении:

Excel.DisplayAlerts = Ложь;
Книга.Worksheets.Item("Лист1").Delete();

Удалять можно только пока в книге остаётся хотя бы один видимый лист. Поэтому сначала добавляйте свои листы, потом удаляйте лишние.

Копирование листа-шаблона вместо создания с нуля

Если у вас есть оформленная форма — шапка, ширина колонок, формулы, печатные настройки, — проще один раз подготовить шаблон и копировать его, чем перерисовывать в коде. Метод Copy ведёт себя как Move: тоже принимает Before или After.

Шаблон = Книга.Worksheets.Item("Шаблон");

Шаблон.Copy(, Книга.Worksheets.Item(Книга.Worksheets.Count));
НовыйЛист = Книга.Worksheets.Item(Книга.Worksheets.Count);
НовыйЛист.Name = "Контрагент 1";

Что нужно знать:

  1. После Copy копия становится активным листом, но возвращаемого значения у метода нет. Поэтому либо берёте последний лист по номеру (как выше, если копировали в конец), либо обращаетесь к Excel.ActiveSheet.
  2. Копия получает автоматическое имя вида Шаблон (2). Переименуйте её сразу.
  3. Копируются формулы, форматы, объединённые ячейки, диаграммы и именованные диапазоны, привязанные к листу. Именно ради этого шаблоны и делают.
  4. Шаблон может лежать в той же книге или в отдельном файле. Во втором случае откройте файл шаблона как книгу и копируйте лист с параметром After из другой книги — это работает так же.

Хранить шаблон удобно в макете 1С (двоичные данные), а перед использованием записывать во временный файл. Тогда шаблон всегда версионируется вместе с конфигурацией, и пользователи не ломают его вручную.

Заполнение листа: быстро и без зависаний

Основная потеря времени — не создание листов, а запись данных. Обращение к ячейке через COM занимает заметное время, а на тысячах строк эти миллисекунды складываются в минуты.

Медленно:

Для Н = 1 По Таблица.Количество() Цикл
    НовыйЛист.Cells(Н, 1).Value = Таблица[Н - 1].Наименование;
    НовыйЛист.Cells(Н, 2).Value = Таблица[Н - 1].Сумма;
КонецЦикла;

Быстро — собрать данные в массив и записать одним обращением:

Строк = Таблица.Количество();
Колонок = 2;
Массив = Новый COMSafeArray("VT_VARIANT", Колонок, Строк);

Для Н = 0 По Строк - 1 Цикл
    Массив.SetValue(0, Н, Таблица[Н].Наименование);
    Массив.SetValue(1, Н, Таблица[Н].Сумма);
КонецЦикла;

Диапазон = НовыйЛист.Range(НовыйЛист.Cells(1, 1), НовыйЛист.Cells(Строк, Колонок));
Диапазон.Value = Массив;

Размерности COMSafeArray задаются как «колонки, строки», а индексы в SetValue идут с нуля. Это легко перепутать, поэтому сначала проверьте на пяти строках.

Дополнительно ускоряют работу три настройки на время выгрузки:

НастройкаЗначение на время записиЗачем
Excel.ScreenUpdatingЛожьExcel не перерисовывает экран
Excel.Calculation-4135 (ручной пересчёт)Формулы не пересчитываются после каждой ячейки
Excel.EnableEventsЛожьНе срабатывают макросы книги

После записи верните прежние значения: ScreenUpdating = Истина, Calculation = -4105 (автоматический), EnableEvents = Истина. Если не вернуть, пользователь потом получит Excel, который не пересчитывает формулы.

Не оставляйте Excel в изменённом состоянии при ошибке. Возврат настроек положите в блок Исключение, иначе после сбоя они останутся выключенными до перезапуска.

Сохранение, закрытие и освобождение ресурсов

Самая неприятная проблема COM-выгрузок: код отработал, а в диспетчере задач остался невидимый EXCEL.EXE. Через сутки их накапливается десяток, и потом файлы «заняты неизвестным процессом».

Правильное завершение:

Попытка
    Книга.SaveAs("C:\Отчёты\Реестр.xlsx");
    Книга.Close(Ложь);   // не спрашивать о сохранении
    Excel.Quit();
Исключение
    // сообщить об ошибке
КонецПопытки;

Книга = Неопределено;
Excel = Неопределено;

Порядок: сохранить, закрыть книгу, выйти из Excel, обнулить переменные. Если пропустить Quit() или не обнулить ссылки, процесс останется жить.

Чтобы он не оставался и при сбое в середине, оберните основной код в Попытка … Исключение, а закрытие вынесите так, чтобы оно выполнялось в обоих случаях. Если в базе есть пользователи, которые могут прервать выгрузку, предупредите их: принудительное завершение сеанса 1С оставит Excel висеть.

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

Типичные ошибки при работе с листами

Ошибки здесь однотипны. Вот те, что встречаются чаще всего:

  • ❌ «Класс не зарегистрирован» или не создаётся COM-объект. Excel не установлен на машине, где выполняется код, либо разрядность не совпадает. Если код идёт на сервере, а Excel стоит только у пользователя, перенесите выгрузку на клиента.
  • ❌ Лист не добавляется, возникает исключение. Чаще всего книга защищена (структура заблокирована) или открыта только для чтения. Проверьте свойство защиты структуры книги и режим открытия.
  • ❌ Ошибка при присвоении имени. Длина больше 31 символа, запрещённый символ или дубль имени. Применяйте функции очистки из раздела выше.
  • ❌ Данные попали не в тот лист. Вы сохранили ссылку на лист до вызова Copy или Move, а работаете с ActiveSheet. Всегда держите ссылку на конкретный лист в переменной и пишите через неё.
  • ❌ Появляется диалог, и код встаёт. Не выключен DisplayAlerts, либо файл с таким именем уже открыт в другом сеансе Excel.
  • ❌ Русские буквы в формулах не работают. В Formula формулы пишутся на английском (SUM), в FormulaLocal — на языке интерфейса Excel (СУММ). Для переносимости используйте Formula.

Для диагностики на сервере полезно писать в журнал регистрации ОписаниеОшибки() целиком: в тексте обычно видно, на каком именно обращении упал COM.

Частые вопросы

Можно ли добавить лист в Excel из 1С без установленного Excel?
Через COM — нет. Если Excel не нужен как программа, можно сформировать файл xlsx силами 1С (табличный документ, Записать()), но добавить лист в готовую книгу так не получится.
Как добавить сразу несколько листов?
Вторым-третьим параметром Add количество не передаётся надёжно во всех версиях, поэтому добавляйте в цикле: один вызов Add на каждый лист, с именем из списка.
Как узнать, сколько листов в книге?
Свойство Книга.Worksheets.Count. Скрытые листы тоже входят в это число.
Как скрыть лист?
Установите свойству Visible значение 0 (скрыт) или -1 (виден). Значение 2 делает лист «очень скрытым», он не виден даже в окне показа скрытых листов.
Работает ли это на сервере 1С?
Технически можно, но Microsoft не рекомендует запускать Excel как серверный компонент: возможны зависания диалогов и утечки процессов. Для серверных задач надёжнее формировать xlsx средствами платформы.
Как защитить лист паролем?
Вызовом Лист.Protect("пароль"). Снимается методом Unprotect с тем же паролем.