Сущность технологии СОМ. Библиотека программиста
Шрифт:
HRESULT Method17([in] long cMax,
[in, out] long *pcActual,
[in, out, size_is(cMax), length_is(*pcActual)] short *rgs);
что предполагает следующий код на стороне клиента:
void f(IFoo *pFoo)
{
short rgs[8];
rgs[0] = 0; rgs[1] = 1;
long cActual = 2;
pFoo->Method17(8, &cActual, rgs);
// .. process first cActual elements of rgs
// .. обрабатываем первые cActual элементов из массива rgs
}
Если число элементов на входе и на выходе одно и то же, то подойдет
HRESULT Method18([in] long cElems,
[in, out, size_is(cElems)] short *rgs);
Данный метод использует эффективность совместимого массива, и его гораздо проще использовать.
Приведенные выше примеры оперировали с одномерными массивами. Рассмотрим следующий прототип на С:
void g(short **arg1);
Этот прототип может означать в С все, что угодно. Возможно, функция ожидает указатель на одно короткое целое число:
void g(short **arg1) {
// return ptr to static
// возвращаем указатель на static
static short s;
*arg1 = &s;
}
Или, возможно, функция ожидает массив из 100 коротких указателей:
void g(short **arg1)
{
// square 100 shorts by ref
// квадрат из 100 коротких целых указателей
for (int n = 0; n < 100; n++)
*(arg1[n]) *= *(arg1[n]);
}
А также, возможно, функция ожидает указатель на указатель на массив коротких целых:
void g(short **arg1)
{
// square 100 shorts
// квадрат из 100 коротких целых
for (int n = 0; n < 100; n++)
(*arg1)[n] *= (*arg1)[n];
}
Этот синтаксический кошмар разрешается в IDL использованием такого синтаксиса, который часто побуждает пользователей-новичков бежать за утешением к документации.
Атрибуты IDL [size_is] и [lengtn_is] принимают переменное количество разделенных запятой аргументов, по одному на каждый уровень косвенности. Если параметр пропущен, то считается, что соответствующий уровень косвенности является указателем на экземпляр, а не на массив. Для того чтобы показать, что параметр является указателем на указатель на одиночный экземпляр, не требуется более никаких атрибутов:
HRESULT Method19([in] short **pps);
что означает такое расположение в памяти:
pps -> *pps-> **pps
Для того чтобы показать, что параметр является указателем на массив указателей на экземпляры, нужно написать следующий код IDL:
HRESULT Method20([in, size_is(3)] short **rgps);
что в памяти будет выглядеть примерно так:
rgps -> rgps[0] -> *rgps[0]
rgps[1] -> *rgps[1]
rgps[2] -> *rgps[2]
Для того чтобы показать, что параметр является указателем на указатель на массив экземпляров, следует написать такой код на IDL:
HRESULT Method21([in, size_is(,4)] short **pprgs);
что в памяти будет выглядеть следующим образом:
pprgs -> pprgs -> (pprgs)[0]
(pprgs)[1]
(pprgs)[2]
(pprgs)[3]
Для
HRESULT Method22([in, size_is(3,4)] short **rgrgs);
что в памяти будет выглядеть примерно так:
rgrgs -> rgrgs[0] -> rgrgs[0][0]
rgrgs[0][1]
rgrgs[0][2]
rgrgs[0][3]
rgrgs[1] -> rgrgs[1][0]
rgrgs[1][1]
rgrgs[1][2]
rgrgs[1][3]
rgrgs[2] -> rgrgs[2][0]
rgrgs[2][1]
rgrgs[2][2]
rgrgs[2][3]
Данный синтаксис, быть может, оставляет желать лучшего, тем не менее, он обладает большей гибкостью и меньшей неоднозначностью, чем на С.
Важно отметить, что приведенный выше метод IDL задает многомерный массив; формально он представляет собой массив указателей на массив указателей на экземпляры. Это не то же самое, что многомерный массив в языке С, который может быть определен в IDL с использованием стандартного синтаксиса С:
HRESULT Method23([in] short rgrgs[3][4]);
Данный синтаксис предполагает, что все элементы массива будут размещены в памяти непрерывно, что определенно не совпадает с предположением предыдущего примера.
Допускается задавать первое измерение многомерного массива с помощью атрибута [size_is]:
HRESULT Method24([in, size_is(3)] short rgrgs[][4]);
однако нельзя задавать никакого иного измерения, кроме крайнего левого.
Выражения, использованные атрибутами [size_is], [length_is] и другими атрибутами задания размерности массива, не могут быть размещены в вызовах функций. При этом, например, стал бы затруднительным маршалинг строк, соответствие и/или переменная длина которых размещены в вызовах функций wcslen или strlen. Это означает, что такой код в IDL является недопустимым:
HRESULT Method24([in, size_is(wcslen(wsz) + 1)] const OLECHAR *wsz);
Поскольку это ограничение сделало бы использование строк чрезвычайно неудобным для клиентских программ, IDL поддерживает строковый атрибут, который требует на уровне маршалинга вызывать соответствующую функцию xxxlen для вычисления соответствия массива. Ниже приведено правильное задание строки в качестве входного параметра:
HRESULT Method25([in, string] const OLECHAR *wsz);
или:
HRESULT Method26([in, string] const OLECHAR wsz[]);
При использовании строк в качестве выходных или входных/выходных параметров почти всегда целесообразно явно задавать длину буфера вызывающей программы, для гарантии того, что он будет достаточно большим на стороне сервера. Рассмотрим следующий полный ошибок код IDL:
HRESULT Method27([in, out, string] OLECHAR *pwsz);
Если вызывающая программа запускает этот метод с помощью достаточно короткой строки:
void f(IFoo *pFoo)
{
OLECHAR wsz[1024];
wcscpy(wsz, OLESTR(«Hello»));