Чтение свойств и данных COM-объектов во встроенном языке 1С
Содержание
Значение 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Объект не раскрывается в список свойств. Отсюда частый вопрос: как узнать, что у объекта вообще есть?
Рабочие приёмы:
- 📘 Браузер объектов VBA. В Excel или Word откройте редактор
Alt+F11, затемF2. Там видны все классы, свойства, методы и их параметры. Это первый и самый быстрый источник. - 🧾 Документация разработчика. Для Office, ADO и WMI она есть у производителя. Имена свойств в 1С пишут ровно так, как в документации, латиницей.
- 🔍 PowerShell. Команда
New-Object -ComObject Excel.Application | Get-Memberвыведет список членов объекта. Удобно для незнакомых ProgID. - 🧪 Пробное чтение в отладке. В табло отладчика можно вычислить выражение
Лист.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 = Неопределено;
КонецЕсли;
Что означают типичные сообщения:
.Value и .Text одной ячейки.Офисные приложения не рассчитаны на запуск от имени службы без интерактивного пользователя. На сервере 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?
ТабличныйДокумент.Прочитать(), для внешних баз — внешние источники данных или HTTP-сервисы.Чем `.Value` отличается от `.Text` у ячейки Excel?
Value возвращает значение ячейки: число, дату, строку. Text — строку в том виде, как она отображается с учётом формата, например «1 250,00 ₽». Для расчётов берите Value.Как получить номер последней заполненной строки листа?
Лист.UsedRange.Rows.Count с поправкой на первую строку диапазона: Лист.UsedRange.Row + Лист.UsedRange.Rows.Count - 1. Пустые строки с форматированием тоже попадают в диапазон.Почему COM-объект нельзя передать с сервера на клиент?
COMОбъект не сериализуется. Передавайте между клиентом и сервером только результат: строку, число, массив или структуру с прочитанными значениями.Нужно ли обнулять переменную COM-объекта?
Неопределено после Quit(). Так 1С освобождает ссылку, и внешний процесс завершается сразу, а не при закрытии сеанса.