Почему завершается аварийно программа 1cv8c при загрузке большого файла Excel и как это исправить?

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

Разберем распространенную проблему, возникающую при обращении к крупным таблицам MS Excel (более 100–150 тысяч строк) через COM-соединение. Во время работы скрипта на вызове метода SpecialCells(11) приложение 1cv8c.exe может аварийно завершать работу. В журнале событий Windows при этом фиксируется ошибка BEX с кодом исключения c0000005 и сигнатурой модуля StackHash_0a9e.

Выясним причины возникновения ошибки

Проанализируем техническую сторону сбоя:

  1. Ограничение виртуальной памяти 32-битного клиента: 32-разрядный процесс 1cv8c.exe имеет жесткий лимит доступной оперативной памяти в 2 ГБ. При поячеечной обработке через COM-объект в памяти удерживается колоссальное количество промежуточных объектов, что приводит к переполнению буфера.
  2. Особенности работы метода SpecialCells(11): Вызов SpecialCells(11) заставляет Excel сканировать всю таблицу для поиска последней заполненной ячейки (xlCellTypeLastCell). На больших массивах данных со сложным форматированием или ранее удаленными строками этот процесс вызывает глубокие рекурсивные вызовы и переполнение стека памяти (StackHash).
  3. Срабатывание защиты DEP (Data Execution Prevention): Системная служба предотвращения выполнения данных Windows блокирует поток процесса 1cv8c.exe при попытке стороннего COM-сервера обратиться к незащищенной области памяти.

Способ 1. Прямое считывание диапазона UsedRange в массив

Рассмотрим вариант отказа от обращения к SpecialCells(11). Вместо поиска границы ячеек выгрузим данные используемого диапазона непосредственно в массив 1С. Это минимизирует количество вызовов COM-объекта и кардинально ускоряет обработку.


Лист = XLSОбъект.Sheets(1);
МассивДанных = Лист.UsedRange.Value2.Выгрузить();

Способ 2. Загрузка файла через технологию ADO (ADODB.Connection)

Посмотрим на чтение Excel-файла как реляционной базы данных через OLEDB-провайдер. Этот подход не требует запуска MS Excel как COM-сервера и исключает падение клиента 1С.

Разберем реализацию с помощью объекта ADODB.Connection:


// Создаем подключение
Connection = Новый COMОбъект("ADODB.Connection");

Если Версия = "12" ИЛИ Версия = "14" Тогда
    СтрокаПодключения = "Provider=Microsoft.ACE.OLEDB.12.0;Data Source=" + ПутьКФайлу + ";Extended Properties=""Excel 12.0 Xml;HDR=" + ?(Заголовок, "YES", "NO") + ";IMEX=1""";
Иначе
    СтрокаПодключения = "Provider=Microsoft.Jet.OLEDB.4.0;Data Source=" + ПутьКФайлу + ";Extended Properties=""Excel 8.0;HDR=" + ?(Заголовок, "YES", "NO") + ";IMEX=1""";
КонецЕсли;

Попытка
    Connection.Open(СтрокаПодключения);
Исключение
    ВызватьИсключение "Ошибка подключения к файлу через ADO: " + ОписаниеОшибки();
КонецПопытки;

Command = Новый COMОбъект("ADODB.Command");
Command.ActiveConnection = Connection;
Command.CommandText = "Select * From [" + ИмяСтраницы + "$]";

Попытка
    Записи = Command.Execute();
Исключение
    Connection.Close();
    ВызватьИсключение "Ошибка при выполнении запроса: " + ОписаниеОшибки();
КонецПопытки;

// Формируем структуры колонок в Таблице Значений
Для НомерПоля = 0 По Записи.Fields.Count - 1 Цикл
    СтрКолонка = Строка(Записи.Fields.Item(НомерПоля).Name);
    СтрКолонка = СтрЗаменить(СтрКолонка, "№", "Num");
    
    НедопустимыеСимволы = " ~`!@""#№$;%^:&?*()-+=/\|.,";
    Для i = 1 По СтрДлина(НедопустимыеСимволы) Цикл
        СтрКолонка = СтрЗаменить(СтрКолонка, Сред(НедопустимыеСимволы, i, 1), "_");
    КонецЦикла;
    
    ИмяКолонки = СокрЛП(СтрКолонка);
    ТЗ.Колонки.Добавить(ИмяКолонки, , СтрКолонка);
КонецЦикла;

// Переносим строки в Таблицу Значений
Пока НЕ Записи.EOF() Цикл
    НоваяСтрока = ТЗ.Добавить();
    Для НомерПоля = 0 По Записи.Fields.Count - 1 Цикл
        НоваяСтрока[НомерПоля] = Записи.Fields(Записи.Fields.Item(НомерПоля).Name).Value;
    КонецЦикла;
    Записи.MoveNext();
КонецЦикла;

Command = Неопределено;
Записи = Неопределено;
Connection.Close();

Способ 3. Использование метода ТабличныйДокумент.Прочитать()

Проанализируем штатные возможности платформы 1С. Объект ТабличныйДокумент умеет считывать файлы форматов .xlsx и .xls напрямую без использования сторонних приложений и драйверов. Это полностью исключает ошибки COM-маршалинга и сбои BEX.

Для загрузки достаточно прочитать файл в ТабличныйДокумент, а затем обойти полученные ячейки или выгрузить их с помощью объекта ПостроительЗапроса.

Способ 4. Потоковое чтение XLSX через ЧтениеXML и ЧтениеZipФайла

Для обработки таблиц, содержащих сотни тысяч строк, наиболее производительным методом является прямой разбор структуры файла. Так как формат .xlsx представляет собой ZIP-архив с XML-документами, вы можете распаковать файл через ЧтениеZipФайла и последовательно прочитать данные из XML-файлов с помощью объекта ЧтениеXML. Потоковое чтение потребляет минимальное количество оперативной памяти.

Дополнительные рекомендации по настройке окружения

Если переписывание алгоритма чтения требует времени, обратите внимание на следующие инфраструктурные решения:

← На главную