Показаны сообщения с ярлыком COM. Показать все сообщения
Показаны сообщения с ярлыком COM. Показать все сообщения

22 мая 2008

Smart pointer for COM objects

Указатели на объекты должны быть умными - чтобы автоматически управлять временем жизни объекта. Это понятное дело. Для COM объектов есть широкий выбор стандартных умных указателей: _com_ptr_t, CComPtr, CComQIPtr. Какой же из них предпочесть? Большой разницы между ними нет, однако вся соль - в нюансах.

Шаблоны CComPtr и CComQIPtr предлагает нам ATL. Так что ести вы не используете ATL у себя в проекте, то ваш выбор однозначен - _com_ptr_t. Тут даже думать не надо - особого смысла пораждать зависимость от ATL ради CComPtr/CComQIPtr нет.

У _com_ptr_t есть полезный макрос для создания конкретных типов (через typedef): _COM_SMARTPTR_TYPEDEF. Выглядит это так:
// = typedef _com_ptr_t<...> IFooBarPtr;
_COM_SMARTPTR_TYPEDEF(IFooBar, __uuidof(IFooBar));
...
IFooBarPtr FooBar;
Еще бОльшая прелесть состоит в том, что для большинства интерфейсов уже заданы такие typedef-ы, то есть можно смело писать IHTMLDocument2Ptr Document; без соответствующего тайпдефа. Однако засада в том, что сделаны не все тайпдефы и для некоторых интерфейсов придется либо самим объявлять тип через _COM_SMARTPTR_TYPEDEF или использовать _com_ptr_t напрямую.

_com_ptr_t, как и CComQIPtr умеют вызывать QueryInterface "на ходу", что довольно удобно:
IDispatchPtr RawDocument = GetDocument();
IHTMLDocument2Ptr Document(RawDocument); // здесь автоматически вызывается QueryInterface
Небольшим плюсом CComQIPtr может служить его бОльшая лаконичность по сравнению с _com_ptr_t:

CComQIPtr<IHTMLDocument4> Document;
vs
_com_ptr_t<IHTMLDocument4, &__uuidof(IHTMLDocument4)> Document;

Вывод из этого я делаю такой: если используете ATL — берите CComQIPtr, если нет — _com_ptr_t. Если вдруг придется "слезть" с ATL, то будет довольно просто перевести код с CComQIPtr на _com_ptr_t.

Post scriptum
Все перечисленные выше умные указатели переопределяют operator& так, что он возвращает адрес обычного указателя, вместо адреса умного указателя. Это очень удобно для таких вот вызовов, которых в COM предостаточно:
IWebBrowserPtr Browser = GetBrowser();
IDispatchPtr Document;
// calling HRESULT STDMETHODCALLTYPE get_Document(IDispatch **ppDisp);
Browser->get_Document(&Document);
Соответственно, если будет желание положить элемент в контейнер, то оператору & очень желательно вернуть прежнюю логику. Например, обернув умный указатель в CAdapt из ATL:
std::vector< CAdapt< CComQIPtr<IDispatch> > > Objects
А если понадобится, то можно в любой момент сделать "срезку" до CComQIPtr/_com_ptr_t с его переопределенным оператором &:
BOOST_FOREACH(CComQIPtr<IDispatch> &Object, Objects)
Browser->get_Document(&Object);

PPS. Дискуссии по теме: здесь и здесь.

24 февраля 2007

IDispatch Wrapper

Работать с COM-объектами из pure C довольно неудобно. Тут одназначно нужно использовать разные C++ wrapper-ы и helper-ы. Некоторые из них доступны "из коробки": _com_ptr_t, _variant_t, директива #import. Но с IDispatch все же не особо удобно работать, как например в Visual Basic.

Немного погуглив обнаружил отличный wrapper для IDispatch by Mike Morearty. Позволяет намного упростить жизнь:
CDispatchPtr htmldoc = ...;
_bstr_t html = htmldoc.Get("body").Get("innerHTML");
htmldoc.Put("title", "New Title");
htmldoc.Get("body").Get("firstChild").Invoke("insertAdjacentText", "afterBegin", "hello world");

Надо будет еще в дополнение к нему написать wrapper для COM-коллекций в STL-совместимые контейнеры.

PS. Хотя в этом IDispatch wrapper-е явно не сказано про лицензию, но по почте Майк ответил "you're free to use the code, and to modify it."