Чтение свойств и данных COM-объектов во встроенном языке 1С

· 9 мин

Содержание

Значение COM-объекта в 1С читают так же, как реквизит обычного объекта: через точку. Значение = Excel.ActiveSheet.Cells(1, 1).Value. Сам объект создают конструктором Новый COMОбъект("ProgID") или подключаются к уже существующему через ПолучитьCOMОбъект().

Сложности начинаются дальше: отладчик не показывает свойства, параметрическое свойство нужно вызывать как метод, а диапазон Excel возвращает не массив, а COMSafeArray. Ниже по порядку: как создать объект, как достать из него значение, как не упасть на ошибке и как читать тысячи строк за секунды, а не за минуты.

Что такое COM-объект и где он доступен в 1С

COM — механизм Windows, через который одна программа управляет другой. Excel, Word, драйверы ADO, служба WMI, сама 1С (V83.COMConnector, V83.Application) публикуют свои объекты под строковым именем — ProgID.

Для 1С внешний COM-объект выглядит как значение типа COMОбъект. У него есть свойства и методы, но их описание хранится не в 1С, а в библиотеке типов самого приложения.

Где это работает:

  • 🖥️ Только Windows. На Linux-сервере и в macOS-клиенте конструктора COMОбъект нет.
  • 🌐 Не в веб-клиенте. Код с COM оборачивают в #Если Не ВебКлиент Тогда … #КонецЕсли или переносят на сервер.
  • ⚙️ На клиенте или на сервере. В клиент-серверном варианте объект, созданный в процедуре &НаСервере, живёт на компьютере рабочего процесса. Там же должна быть установлена и зарегистрирована программа.

Разрядность важна. 64-битный сервер 1С не загрузит 32-битную COM-библиотеку, которая работает внутри процесса (in-process DLL). Классический пример — 32-битный драйвер или компонента, установленная только для 32-битного клиента. Проверьте, что разрядность программы совпадает с разрядностью процесса 1С.

Как создать объект перед получением значений

Есть два способа. Выбор зависит от того, нужен ли новый экземпляр приложения или уже открытый.

СпособКогда применятьПример
Новый COMОбъект("ProgID")Нужен новый экземпляр: открыть файл, выполнить запросНовый COMОбъект("Excel.Application")
ПолучитьCOMОбъект("", "ProgID")Подключиться к уже запущенному приложениюПолучитьCOMОбъект("", "Excel.Application")
ПолучитьCOMОбъект("Путь")Открыть файл тем приложением, которое за ним закрепленоПолучитьCOMОбъект("C:\Data\Price.xlsx")
ПолучитьCOMОбъект("winmgmts:…")Моникер-строка, например WMIПолучитьCOMОбъект("winmgmts:\\.\root\cimv2")

Минимальный шаблон для Excel:

&НаСервере
Функция ПрочитатьЯчейку(ПутьКФайлу)

    Excel = Новый COMОбъект("Excel.Application");
    Excel.DisplayAlerts = Ложь;
    Книга = Excel.Workbooks.Open(ПутьКФайлу);
    Лист = Книга.Worksheets(1);

    Значение = Лист.Cells(1, 1).Value;

    Книга.Close(Ложь);
    Excel.Quit();
    Excel = Неопределено;

    Возврат Значение;

КонецФункции

💡 Путь к файлу в серверной процедуре — это путь на сервере. Файл с компьютера пользователя сначала передают через временное хранилище и сохраняют во временный каталог сервера.

Базовые способы получить значение из COM-объекта

Простое свойство

Свойство без параметров читают через точку: Excel.Version, Книга.Name, Лист.UsedRange.Rows.Count. Цепочку можно строить любой длины, каждая точка возвращает новый COM-объект или примитивное значение.

Свойство с параметрами

В VBA пишут Cells(1, 1) или Worksheets("Лист1"), и это свойство с аргументами. В 1С такое свойство вызывают как метод — со скобками:

Ячейка = Лист.Cells(5, 2);           // строка 5, колонка 2
Текст = Лист.Range("B5").Text;      // то, что видно на экране
Сумма = Лист.Range("B5").Value;     // само значение

Свойство по умолчанию

В VBA можно написать Fields("Сумма") и опустить .Value. В 1С на это не рассчитывайте: называйте свойство явно. RS.Fields.Item("Сумма").Value работает всегда, сокращённая запись — не всегда.

Как приходят типы

1С сама переводит простые типы COM в свои:

В COMВ 1С
строка (BSTR)Строка
целое, дробное, денежноеЧисло
дата (DATE)Дата
логическоеБулево
пустое значение (VT_EMPTY)Неопределено
массив (SAFEARRAY)COMSafeArray
объект (IDispatch)COMОбъект

Проверяйте тип перед использованием: пустая ячейка Excel даёт Неопределено, а не пустую строку.

Значение = Лист.Cells(Стр, 3).Value;
Если ТипЗнч(Значение) = Тип("Число") Тогда
    Сумма = Значение;
ИначеЕсли ТипЗнч(Значение) = Тип("Строка") Тогда
    Сумма = Число(СтрЗаменить(Значение, " ", ""));
