KnigaRead.com/
KnigaRead.com » Компьютеры и Интернет » Программирование » Дональд Бокс - Сущность технологии СОМ. Библиотека программиста

Дональд Бокс - Сущность технологии СОМ. Библиотека программиста

На нашем сайте KnigaRead.com Вы можете абсолютно бесплатно читать книгу онлайн Дональд Бокс, "Сущность технологии СОМ. Библиотека программиста" бесплатно, без регистрации.
Перейти на страницу:

wcscpy(g_wsz, bstr);


должен быть защищен от возможных нулевых указателей:


wcscpy (g_wsz, bstr ? bstr : OLESTR(""));


Для упрощения использования BSTR в заголовочном файле ustring.h содержится простая встраиваемая функция:


intline OLECHAR *SAFEBSTR(BSTR b)

{

return b ? b : OLESTR("");

}


Разрешение использовать нулевые указатели в качестве BSTR делает тип данных более эффективным с точки зрения использования памяти, хотя и приходится засорять код этими простыми проверками.

Простые типы, показанные на рис. 2.6, могут компоноваться вместе с применением структур языка С. IDL подчиняется правилам С для пространства имен тегов (tag namespace). Это означает, что большинство IDL-определений интерфейсов либо используют операторы определения типа (typedef):


typedef struct tagCOLOR

{

double red;

double green;

double blue;

} COLOR;

HRESULT SetColor([in] const COLOR *pColor);


либо должны использовать ключевое слово struct для квалификации имени тега:


struct COLOR { double red; double green; double blue; };

HRESULT SetColor([in] const struct COLOR *pColor);


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

IDL и СОМ поддерживают также объединения (unions). Для обеспечения однозначной интерпретации объединения IDL требует, чтобы в этом объединении имелся дискриминатор (discriminator), который показывал бы, какой именно член объединения используется в данный момент. Этот дискриминатор должен быть целого типа (integral type) и должен появляться на том же логическом уровне, что и само объединение. Если объединение объявлено вне области действия структуры, то оно считается неинкапсулированным (nonencapsulated):


union NUMBER

{

[case(1)] long i;

[case(2)] float f;

};


Атрибут [case] применен для установления соответствия каждого члена объединения своему дискриминатору. Для того чтобы связать дискриминатор с неинкапсулированным объединением, необходимо применить атрибут [switch_is]:


HRESULT Add([in, switch_is(t)] union NUMBER *pn, [in] short t);


Если объединение заключено вместе со своим дискриминатором в окружающую структуру, то этот составной тип (aggregate type) называется инкапсулированным, или размеченным объединением (discriminated union):


struct UNUMBER

{ short t; [switch_is(t)]

union VALUE

{

[case(1)] long i;

[case(2)] float f;

};

};


СОМ предписывает для использования с Visual Basic одно общее размеченное объединение. Это объединение называется VARIANT[4] и может хранить объекты или ссылки на подмножество базовых типов, поддерживаемых IDL. Каждому из поддерживаемых типов присвоено соответствующее значение дискриминатора:


VT_EMPTY nothing

VT_NULL SQL style Null

VT_I2 short

VT_I4 long

VT_R4 float

VT_R8 double

VT_CY CY (64-bit currency)

VT_DATE DATE (double)

VT_BSTR BSTR

VT_DISPATCH IDispatch *

VT_ERROR HRESULT

VT_BOOL VARIANT_BOOL (True=-1, False=0)

VT_VARIANT VARIANT *

VT_UNKNOWN IUnknown *

VT_DECIMAL 16 byte fixed point

VT_UI1 opaque byte


Следующие два флага можно использовать в сочетании с вышеприведенными тегами, чтобы указать, что данный вариант (variant) содержит ссылку или массив данного типа:

VT_ARRAY Указывает, что вариант содержит массив SAFEARRAY

VT_BYREF Указывает, что вариант является ссылкой

СОМ предлагает несколько API-функций для управления VARIANT:


// initialize a variant to empty

// обнуляем вариант

void VariantInit(VARIANTARG * pvarg);

// release any resources held in a variant

// освобождаем все ресурсы, используемые в варианте

HRESULT VariantClear(VARIANTARG * pvarg);

// deep-copy one variant to another

// полностью копируем один вариант в другой

HRESULT VariantCopy(VARIANTARG * plhs, VARIANTARG * prhs);

// dereference and deep-copy one variant into another

// разыменовываем и полностью копируем один вариант в другой

HRESULT VariantCopyInd(VARIANT * plhs, VARIANTARG * prhs);

// convert a variant to a designated type

// преобразуем вариант к указанному типу

HRESULT VariantChangeType(VARIANTARG * plhs, VARIANTARG * prhs, USHORT wFlags, VARTYPE vtlhs);

// convert a variant to a designated type

// преобразуем вариант к указанному типу (с явным указанием кода локализации)

HRESULT VariantChangeTypeEx(VARIANTARG * plhs, VARIANTARG * prhs, LCID lcid, USHORT wFlags, VARTYPE vtlhs);


Эти функции значительно упрощают управление VARIANT'ами. Чтобы понять, как используются эти API-функции, рассмотрим метод, принимающий VARIANT в качестве [in]-параметра:


HRESULT UseIt([in] VARIANT var);


Следующий фрагмент кода демонстрирует, как передать в этот метод целое число:


VARIANT var;

VariantInit(&var);

// initialize VARIANT

// инициализируем VARIANT

V_VT(&var) = VT_I4;

// set discriminator

// устанавливаем дискриминатор

V_I4(&var) = 100;

// set union

// устанавливаем объединение

HRESULT hr = pItf->UseIt(var);

// use VARIANT

// используем VARIANT

VariantClear(&var);

// free any resources in VARIANT

// освобождаем все ресурсы VARIANT


Отметим, что этот фрагмент кода использует макросы стандартного аксессора (accessor) для доступа к элементам данных VARIANT. Две следующие строки


V_VT(&var) = VT_I4;

V_I4(&var) = 100;


эквивалентны коду, который обращается к самим элементам данных:


var.vt = VT_I4;

var.lVal = 100;


Первый вариант предпочтительнее, так как он может компилироваться на С-трансляторах, которые не поддерживают неименованных объединений.

Ниже приведен пример того, как с помощью приведенной выше технологии реализация метода использует параметр 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);

}


Для уменьшения количества дополнительных вызовов методов между клиентом и объектом указатели интерфейса с динамически типизируемым типом должны всегда использоваться вместо указателей интерфейса со статически типизируемым типом IUnknown.


Атрибуты и свойства

Иногда бывает полезно показать, что объект имеет некие открытые свойства, которые могут быть доступны и/или которые можно модифицировать через СОМ-интерфейс. СОМ 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);

}


Использование атрибутов [propget] и [propput] информирует компилятор IDL, что методы, которые ему соответствуют, должны быть отображены в преобразователи свойств (property mutators) или в аксессоры на языках, явно поддерживающих свойства. Применительно к Visual Basic это означает, что элементами Age, HairCount и CurrentThought можно манипулировать, используя тот же синтаксис, как при обращении к элементам структуры:

Перейти на страницу:
Прокомментировать
Подтвердите что вы не робот:*