Заказы на доработку 1С (сервис удаленной работы)

Хранилище

База знаний
Неназначенных незавершенных заказов: 2
Бесплатные отчеты, обработки, конфигурации, внешние компоненты для 1С Статьи, описание работы, методики по работе с 1С

Здравствуйте, гость ( Вход | Зарегистрироваться )



> Особенности работы с объектом, полученным через COM-соединение. Ошибка “Метод не найден”          
Vofka Подменю пользователя
сообщение 05.12.12, 17:23
Сообщение #1

У нас здесь своя атмосфера...
***********
Группа: Основатель
Сообщений: 13955
Из: Киев
Спасибо сказали: 4520 раз
Рейтинг: 3642.8

На прошлой неделе ребята из отдела попросили помочь разобраться с проблемой: при работе со справочником другой базы, к которой подключение осуществлялось через COM-соединение, при попытке установке значений одного из реквизитов этого справочника возникало исключение “Метод не найден”, в то время как установка значений остальных свойств происходила без каких-либо проблем.



Вопрос разрешился сразу, как только я увидел имя реквизита-виновника – “ПолноеНаименование”. Не все знают, что у объекта СправочникОбъект есть метод с таким именем – очень уж редко он бывает нужен. Возникает резонный вопрос, почему же при работе с реквизитом непосредственно в модуле базы, в которой расположен справочник, никакого конфликта не возникает, в то время как при работе с этим справочником “извне” (на клиенте COM-соединения) – вызывается исключение?

Чтобы разобраться, почему возникла ошибка, нужно понимать хотя бы в общих чертах, что происходит “за сценой”, когда выполняется работа с COM-объектами во встроенном языке .

Как устроено взаимодействие с COM-объектами



Объекты, полученные через COM-соединение из другой ИБ – чужеродны для той ИБ, из которой осуществляется доступ, поэтому они обернуты в специальный универсальный программный интерфейс IDispatch, именно поэтому в отладчике все объекты, полученные через COM-подключение мы видим как безликий тип COMОбъект.

Интерфейс IDispatch предоставляет универсальные методы для работы с “внешними” по отношению к клиенту COM-соединения объектами, основными методами интерфейса являются GetIDsOfNames(), который по имени метода/свойства позволяет получить его числовой идентификатор и Invoke(), который позволяет вызывать необходимый метод, указав его числовой идентификатор.

Поскольку метод Invoke() универсальный и используется как для вызова методов объекта, так и для получения/установки свойств, то у него присутствует еще один параметр, который указывает, что нужно сделать – вызвать метод (DISPATCH_METHOD), получить значение свойства (DISPATCH_PROPERTYGET) или установить значение свойства (DISPATCH_PROPEERTYPUT).

Посмотрим теперь, как это работает на уровне исполнения программного кода конфигурации.

Алгоритм установки свойства COM-объекта

Что же происходит в тот момент, когда программный код на встроенном языке обрабатывает установку свойства COM-объекта? Например, для следующего участка программного кода:

// Соединение - COM-соединение с внешней ИБ.
НовыйЭлемент = Соединение.Справочники.Корреспонденты.СоздатьЭлемент();

// Здесь будет вызвано исключение времени исполнения.
НовыйЭлемент.ПолноеНаименование = "Иванов Иван Иванович";


Этот программный код будет выполнен приблизительно следующим образом (псевдокод):
// Соединение - COM-соединение с внешней ИБ.
НовыйЭлемент = Соединение.Справочники.Корреспонденты.СоздатьЭлемент();

// Получаем идентификатор свойства "ПолноеНаименование".
ID = IDispatch::GetIDsOfNames(НовыйЭлемент, "ПолноеНаименование");

// Вызываем операцию установки значения свойства.
IDispatch::Invoke(НовыйЭлемент, ID, DISPATCH_PROPEERTYPUT, "Иванов Иван Иванович");


Конечно же, такое представление ситуации несколько утрировано, и более того, я сознательно исказил сигнатуры методов, чтобы сосредоточиться на механике происходящего, а не на конкретных деталях ее реализации. Но этой информации более чем достаточно, чтобы понять, что приводит к возникновению исключения.

Причины ошибки

Как мы знаем, с именем ПолноеНаименование у объекта типа СправочникОбъект есть не только свойство (реквизит), но и метод, но система об этом ничего не знает (точнее, никак не отрабатывает такую ситуацию)! И, как можно уже догадаться, именно числовой идентификатор метода, а не свойства, возвращается нам вызовом GetIDsOfNames(). А для метода не определена операция DISPATCH_PROPEERTYPUT (установка значения свойства).

