comsafearray 1с
Понадобилось однажды мне в загрузку пользователей из AD подгрузить информацию о группе, в котором находится пользователь. Узнав, через редактор атрибутов ‘Active Directory‘, что за данную информацию отвечает атрибут ‘canonicalName‘, был быстро дописан механизм загрузки:
1 2 3 4 5 6 7 8 9 10 |
//... ТекстЗапроса = "select name, samaccountname,mail,userAccountControl,canonicalName from 'LDAP://"+ДоменDNS+"' WHERE objectCategory = 'Person' AND objectClass= 'user' ORDER BY name"; //.. Пока НЕ ВыборкаАДО.EOF Цикл Каталог = ВыборкаАДО.Fields("canonicalName").Value; КонецЦикла; |
Но как оказалось, получаемый тип не был строкой. Это оказался тип данных COMSafeArray:

Итак, COMSafeArray – это тип данных, который позволяет работать с многомерными массивами из COM:
Полистав немного справку и помучив отладчик, я понял что в моем случае группу пользователя из ‘AD‘, которая хранится в значение типа ‘COMSafeArray‘, можно выгрузить в обычный массив и дальше уже работать по старинке:
1 2 3 4 5 6 7 8 9 10 |
//... ТекстЗапроса = "select name, samaccountname,mail,userAccountControl,canonicalName from 'LDAP://"+ДоменDNS+"' WHERE objectCategory = 'Person' AND objectClass= 'user' ORDER BY name"; //.. Пока НЕ ВыборкаАДО.EOF Цикл МассивГрупп = ВыборкаАДО.Fields("canonicalName").Value.Выгрузить(); КонецЦикла; |
Так же можно было просто читать значения, получая их по индексу, используя методы ‘GetLength’ и ‘GetValue’:
1 2 3 4 5 6 7 |
Количество = ВыборкаАДО.Fields("canonicalName").Value.GetLength(0); Для Счетчик = 0 По Количество - 1 Цикл Группа = ВыборкаАДО.Fields("canonicalName").Value.GetValue(Счетчик); КонецЦикла; |
Важно понимать какой перед нами массив: одномерный или многомерный. От этого будет зависеть алгоритм работы с ним.
Вот таким нехитрым способом удалось загрузить информацию о группах пользователя из AD.
В типовых конфигурациях данных тип ‘COMSafeArray‘ используют в различных решениях при работе с файлами через com-соединения:
- Процедура СравнитьФайлы(ПутьКФайлу1, ПутьКФайлу2, СпособСравненияВерсийФайлов) Экспорт
- Функция ИнициализироватьПечатнуюФормуOOWriter(Знач Шаблон = Неопределено) Экспорт
- Функция ПолучитьТаблицуДанных(ExcelЛист, Данные, НомерНачальнойКолонки, НомерНачальнойСтроки, МассивНеРазрешимыхСимволов, БыстроеСчитываниеДанных, НомерКонечнойСтроки) Экспорт
- и т.д.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 |
Процедура СравнитьФайлы(ПутьКФайлу1, ПутьКФайлу2, СпособСравненияВерсийФайлов) Экспорт Попытка Если СпособСравненияВерсийФайлов = "MicrosoftOfficeWord" Тогда ОбъектWord = Новый COMОбъект("Word.Application"); ОбъектWord.Visible = 0; Документ = ОбъектWord.Documents.Open(ПутьКФайлу1); Документ.Merge(ПутьКФайлу2, 0, 0, 0); // MergeTarget:=wdMergeTargetSelected, DetectFormatChanges:=False, UseFormattingFrom:=wdFormattingFromCurrent ОбъектWord.Visible = 1; ОбъектWord.Activate(); Документ.Close(); ИначеЕсли СпособСравненияВерсийФайлов = "OpenOfficeOrgWriter" Тогда // Снимем readonly - иначе не сработает. Файл1 = Новый Файл(ПутьКФайлу1); Файл1.УстановитьТолькоЧтение(Ложь); Файл2 = Новый Файл(ПутьКФайлу2); Файл2.УстановитьТолькоЧтение(Ложь); // Открыть OpenOffice ServiceManager = Новый COMОбъект("com.sun.star.ServiceManager"); Reflection = ServiceManager.createInstance("com.sun.star.reflection.CoreReflection"); Desktop = ServiceManager.createInstance("com.sun.star.frame.Desktop"); Dispatcher = ServiceManager.createInstance("com.sun.star.frame.DispatchHelper"); // Открыть документ OpenOffice. Args = Новый COMSafeArray("VT_DISPATCH", 1); OOДокумент = Desktop.loadComponentFromURL(ПреобразоватьВURL(ПутьКФайлу2), "_blank", 0, Args); frame = Desktop.getCurrentFrame(); // установить показ изменений ПараметрыСравнения = Новый COMSafeArray("VT_VARIANT", 1); ПараметрыСравнения.SetValue(0, ПрисвоитьЗначениеСвойству(ServiceManager, "ShowTrackedChanges", Истина)); dispatcher.executeDispatch(frame, ".uno:ShowTrackedChanges", "", 0, ПараметрыСравнения); // сравнить с документом ПараметрыВызова = Новый COMSafeArray("VT_VARIANT", 1); ПараметрыВызова.SetValue(0, ПрисвоитьЗначениеСвойству(ServiceManager, "URL", ПреобразоватьВURL(ПутьКФайлу1))); dispatcher.executeDispatch(frame, ".uno:CompareDocuments", "", 0, ПараметрыВызова); OOДокумент = Неопределено; КонецЕсли; Исключение ОбщегоНазначенияКлиентСервер.СообщитьПользователю( КраткоеПредставлениеОшибки(ИнформацияОбОшибке() )); КонецПопытки; КонецПроцедуры |
С типами данных, в том числе и ‘COMSafeArray‘ , которые можно передавать через COM, можно ознакомиться в соответствующей статье на сайте 1с.
Так же данный тип можно использовать в скоростной загрузки данных в Ексель. Для этого всего лишь достаточно данные поместить в ‘COMSafeArray‘ и затем поместить их в диапазон ячеек:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 |
&НаСервере Процедура ТестЕксельСервер() Ексель=Новый COMОбъект("Excel.Application"); Книга = Ексель.Workbooks.Add(); Лист = Книга.Worksheets(1); Запрос = Новый Запрос("ВЫБРАТЬ | Контрагенты.Код, | Контрагенты.Наименование |ИЗ | Справочник.Контрагенты КАК Контрагенты"); ТаблицаРезультата = Запрос.Выполнить().Выгрузить(); ВсегоСтрок = ТаблицаРезультата.Количество(); ВсегоКолонок = ТаблицаРезультата.Колонки.Количество(); МассивКОМ = Новый COMSafeArray("VT_VARIANT", ВсегоКолонок, ВсегоСтрок); Для индСтрок = 0 По ТаблицаРезультата.Количество() - 1 Цикл СтрокаТаблицы = ТаблицаРезультата[индСтрок]; Для индКолонок = 0 По ВсегоКолонок - 1 Цикл МассивКОМ.SetValue(индКолонок, индСтрок, СтрокаТаблицы[индКолонок]); КонецЦикла; КонецЦикла; Лист.Range(Лист.Cells(1,1), Лист.Cells(ВсегоСтрок,ВсегоКолонок)).Value = МассивКОМ; Книга.SaveCopyAs("D:\Test.xls"); Ексель.DisplayAlerts = 0; Ексель.Quit(); Ексель.DisplayAlerts = 1; Ексель=Null; КонецПроцедуры |
Здесь стоит обратить внимание на то, что вместо стандартного сохранения ‘SaveAs’ используется ‘SaveCopyAs’. Так как в противном случае мы получим ошибку:

Каждый день узнаю что-то новое и каждый день не перестаю этому удивляться. Всем удачи!