Выгрузка данных из 1С в файлы Microsoft Excel через COM-соединение (COMОбъект("Excel.Application")) — популярный, но часто нестабильный сценарий. Разработчики нередко сталкиваются с ситуациями, когда файл сохраняется пустым, 1С выдает ошибку при перезаписи документа или зависают фоновые процессы Excel в операционной системе.
Разберем подробно причины возникновения таких сбоев, выясним, как правильно исправить код работы с COM-объектом, и рассмотрим более надежный альтернативный способ выгрузки без использования внешних библиотек.
Проанализируем основные технические проблемы, которые приводят к ошибкам при работе с Excel через COM:
Save или SaveAs должен вызываться у конкретного объекта книги (Workbook), а не у самого корневого объекта Excel.Application.EXCEL.EXE: если при предыдущих выгрузках не был корректно вызван метод Quit() и не были очищены ссылки на COM-переменные в памяти, в диспетчере задач накапливаются невидимые процессы, которые блокируют файлы на диске.NumberFormat в COM-модели принимает англоязычные шаблоны (например, "dd.mm.yyyy"), тогда как для русскоязычных масок ("ДД.ММ.ГГГГ") необходимо использовать свойство NumberFormatLocal.Посмотрим на пример правильной структуры процедуры. Для стабильной работы выгрузки необходимо:
DisplayAlerts = False.Разберем по шагам правильный алгоритм:
Процедура ВыгрузитьВExcelЧерезCOM()
// 1. Проверяем наличие данных перед запуском Excel
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ХозрасчетныйДвиженияССубконто.Период КАК Период,
| ХозрасчетныйДвиженияССубконто.Регистратор КАК Регистратор,
| ХозрасчетныйДвиженияССубконто.СчетДт КАК СчетДт,
| ХозрасчетныйДвиженияССубконто.Сумма КАК Сумма
|ИЗ
| РегистрБухгалтерии.Хозрасчетный.ДвиженияССубконто(&НачалоПериода, &КонецПериода, , , ) КАК ХозрасчетныйДвиженияССубконто";
Запрос.УстановитьПараметр("НачалоПериода", НачалоГода(ТекущаяДата()));
Запрос.УстановитьПараметр("КонецПериода", КонецГода(ТекущаяДата()));
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
Сообщить("Запрос не вернул данных для выгрузки. Операция прервана.");
Возврат;
КонецЕсли;
// 2. Инициализируем COM-соединение
Попытка
ExcelПриложение = Новый COMОбъект("Excel.Application");
Исключение
Сообщить("Не удалось запустить Excel. Проверьте установку программы.");
Возврат;
КонецПопытки;
ПутьКФайлу = "C:\Temp\ExportData.xlsx";
Попытка
// Отключаем вывод предупреждений и обновление экрана для ускорения
ExcelПриложение.DisplayAlerts = False;
ExcelПриложение.ScreenUpdating = False;
// Открываем существующую книгу или создаем новую
Книга = ExcelПриложение.Workbooks.Open(ПутьКФайлу);
Лист = Книга.Sheets(1);
Выборка = РезультатЗапроса.Выбрать();
НомерСтроки = 1;
Пока Выборка.Следующий() Цикл
НомерСтроки = НомерСтроки + 1;
Лист.Cells(НомерСтроки, 1).NumberFormatLocal = "@";
Лист.Cells(НомерСтроки, 1).Value = Строка(Выборка.Регистратор);
Лист.Cells(НомерСтроки, 2).NumberFormatLocal = "ДД.ММ.ГГГГ";
Лист.Cells(НомерСтроки, 2).Value = Выборка.Период;
Лист.Cells(НомерСтроки, 3).NumberFormatLocal = "0,00";
Лист.Cells(НомерСтроки, 3).Value = Выборка.Сумма;
КонецЦикла;
// 3. Сохраняем именно книгу, а не приложение
Книга.Save();
Книга.Close(False);
Исключение
Сообщить("Ошибка при обработке документа: " + ОписаниеОшибки());
КонецПопытки;
// 4. Обязательно корректно закрываем процесс Excel
ExcelПриложение.ScreenUpdating = True;
ExcelПриложение.DisplayAlerts = True;
ExcelПриложение.Quit();
// Очищаем переменные COM
Лист = Неопределено;
Книга = Неопределено;
ExcelПриложение = Неопределено;
КонецПроцедуры
Работа через COM-соединение имеет существенные ограничения: она работает только под Windows, требует установленного MS Office одинаковой разрядности с платформой 1С и нестабильна при выполнении в фоновых заданиях на сервере.
Рассмотрим гораздо более надежный и быстрый способ: сформировать ТабличныйДокумент средствами платформы 1С и сохранить его методом Записать() в формате XLSX. Для этого способа не требуется установленный Microsoft Excel.
Процедура СохранитьВXLSXНативно()
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ХозрасчетныйДвиженияССубконто.Период КАК Период,
| ХозрасчетныйДвиженияССубконто.Регистратор КАК Регистратор,
| ХозрасчетныйДвиженияССубконто.СчетДт.Код КАК СчетДтКод,
| ХозрасчетныйДвиженияССубконто.Сумма КАК Сумма
|ИЗ
| РегистрБухгалтерии.Хозрасчетный.ДвиженияССубконто(&НачалоПериода, &КонецПериода, , , ) КАК ХозрасчетныйДвиженияССубконто";
Запрос.УстановитьПараметр("НачалоПериода", НачалоГода(ТекущаяДата()));
Запрос.УстановитьПараметр("КонецПериода", КонецГода(ТекущаяДата()));
РезультатЗапроса = Запрос.Выполнить();
Если РезультатЗапроса.Пустой() Тогда
Сообщить("Нет данных для выгрузки.");
Возврат;
КонецЕсли;
ТабДок = Новый ТабличныйДокумент;
// Формируем шапку программно
ТабДок.Область("R1C1").Текст = "Регистратор";
ТабДок.Область("R1C2").Текст = "Период";
ТабДок.Область("R1C3").Текст = "Счет Дт";
ТабДок.Область("R1C4").Текст = "Сумма";
Выборка = РезультатЗапроса.Выбрать();
НомерСтроки = 1;
Пока Выборка.Следующий() Цикл
НомерСтроки = НомерСтроки + 1;
ТабДок.Область("R" + НомерСтроки + "C1").Текст = Строка(Выборка.Регистратор);
ТабДок.Область("R" + НомерСтроки + "C2").Текст = Формат(Выборка.Период, "ДФ=dd.MM.yyyy");
ТабДок.Область("R" + НомерСтроки + "C3").Текст = Выборка.СчетДтКод;
ОбластьСуммы = ТабДок.Область("R" + НомерСтроки + "C4");
ОбластьСуммы.СодержитЗначение = Истина;
ОбластьСуммы.ТипЗначения = Новый ОписаниеТипов("Число");
ОбластьСуммы.Значение = Выборка.Сумма;
КонецЦикла;
// Сохраняем табличный документ напрямую в XLSX
ПутьКФайлу = "C:\Temp\Выгрузка.xlsx";
ТабДок.Записать(ПутьКФайлу, ТипФайлаТабличногоДокумента.XLSX);
Сообщить("Файл успешно сохранен: " + ПутьКФайлу);
КонецПроцедуры
Если задача заключается в передаче больших объемов данных для последующего анализа в Excel, оптимально использовать встроенный объект ЗаписьТекста. Этот способ работает быстрее всех остальных и расходует минимум оперативной памяти.
Процедура СохранитьВCSV()
ПутьКФайлу = "C:\Temp\ExportData.csv";
ТекстФайла = Новый ЗаписьТекста(ПутьКФайлу, КодировкаТекста.ANSI);
// Записываем строку заголовков с разделителем ";"
ТекстФайла.ЗаписатьСтроку("Регистратор;Дата;Сумма");
Запрос = Новый Запрос(
"ВЫБРАТЬ
| ХозрасчетныйДвиженияССубконто.Период КАК Период,
| ХозрасчетныйДвиженияССубконто.Регистратор КАК Регистратор,
| ХозрасчетныйДвиженияССубконто.Сумма КАК Сумма
|ИЗ
| РегистрБухгалтерии.Хозрасчетный.ДвиженияССубконто(&НачалоПериода, &КонецПериода, , , ) КАК ХозрасчетныйДвиженияССубконто"
);
Запрос.УстановитьПараметр("НачалоПериода", НачалоГода(ТекущаяДата()));
Запрос.УстановитьПараметр("КонецПериода", КонецГода(ТекущаяДата()));
Выборка = Запрос.Выполнить().Выбрать();
Пока Выборка.Следующий() Цикл
СтрокаДанных = СтрШаблон("%1;%2;%3",
Строка(Выборка.Регистратор),
Формат(Выборка.Период, "ДФ=dd.MM.yyyy"),
Формат(Выборка.Сумма, "ЧДЦ=2; ЧГ=0")
);
ТекстФайла.ЗаписатьСтроку(СтрокаДанных);
КонецЦикла;
ТекстФайла.Закрыть();
Сообщить("Выгрузка в CSV завершена.");
КонецПроцедуры
Если файл сохраняется пустым, проверьте выполнение следующих шагов:
РезультатЗапроса.Пустой() или проверьте количество строк через точку останова.EXCEL.EXE, запущенные от имени вашего пользователя.NumberFormatLocal: при указании форматов на русском языке всегда используйте NumberFormatLocal вместо NumberFormat, чтобы не вызывать скрытых ошибок COM-интерфейса.