Ниже приведен пример того, как с помощью приведенной выше технологии реализация метода использует параметр VARIANT в качестве строки:
STDMETHODIMP MyClass::UseIt( /*[in] */ VARIANT var)
{
// declare and init a second VARIANT
// объявляем и инициализируем второй VARIANT
VARIANT var2;
VariantInit(&var2);
// convert var to a BSTR and store it in var2
// преобразуем переменную в BSTR и заносим ее в var2
HRESULT hr = VariantChangeType(&var2, &var, 0, VT_BSTR);
// use the string
// используем строку
if (SUCCEEDED(hr))
{
ustrcpy(m_szSomeDataMember, SAFEBSTR(V_BSTR(&var2)));
// free any resources held by var2
// освобождаем все ресурсы, поддерживаемые var2
VariantClear(&var2);
}
return hr;
}
Отметим, что API-процедура VariantChangeType способна осуществлять сложное преобразование любого переданного клиентом типа из VARIANT в нужный тип (в данном случае BSTR).
Один из последних типов данных, который вызывает дискуссию, – это интерфейс СОМ. Интерфейсы СОМ могут быть переданы в качестве параметров метода одним из двух способов. Если тип интерфейсного указателя известен на этапе проектирования, то тип интерфейса может быть объявлен статически:
HRESULT GetObject([out] IDog **ppDog);
Если же тип на этапе проектирования неизвестен, то разработчик интерфейса может дать пользователю возможность задать тип на этапе выполнения. Для поддержки динамически типизируемых интерфейсов в IDL имеется атрибут [iid_is]:
HRESULT GetObject([in] REFIID riid, [out, iid_is(riid)] IUnknown **ppUnk);
Хотя эта форма будет работать вполне хорошо, следующий вариант предпочтительнее из-за его подобия с QueryInterface:
HRESULT GetObject([in] REFIID riid, [out, iid_is(riid)] void **ppv);
Атрибут [iid_is] можно использовать как с параметрами [in], так и [out] для типов IUnknown * или void *. Для того чтобы использовать параметр интерфейса с динамически типизируемым типом, необходимо просто установить IID указателя требуемого типа:
IDog *pDog = 0; HRESULT hr = pItf->GetObject(IID_IDog, (void**)&pDog);
Соответствующая реализация для инициализации этого параметра просто использовала бы метод QueryInterface для нужного объекта:
STDMETHODIMP Class::GetObject(REFIID riid, void **ppv)
{
extern IUnknown * g_pUnkTheDog;
return g_pUnkTheDog->QueryInterface(riid, ppv);
}
Для уменьшения количества дополнительных вызовов методов между клиентом и объектом указатели интерфейса с динамически типизируемым типом должны
Атрибуты и свойства
Иногда бывает полезно показать, что объект имеет некие открытые свойства, которые могут быть доступны и/или которые можно модифицировать через СОМ-интерфейс. СОМ IDL позволяет аннотировать методы интерфейса с тем, чтобы данный метод либо модифицировал, либо читал именованный атрибут объекта. Рассмотрим такое определение интерфейса:
[ object, uuid(0BB3DAE1-11F4-11d1-8C84-0080C73925BA) ]
interface ICollie : IDog
{
// Age is a read-only property
// Age (возраст) – это свойство только для чтения
[propget] HRESULT Age([out, retval] long *pVal);
// HairCount is a read/write property
// HairCount (счетчик волос) – свойство для чтения/записи
[propget] HRESULT HairCount([out, retval] long *pVal);
[propput] HRESULT HairCount([in] long val);
// CurrentThought is a write-only property
// CurrentThought (текущая мысль) – свойство только для записи
[propput] HRESULT CurrentThought([in] BSTR val);
}
Использование атрибутов
Sub UseCollie(fido as ICollie)
fido.HairCount = fido.HairCount – (fido.Age * 1000)
fido.CurrentThought = «I wish I had a bone»
End Sub
С++-отображение этого интерфейса просто прибавляет к именам методов конструкции put или get, чтобы подсказать программисту, что обращение относится к свойству:
void UseCollie(ICollie *pFido)
{
long n1, n2;
HRESULT hr = pFido->getHairCount(&n1);
assert(SUCCEEDED(hr));
hr = pFido->getAge(&n2);
assert(SUCCEEDED(hr));
hr = pFido->putHairCount(n1 – (n2 * 1000)): assert(SUCCEEDED(hr));
BSTR bstr = SysAllocString(OLESTR(«I wish I had a bone»));
hr = pFido->putCurrentThought(bstr);
assert(SUCCEEDED(hr));
SysFreeString(bstr);
}
Хотя свойства напрямую не обеспечивают развития, они полезны для выполнения точных преобразований на те языки, которые их поддерживают[1].
Исключения
СОМ имеет специфическую поддержку выбрасывания (throwing) исключительных ситуаций из реализации методов. Поскольку в языке C++ не существует двоичного стандарта для исключений, СОМ предлагает явные API-функции для выбрасывания и перехвата объектов СОМ-исключений:
// throw an exception
// возбуждаем исключения
HRESULT SetErrorInfo([in] ULONG reserved, //