Позднее связывание COM-объектов с помощью C ++ Builder

Мы взаимодействуем с некоторыми сторонними COM-объектами из приложения C ++ Builder 2010.

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

object->myProperty = 42;
object->doSomething(666);

Однако нас укусили изменения в интерфейсе COM-объекта (который все еще расширяется и развивается), что приводит к сбою нашего собственного приложения, потому что некоторые идентификаторы GUID методов, похоже, становятся недействительными - даже если единственным изменением интерфейса было добавление нового метода).

Позднее связывание было предложено как способ решения этой проблемы. Я думаю, что для этого нужно изменить наш код примерно так:

object.OlePropertySet("myProperty", 42);
object.OlePrcedure("doSomething", 666);

Очевидно, это болезненно читать и писать, поэтому нам пришлось бы вместо этого писать классы-оболочки.

Есть ли способ автоматически генерировать обертки с поздним связыванием при импорте библиотеки типов? И если да, то достаточно ли они умен, чтобы выполнять текстовую привязку только один раз при создании объекта, а не при каждом вызове метода?


person Roddy    schedule 26.07.2012    source источник


Ответы (2)


Когда вы импортируете TypeLibrary для COM-объекта, который поддерживает позднее связывание (когда он реализует интерфейс IDispatch), импортер может генерировать отдельные классы оболочки (не компоненты) как для статического, так и для позднего связывания.

Добавление нового метода к существующему интерфейсу не должно сделать ваш код недействительным. У методов нет GUID. Однако для интерфейса на основе IDispatch его методы действительно имеют DISPID значения, связанные с ними, и эти DISPID значения могут быть изменены от одного выпуска к другому. Хотя ни один уважаемый COM-разработчик никогда не должен этого делать после того, как определение интерфейса заблокировано.

person Remy Lebeau    schedule 27.07.2012
comment
Спасибо, не могли бы вы указать мне на пример того, как я использую (или выбираю) классы с поздним связыванием? В настоящее время я использую объекты T ‹classname› из заголовка <libfile>_OCX.hpp. - person Roddy; 30.07.2012
comment
Я считаю, что C ++ и C ++ Builder не имеют сложных функций позднего связывания, которые есть в Delphi, которые являются особенностью компилятора Delphi. (Объявите вариант, вызовите CreateOleObject, вызовите метод.) Вместо этого вам придется сделать это жестким путем. Посмотрите примеры OLE Automation на C ++, техника и стиль кодирования такие же. - person Warren P; 02.07.2013
comment
Классы Variant и OleVariant в C ++ Builder поддерживают операции позднего связывания, аналогичные тем, что компилятор Delphi изначально поддерживает, как показал Родди, то есть: object.myProperty := 42; в Delphi становится object.OlePropertySet("myProperty", 42); в C ++, object.doSomething(666); в Delphi становится object.OleProcedure("doSomething", 666); в C ++ и т. Д. - person Remy Lebeau; 02.07.2013
comment
@RemyLebeau - добавлен ответ с использованием IxxxDisp оберток, что дает привязки на основе DISPID. - person Roddy; 18.12.2013

После глубокого исследования кода и заголовков, сгенерированных TLIBIMP, это оказалось довольно просто.

Если в вашей библиотеке типов есть класс Foo, то после импорта библиотеки типов вы обычно будете использовать автоматически сгенерированные классы интеллектуальных указателей IFooPtr.

{
  IFooPtr f;
  ...
  f->myMethod(1,2);
}

Следует отметить, что на этом этапе привязки являются статическими, то есть они зависят не только от GUID объектов и DISPID методов, но и от точного макета VTable в DLL. Любые изменения, которые влияют на vtable - например, добавление дополнительного метода к базовому классу Foo приведет к сбою вызова метода.

Чтобы использовать динамические привязки, вы можете использовать классы IFooDisp вместо IFooPtr. Опять же, это умные оболочки, автоматически обрабатывающие время жизни объектов. Обратите внимание, что с этими классами вы должны использовать оператор . для доступа к методам, а не косвенный оператор ->. Использование оператора косвенного обращения вызовет метод, но через статическую привязку.

{
  IFooDisp f;
  ...
  f.myMethod(1,2);
}

При использовании этих оболочек на основе IDispatch методы будут отправляться по их идентификаторам DISPID, даже если макет vtable объектов изменен. Я думаю, что эти классы также позволяют выполнять отправку по имени функции, а не по DISPID, но подробности этого не подтверждены.

person Roddy    schedule 17.12.2013
comment
Примечание. Интерфейсы должны быть заморожены после публикации, т. Е. Заданный IID соответствует заданной таблице vtable, конец истории. Таким образом, клиенты могут безопасно использовать привязку к vtable. Если вы хотите добавить новых участников, вы должны быть производными от этого интерфейса. - person M.M; 09.05.2014
comment
Также (в любом случае для DAX) f.myMethod использует IDispatch::Invoke с жестко запрограммированным DISPID; и f->myMethod использует vtable. - person M.M; 09.05.2014
comment
@MattMcNabb Спасибо за заголовок DAX. BCB2010 не использует это, хотя AFAIK. И да, в идеальном мире интерфейс был бы заморожен. К сожалению, мне кажется, что я там не живу. - person Roddy; 09.05.2014