Иначе
    Сумма = 0;
КонецЕсли;

Как найти «невидимые» свойства COM-объекта

В отладчике 1С переменная типа COMОбъект не раскрывается в список свойств. Отсюда частый вопрос: как узнать, что у объекта вообще есть?

Рабочие приёмы:

  1. 📘 Браузер объектов VBA. В Excel или Word откройте редактор Alt+F11, затем F2. Там видны все классы, свойства, методы и их параметры. Это первый и самый быстрый источник.
  2. 🧾 Документация разработчика. Для Office, ADO и WMI она есть у производителя. Имена свойств в 1С пишут ровно так, как в документации, латиницей.
  3. 🔍 PowerShell. Команда New-Object -ComObject Excel.Application | Get-Member выведет список членов объекта. Удобно для незнакомых ProgID.
  4. 🧪 Пробное чтение в отладке. В табло отладчика можно вычислить выражение Лист.Name или Лист.UsedRange.Address — значение примитивного типа отладчик покажет.

💡 Для коллекций (Workbooks, Worksheets, Fields) почти всегда есть Count и Item(Индекс). Если не знаете, как перебрать коллекцию, начните с них или с цикла Для Каждого, который 1С поддерживает для перечисляемых COM-коллекций.

Для Каждого Лист Из Книга.Worksheets Цикл
    Сообщить(Лист.Name);
КонецЦикла;

Обработка ошибок при получении значений через COM

COM-вызов может упасть в любой точке: программа не установлена, файл занят, имя свойства с опечаткой. Без обработки пользователь увидит исключение, а процесс Excel останется висеть в памяти.

Оборачивайте работу в Попытка … Исключение и закрывайте приложение в обеих ветках:

Excel = Неопределено;
Попытка
    Excel = Новый COMОбъект("Excel.Application");
    Книга = Excel.Workbooks.Open(ПутьКФайлу);
    Значение = Книга.Worksheets(1).Range("A1").Value;
    Книга.Close(Ложь);
Исключение
    Инфо = ИнформацияОбОшибке();
    ЗаписьЖурналаРегистрации("Загрузка из Excel", УровеньЖурналаРегистрации.Ошибка, , ,
        ПодробноеПредставлениеОшибки(Инфо));
    Значение = Неопределено;
КонецПопытки;

Если Excel <> Неопределено Тогда
    Попытка
        Excel.Quit();
    Исключение
        // приложение уже закрыто
    КонецПопытки;
    Excel = Неопределено;
КонецЕсли;

Что означают типичные сообщения:

Диагностика: выберите симптом

Офисные приложения не рассчитаны на запуск от имени службы без интерактивного пользователя. На сервере 1С Excel может зависать на диалогах. Для серверной загрузки надёжнее сохранить файл и прочитать его средствами платформы: ТабличныйДокумент.Прочитать() умеет открывать xlsx без установленного Office.

Как быстро читать большие объёмы данных

Каждое обращение через точку — это отдельный межпроцессный вызов. Чтение 10 000 строк по 5 колонок поячейке — это 50 000 вызовов и минуты ожидания.

Читайте диапазон целиком

Range.Value у диапазона из нескольких ячеек возвращает COMSafeArray. Его метод Выгрузить() превращает данные в обычный массив 1С за один вызов:

Используемый = Лист.UsedRange;
ЧислоСтрок = Используемый.Rows.Count;
ЧислоКолонок = Используемый.Columns.Count;

Данные = Используемый.Value.Выгрузить();

Для НомерСтроки = 1 По ЧислоСтрок Цикл
    Код = Данные[0][НомерСтроки - 1];
    Наименование = Данные[1][НомерСтроки - 1];
    // обработка строки
КонецЦикла;

У выгруженного массива первый индекс — колонка, второй — строка, нумерация с нуля. Перепутанный порядок даёт сдвинутые данные без всякой ошибки. Проверьте на маленьком диапазоне 2×3, прежде чем запускать на всём файле.

💡 Если диапазон состоит из одной ячейки, Value вернёт само значение, а не массив. Проверяйте тип: ТипЗнч(Значение) = Тип("COMSafeArray").

Другие приёмы ускорения

  • ⚡ Сохраняйте промежуточный объект в переменную: Лист = Книга.Worksheets(1) вместо повторения цепочки в каждой строке цикла.
  • 🖼️ Отключайте перерисовку при записи: Excel.ScreenUpdating = Ложь.
  • 🔒 Excel.DisplayAlerts = Ложь — чтобы диалог «Сохранить изменения?» не остановил процесс.
  • 🧹 Закрывайте книгу и вызывайте Quit() сразу после чтения, а не в конце длинной обработки.

Практические примеры получения значений

ADO: значение поля из внешней базы

Соединение = Новый COMОбъект("ADODB.Connection");
Соединение.Open(СтрокаПодключения);
Выборка = Соединение.Execute("SELECT Code, Name FROM Goods");

Пока Не Выборка.EOF Цикл
    Код = Выборка.Fields.Item("Code").Value;
    Имя = Выборка.Fields.Item("Name").Value;
    // обработка
    Выборка.MoveNext();
