Сущность технологии СОМ. Библиотека программиста - Дональд Бокс
- Дата:20.06.2024
- Категория: Компьютеры и Интернет / Программирование
- Название: Сущность технологии СОМ. Библиотека программиста
- Автор: Дональд Бокс
- Просмотров:3
- Комментариев:0
Шрифт:
Интервал:
Закладка:
FADF_EMBEDDEO
/* array is embedded in a structure */
/* массив вложен в структуру */
FADF_FIXEDSIZE
/* may not be resized or reallocated */
/* не может изменить размеры или быть перемещен*/
FADF_BSTR
/* an array of BSTRs */
/* массив из BSTR */
FADF_UNKNOWN
/* an array of IUnknown* */
/* массив из IUnknown* */
FADF_DISPATCH
/* an array of IDispatch* */
/* массив из IDispatch* */
FADF_VARIANT
/* an array of VARIANTS */
/* массив из VARIANTS */
Для предоставления SAFEARRAY возможности определять типы данных своих элементов, компилятор IDL распознает специальный, специфический для SAFEARRAY, синтаксис:
HRESULT Method([in] SAFEARRAY(type) *ppsa);
где type – тип элемента в SAFEARRAY. Соответствующий прототип данного метода в C++ выглядел бы примерно так:
HRESULT Method(SAFEARRAY **psa);
Отметим, что в определении IDL используется только один уровень косвенности; в то же время в соответствующем определении C++ используются два уровня косвенности. Рассмотрим следующее определение на IDL, задающее массив типа SAFEARRAY из коротких целых чисел:
HRESULT Method([in] SAFEARRAY(short) *psa);
Соответствующее определение на Visual Basic выглядело бы таким образом:
Sub Method(ByVal psa As Integer())
Отметим, что в варианте на Visual Basic не указано явно размерностей массива. Напомним, однако, что Visual Basic поддерживает массивы с фиксированной длиной.
Тип данных SAFEARRAY поддерживается весьма богатым набором API-функций, которые позволяют изменять размерность массивов и производить обход их содержимого переносимым образом. Для доступа к элементам типа SAFEARRAY СОМ предусматривает следующие вызовы API-функций:
// get a pointer to the actual array elements
// получаем указатель на фактически элементы массива
HRESULT SafeArrayAccessData([in] SAFEARRAY *psa, [out] void ** ppv);
// release pointer returned by SafeArrayAccessData
// освобождаем указатель, возвращенный функцией SafeArrayAccessData
HRESULT SafeArrayUnaccessData([in] SAFEARRAY *psa);
// Get number of dimensions
// Получаем количество измерений
ULONG SafeArrayGetDim([in] SAFEARRAY *psa);
// Get upper bound of a dimension
// Получаем верхнюю границу измерения
HRESULT SafeArrayGetUBound([in] SAFEARRAY *psa, [in] UINT nDim, [out] long *pUBound);
// Get lower bound of a dimension
// Получаем нижнюю границу измерения
HRESULT SafeArrayGetLBound([in] SAFEARRAY *psa, [in] UINT nDim, [out] long *pLBound);
Эти методы обеспечивают компактный и надежный способ доступа к текущему содержимому массива. Рассмотрим следующий код на IDL:
HRESULT Sum([in] SAFEARRAY(long) *ppsa, [out, retval] long *pSum);
Тогда следующая реализация метода будет вычислять сумму элементов массива типа SAFEARRAY, состоящего из длинных целых чисел (long integers):
STDMETHODIMP MyClass::Sum(SAFEARRAY **ppsa, long *pnSum)
{
assert(ppsa && *ppsa && pnSum);
assert(SafeArrayGetDim(*ppsa) == 1);
long iUBound, iLBound;
// note that dimension indices are one-based
// отметим, что индексы размерности начинаются с единицы
HRESULT hr = SafeArrayGetUBound(*ppsa, 1, &iUBound);
assert(SUCCEEDED(hr));
hr = SafeArrayGetLBound(*ppsa, 1, &iLBound);
assert(SUCCEEDED(hr));
long *prgn = 0;
hr = SafeArrayAccessData(*ppsa, (void**)&prgn);
*pnSum = 0;
for (long i = 0; i < iUBound – iLBound; i++)
*pnSum += prgn[i];
SafeArrayUnaccessData(*ppsa);
return S_OK;
}
Отметим, что вызовы любых API-функций, которые имеют дело с размерностями массива, используют индексы, начинающиеся с единицы.
Приведенный выше фрагмент кода просто манипулировал содержимым существующего SAFEARRAY-массива. Для создания одномерного массива типа SAFEARRAY для передачи его в качестве параметра метода в СОМ имеется следующая API-функция, которая выделяет память для структуры SAFEARRAY и элементов этого массива в одном непрерывном блоке памяти:
SAFEARRAY *SafeArrayCreateVector(
[in] VARTYPE vt, // element type
// тип элемента
[in] long iLBound, // index of lower bound
// индекс нижней границы
[in] unsigned int cElems); // # of elements
// число элементов
Кроме того, в СОМ имеются различные функции, предназначенные для размещения многомерных массивов, однако их рассмотрение выходит за рамки данной дискуссии. При таком определении метода на IDL:
HRESULT GetPrimes([in] long nStart, [in] long nEnd, [out] SAFEARRAY(long) *ppsa);
следующее определение метода на C++ возвращает вызывающей программе массив типа SAFEARRAY, размещенный в вызываемом методе:
STDMETHODIMP MyClass::GetPrimes (long nMin, long nMax, SAFEARRAY **ppsa)
{
assert(ppsa);
UINT cElems = GetNumberOfPrimes(nMin, nMax);
*ppsa = SafeArrayCreateVector(VT_I4, 0, cElems);
assert(*ppsa);
long *prgn = 0;
HRESULT hr = SafeArrayAccessData(*ppsa, (void**)&prgn);
assert(SUCCEEDED(hr));
for (UINT i=0; i < cElems; i++)
prgn[i] = GetNextPrime(i ? prgn[1 – 1] : nMin);
SafeArrayUnaccessData(*ppsa);
return S_OK;
}
Соответствующий код с клиентской стороны выглядел бы на Visual Basic примерно так:
Function GetSumOfPrimes(ByVal nMin as Long, ByVal nMax as Long) as Long
Dim arr() as Long
Dim n as Variant
Objref.GetPrimes nMin, nMax, arr
GetSumOfPrimes = 0
for each n in arr
GetSumOfPrimes = GetSumOfPrimes + n
Next n
End Function
что соответствует следующему коду на C++:
long GetSumOfPrimes (long nMin, long nMax)
{
SAFEARRAY *pArray = 0;
HRESULT hr = g_pObjRef->GetPrimes(nMin, nMax, &pArray);
assert(SUCCEEDED(hr) && SafeArrayGetDim(pArray) == 1);
long *prgn = 0;
hr = SafeArrayAccessData(pArray, (void**)&prgn);
long iUBound, iLBound, result = 0;
SafeArrayGetUBound(pArray, 1, &iUBound);
SafeArrayGetLBound(pArray, 1, &iLBound);
for (long n = iLBound; n <= iUBound: n++)
result += prgn[n];
SafeArrayUnaccessData(pArray);
SafeArrayDestroy(pArray);
return n;
}
Отметим, что вызывающая программа ответственна за освобождение ресурсов, выделенных для SAFEARRAY-массива, возвращенного как [out]-параметр. Вызов функции SafeArrayDestroy корректно освобождает всю память и все ресурсы, удерживаемые структурой SAFEARRAY.
Управление потоками данных
Отметим, что в предыдущих примерах использования массивов, в том числе типа SAFEARRAY , вопрос о том, какое количество данных будет передано в ORPC-сообщении, решал отправитель данных. Рассмотрим следующее простое определение метода на IDL:
HRESULT Sum([in] long cElems, [in, size_is(cElems)] double *prgd, [out, retval] double *pResult);
Если бы вызывающая программа должна была запустить этот метод следующим образом:
double rgd[1024 * 1024 * 16];
HRESULT hr = p->Sum(sizeof(rgd)/sizeof(*rgd), rgd);
то размер результирующего ответного сообщения ORPC-запроса был бы не меньше 128 Мбайт. Хотя лежащий в основе RPC-протокол вполне в состоянии разбивать большие сообщения на несколько сетевых пакетов, при использовании больших массивов все же возникают некоторые проблемы. Одна очевидная проблема состоит в том, что вызывающая программа должна иметь свыше 128 Мбайт доступной памяти сверх той, которая занята существующим массивом. Дублирующий буфер необходим интерфейсному заместителю для создания ответного ORPC-сообщения, в которое в конечном счете будет скопирован этот массив. Подобная проблема заключается в том, что процесс объекта также должен иметь больше 128 Мбайт доступной памяти для реконструирования полученных RPC-пакетов в единое сообщение ORPC. Если бы массив использовал атрибут [length_is], то следовало бы выделить еще 128 Мбайт, чтобы скопировать этот массив в память для передачи его методу. Эта проблема относится к параметрам как типа [in], так и [out]. В любом случае отправитель массива может иметь достаточное буферное пространство для создания OPRC-сообщения, а получатель массива – нет. Данная проблема является результатом того, что получатели не имеют механизма для управления потоками на уровне приложений.
Более сложная проблема с приведенным выше определением метода связана со временем ожидания (latency). Семантика ORPC-запроса требует, чтобы на уровне RPC/ORPC полное ORPC-сообшение реконструировалось до вызова метода объекта. Это означает, что объект не может начать обработку имеющихся данных, пока не получен последний пакет. Когда общее время передачи большого массива довольно велико, объект будет оставаться незанятым в течение значительного промежутка времени, ожидая получения последнего пакета. Возможно, что в течение этого времени ожидания многие элементы уже успешно прибыли в адресное пространство объекта; тем не менее, семантика вызова метода в СОМ требует, чтобы к началу текущего вызова присутствовали все элементы. Та же проблема возникает, когда массивы передаются как параметры с атрибутом [out], так как клиент не может начать обработку тех частичных результатов операции, которые, возможно, уже получены к этому моменту.
Для решения проблем, связанных с передачей больших массивов в качестве параметров метода, в СОМ имеется стандартная идиома разработки интерфейсов, позволяющая получателю данных явно осуществлять управление потоками элементов массива. Эта идиома основана на передаче вместо фактических массивов специального интерфейсного указателя СОМ. Этот специальный интерфейсный указатель, называемый нумератором (enumerator), позволяет извлекать элементы из отправителя со скоростью, подходящей для получателя. Чтобы применить эту идиому к приведенному выше определению метода, понадобится следующее определение интерфейса:
interface IEnumDouble : Unknown {
// pull a chunk of elements from the sender
// извлекаем порцию данных из отправителя
HRESULT Next([in] ULONG cElems, [out, size_is(cElems), length_is(*pcFetched)] double *prgElems, [out] ULONG *pcFetched);
// advance cursor past cElems elements
// переставляем курсор после элементов cElems
HRESULT Skip([in] cElems);
// reset cursor to first element
// возвращаем курсор на первый элемент
HRESULT Reset(void);
// duplicate enumerator's current cursor
// копируем текущий курсор нумератора
- Аквариум. (Новое издание, исправленное и переработанное) - Виктор Суворов (Резун) - Шпионский детектив
- Права на результаты интеллектуальной деятельности и средства индивидуализации: Комментарий к части четвертой Гражданского кодекса Российской Федерации - Вадим Погуляев - Юриспруденция
- У меня есть идея! Что дальше? - Михаил Соболев - О бизнесе популярно
- Ориентирование - К. М. Станич - Современные любовные романы
- Проект 365 - Константин Рочев - Поэзия