Сущность технологии СОМ. Библиотека программиста
Сущность технологии СОМ. Библиотека программиста читать книгу онлайн
В этой книге СОМ исследуется с точки зрения разработчика C++. Написанная ведущим специалистом по модели компонентных объектов СОМ, она раскрывает сущность СОМ, помогая разработчикам правильно понять не только методы модели программирования СОМ, но и ее основу. Понимание мотивов создания СОМ и ее аспектов, касающихся распределенных систем, чрезвычайно важно для тех разработчиков, которые желают пойти дальше простейших приложений СОМ и стать по-настоящему эффективными СОМ-программистами. Показывая, почему СОМ для распределенных систем (Distributed СОМ) работает именно так, а не иначе, Дон Бокс дает вам возможность применять эту модель творчески и эффективно для ежедневных задач программирования.
Внимание! Книга может содержать контент только для совершеннолетних. Для несовершеннолетних чтение данного контента СТРОГО ЗАПРЕЩЕНО! Если в книге присутствует наличие пропаганды ЛГБТ и другого, запрещенного контента - просьба написать на почту [email protected] для удаления материала
Следует обсудить еще один дополнительный тип данных, связанный с текстом, – BSTR. Строковый тип BSTR нужно применять во всех интерфейсах, которые предполагается использовать из языков Visual Basic или Java. Строки BSTR являются OLECHAR-строками с префиксом длины (length-prefix) в начале строки и нулем в ее конце. Префикс длины показывает число байт, содержащихся в строке (исключая завершающий нуль) и записан в форме четырехбайтового целого числа, непосредственно предшествующего первому символу строки. Рисунок 2.7 демонстрирует BSTR на примере строки «Hi». Чтобы позволить методам свободно возвращать строки BSTR без заботы о выделении памяти, все BSTR размещены с помощью распределителя памяти, управляемого СОМ. В СОМ предусмотрено несколько API-функций для управления BSTR:
// from oleauto.h
// allocate and initialize a BSTR
// выделяем память и инициализируем строку BSTR
BSTR SysAllocString(const OLECHAR *psz);
BSTR SysAllocStringLen(const OLECHAR *psz, UINT cch);
// reallocate and initialize a BSTR
// повторно выделяем память и инициализируем BSTR
INT SysReAllocString(BSTR *pbstr, const OLECHAR *psz);
INT SysReAllocStringLen(BSTR *pbstr, const OLECHAR * psz, UINT cch);
// free a BSTR
// освобождаем BSTR void SysFreeString(BSTR bstr);
// peek at length-prefix as characters or bytes
// считываем префикс длины как число символов или байт
UINT SysStringLen(BSTR bstr);
UINT SysStringByteLen(BSTR bstr);
При пересылке строк методу в качестве параметров типа [in] вызывающий объект должен заботиться о том, чтобы вызвать SysAllocString прежде, чем запускать сам метод, и чтобы вызвать SysFreeString после того, как метод закончил работу. Рассмотрим следующее определение метода:
HRESULT SetString([in] BSTR bstr);
Пусть в вызывающей программе уже имеется строка, совместимая с OLECHAR, тогда для того, чтобы преобразовать строку в BSTR до вызова метода, необходимо следующее:
// convert raw OLECHAR string to a BSTR
// преобразовываем «сырую» строку OLECHAR в строку BSTR
BSTR bstr = SysAllocString(OLESTR(«Hello»));
// invoke method
// вызываем метод HRESULT hr = p->SetString(bstr);
// free BSTR
// освобождаем BSTR SysFreeString(bstr);
Промежуточный класс для работы с BSTR, _UBSTR, включен в заголовочный файл ustring.h:
// from ustring.h (book-specific header file)
// из ustring.h (специфический для данной книги заголовочный файл)
class _UBSTR
{
BSTR m_bstr;
public:
_UBSTR(const char *psz) : m_bstr(SysAllocStringLen(0, strlen(psz)))
{
mbstowcs(m_bstr, psz, INT_MAX);
}
_UBSTR(const wchar_t *pwsz) : m_bstr(SysAllocString(pwsz))
{
}
operator BSTR (void) const
{ return m_bstr; }
~_UBSTR(void)
{ SysFreeString(m_bstr); }
};
При наличии такого промежуточного класса предыдущий фрагмент кода значительно упростится:
// invoke method
// вызываем метод
HRESULT hr = p->SetString(_UBSTR(OLESTR(«Hello»)));
Заметим, что в промежуточном классе UBSTR могут быть в равной степени использованы строки типов char и wchar_t.
При передаче из метода строк через параметры типа [out] объект обязан вызвать SysAllocString, чтобы записать результирующую строку в буфер. Затем вызывающий объект должен освободить буфер путем вызова SysFreeString. Рассмотрим следующее определение метода:
HRESULT GetString([out, retval] BSTR *pbstr);
При реализации метода потребуется создать новую BSTR-строку для возврата вызывающему объекту:
STDMETHODIMP MyClass::GetString(BSTR *pbstr)
{
*pbstr = SysAllocString(OLESTR(«Coodbye!»)) ;
return S_OK;
}
Теперь вызывающий объект должен освободить строку сразу после того, как она скопирована в управляемый приложением строковый буфер:
extern OLECHAR g_wsz[];
BSTR bstr = 0;
HRESULT hr = p->GetString(&bstr);
if (SUCCEEDED(hr))
{
wcscpy(g_wsz, bstr); SysFreeString(bstr);
}
Тут нужно рассмотреть еще один важный аспект BSTR. В качестве BSTR можно передать нулевой указатель, чтобы указать на пустую строку. Это означает, что предыдущий фрагмент кода не совсем корректен. Вызов wcscpy:
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):