КонецЦикла;

Выборка.Close();
Соединение.Close();

Поле со значением NULL в базе вернётся как Null. Перед записью в реквизит 1С проверьте: Если Имя = Null Тогда Имя = ""; КонецЕсли;.

WMI: свойства компьютера

WMI = ПолучитьCOMОбъект("winmgmts:\\.\root\cimv2");
Диски = WMI.ExecQuery("SELECT DeviceID, FreeSpace FROM Win32_LogicalDisk WHERE DriveType = 3");

Для Каждого Диск Из Диски Цикл
    Свободно = Число(Диск.FreeSpace) / 1024 / 1024 / 1024;
    Сообщить(Диск.DeviceID + " свободно " + Формат(Свободно, "ЧДЦ=1") + " ГБ");
КонецЦикла;

FreeSpace в WMI приходит строкой, поэтому оборачиваем в Число(). Для диска со 107 374 182 400 байтами получится ровно 100 ГБ.

Word: текст документа и таблицы

Word = Новый COMОбъект("Word.Application");
Документ = Word.Documents.Open(ПутьКФайлу);

ВесьТекст = Документ.Content.Text;

Если Документ.Tables.Count > 0 Тогда
    Таблица = Документ.Tables.Item(1);
    ТекстЯчейки = Таблица.Cell(2, 3).Range.Text;
    // в конце текста ячейки Word хранит служебные символы
    ТекстЯчейки = Лев(ТекстЯчейки, СтрДлина(ТекстЯчейки) - 2);
КонецЕсли;

Документ.Close(Ложь);
Word.Quit();
Word = Неопределено;

Текст ячейки таблицы Word заканчивается двумя служебными символами: концом абзаца и маркером ячейки. Без обрезки они попадут в реквизит и сломают сравнение строк. Номер ячейки задаётся парой «строка, колонка», нумерация с единицы.

Другая база 1С через COM-соединение

Коннектор = Новый COMОбъект("V83.COMConnector");
База = Коннектор.Connect("Srvr=""srv1c"";Ref=""buh"";Usr=""Обмен"";Pwd=""***"";");

Товар = База.Справочники.Номенклатура.НайтиПоКоду("00-00000015");
Наименование = Товар.Наименование;           // строка — придёт как есть
ЕдиницаТекстом = База.String(Товар.ЕдиницаИзмерения);  // ссылку переводим в строку

Реквизиты примитивных типов приходят готовыми значениями. Ссылки остаются COM-объектами чужой базы: их нельзя записать в реквизит своей базы напрямую. Переводите их в строку через String() или сопоставляйте по коду и уникальному идентификатору.

Версия V83.COMConnector должна совпадать с версией платформы сервера, к которому подключаетесь. После обновления платформы компоненту comcntr.dll перерегистрируют от имени администратора: regsvr32 "C:\Program Files\1cv8\<версия>\bin\comcntr.dll".

Ошибки, на которых чаще всего спотыкаются

  • 👻 Висящие процессы EXCEL.EXE. Причина — не вызван Quit() из-за исключения или ссылки на книгу и лист остались в переменных. Решение — закрытие в отдельном блоке после КонецПопытки, как в примере выше.
  • 🔢 Числа из Excel как строки. Если в файле число записано текстом, .Value вернёт строку с пробелами-разделителями. Чистите её перед Число(), иначе получите исключение преобразования.
  • 📅 Пустая дата. Пустая ячейка с форматом даты вернёт Неопределено, а не '00010101'. Сравнивайте тип, а не значение.
  • 🗂️ Порядок индексов в массиве Выгрузить(). Колонка, потом строка. Ошибку не видно до сверки данных.
  • 🖥️ Код работает у разработчика, но не на сервере. На рабочем месте стоит Office, на сервере — нет, или разной разрядности. Запускайте COM там, где программа реально установлена, или читайте файл средствами платформы.
Можно ли использовать COM-объекты в 1С на Linux?
Нет. COM — технология Windows. На Linux для Excel используют ТабличныйДокумент.Прочитать(), для внешних баз — внешние источники данных или HTTP-сервисы.
Чем `.Value` отличается от `.Text` у ячейки Excel?
Value возвращает значение ячейки: число, дату, строку. Text — строку в том виде, как она отображается с учётом формата, например «1 250,00 ₽». Для расчётов берите Value.
Как получить номер последней заполненной строки листа?
Через Лист.UsedRange.Rows.Count с поправкой на первую строку диапазона: Лист.UsedRange.Row + Лист.UsedRange.Rows.Count - 1. Пустые строки с форматированием тоже попадают в диапазон.
Почему COM-объект нельзя передать с сервера на клиент?
COMОбъект не сериализуется. Передавайте между клиентом и сервером только результат: строку, число, массив или структуру с прочитанными значениями.
Нужно ли обнулять переменную COM-объекта?
Да, присвойте Неопределено после Quit(). Так 1С освобождает ссылку, и внешний процесс завершается сразу, а не при закрытии сеанса.