И как результат, система вызывает исключение времени исполнения – “Метод не найден”. Понятно теперь и о каком методе идет речь: речь идет об операции установки значения свойства (DISPATCH_PROPEERTYPUT). Не так уж и сложно.

Хорошо, что приводит к ошибке мы поняли, но как теперь с этим быть – как все-таки установить значение реквизита ПолноеНаименование у объекта, полученного из другой информационной базы через COM-соединение? Поскольку повлиять на внутренние алгоритмы платформы мы не имеем возможности, для решения задачи (мы же все еще хотим записать значение реквизита ПолноеНаименование?) нам придется использовать обходные пути.

Как обойти ошибку

Переименовать проблемный реквизит

Самый очевидный способ избавиться от исключения – переименовать реквизит таким образом, чтобы он не совпадал с именами существующих методов этого же объекта. Да, платформа различает контекст использования и при наличии одноименных свойств и методов умеет отличать обращение к свойству от вызова метода, в результате в программном коде мы часто видим локальные переменные вида Строка или ТекущаяДата, и они спокойно сосуществуют рядом с одноименными методами. Но, как мы уже знаем есть по крайней мере одна ситуация, в которой это не работает. Лично я в общем случае считаю использование идентификаторов, совпадающих с именами встроенных в платформу методов и свойств считаю дурным тоном, но официальные стандарты никак это не регламентируют.

Так или иначе, переименовать идентификатор не всегда возможно. В частности, в нашей ситуации, конфигурация к которой мы подключаемся – 1С:Документооборот 8, и простое переименование реквизита одного из ключевых справочников системы сильно усложнит дальнейшую поддержку конфигурации (нам ведь и в модулях обращения к реквизиту потребуется везде изменить). Поэтому нужны другие способы.

Выполнить код установки в контексте ИБ, являющейся сервером COM-соединения

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

Если есть возможность модифицировать ИБ, к которой мы подключаемся, то можно, например, в модуле внешнего соединения реализовать экспортный метод, который будет выполнять необходимые действия.

Можно поступить и более хитроумно – убедиться, что у реквизита установлен признак “Заполнять из данных заполнения”, и вызывать у объекта метод Заполнить(), передав в качестве параметра объект Структура (ключ – имя реквизита, значение – значение реквизита). Только предварительно надо убедиться, что ОбработкаЗаполнения реализована с учетом возможности такого вызова (или не реализована вообще). В нашем случае такой подход оказался бы вполне рабочим вариантом.

И, наконец, если возможности модифицировать ИБ, к которой мы подключаемся, нет, то можно использовать почти хакерский подход. Мы можем реализовать внешнюю обработку с единственным экспортируемым методом модуля – УстановитьЗначениеРеквизита() и реализовать этот метод следующим образом:

Функция УстановитьЗначениеРеквизита(Объект, ИмяРеквизита, ЗначениеРеквизита) Экспорт
    Объект[ИмяРеквизита] = ЗначениеРеквизита;
КонецФункции


а в клиентском коде COM-соединения поступать так:

// Соединение - COM-соединение с внешней ИБ.
НовыйЭлемент = Соединение.Справочники.Корреспонденты.СоздатьЭлемент();
// Во внешней базе создаем нашу служебную обработку...
Хак = Соединение.ВнешниеОбработки.Создать("D:\Temp\Клиент\УстановкаРеквизита.epf");
// ... и вызываем реализованный метод.
Хак.УстановитьЗначениеРеквизита(НовыйЭлемент, "ПолноеНаименование", "Иванов Иван Иванович");


Думаю, идея вполне понятна без дополнительных комментариев. Саму обработку можно разместить в макете конфигурации-клиента, а инициализацию внешней обработки, если она будет задействована внутри цикла, надо, конечно же, выполнять вне этого цикла.

[необходимо зарегистрироваться для просмотра ссылки]

От себя добавлю: думаю, что в 8.1 ситуация обстоит так же.

Спасибо сказали: Petre,

Не нашли ответа на свой вопрос?
Зарегистрируйтесь и задайте новый вопрос.


Ответить Новая тема
1 чел. читают эту тему (гостей: 1, скрытых пользователей: 0)
Пользователей: 0

 

RSS Текстовая версия Сейчас: 25.04.24, 2:08
1С Предприятие 8.3, 1С Предприятие 8.2, 1С Предприятие 8.1, 1С Предприятие 8.0, 1С Предприятие 7.7, Литература 1С, Общие вопросы по администрированию 1С, Методическая поддержка 1С - всё в одном месте: на Украинском 1С форуме!