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

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

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

Шрифт:

-
+

Интервал:

-
+

Закладка:

Сделать
1 ... 76 77 78 79 80 81 82 83 84 ... 95
Перейти на страницу:

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»));

pFoo->Method27(wsz);

// .. process updated string

// .. обрабатываем обновленную строку

}

то длина массива, размещенного на стороне сервера, будет вычислена, исходя из длины входной строки (эта длина равна шести с учетом заключительного нулевого символа). Рассмотрим следующую реализацию метода со стороны сервера:

HRESULT CFoo::Method27(OLECHAR *wsz)

{

DisplayString(wsz);

// wsz only can hold 6 characters!

// wsz может хранить только 6 символов!

wcscpy(wsz, OLESTR(«Goodbye»));

return S_OK;

}

Поскольку соответствие массива основывалось на величине wcslen(OLESTR(«Hello»)+1), то, когда реализация метода перезапишет в данную строку что-то более длинное, «хвост» этой строки перезапишет случайное число байтов памяти, что приведет к неисправимым ошибкам (будем надеяться, еще до выпуска данной программы в свет). Это означает, что, хотя вызывающая программа и имела достаточно памяти, заранее выделенной для записи результирующей строки, уровень маршалинга со стороны сервера не знал об этой кажущейся внешней памяти и выделил место, достаточное для хранения только шести символов строки Unicode. Код на IDL должен был быть таким:

HRESULT Method28([in] long cchMax, [in, out, string, size_is(cchMax)] OLECHAR *wsz);

а вызывающая программа могла бы использовать это так:

void f(IFoo *pFoo)

{

OLECHAR wsz[1024];

wcscpy(wsz, OLESTR(«Hello»));

pFoo->Method28(1024, wsz);

// .. process updated string

// .. обрабатываем обновленную строку

}

Наиболее неприятным аспектом примера c [in, out, string] является то, что он прекрасно работает, когда входная строка имеет по крайней мере такую же длину, как выходная строка. Ошибки, связанные с этим методом, будут периодическими и могут ни разу не возникнуть на стадии тестирования проекта.

В большинстве обычных API-функций, когда функция возвращает в вызывающую программу данные переменной длины, вызывающая программа заранее выделяет буфер для хранения результатов функции, а реализация функции заполняет буфер, заготовленный вызывающей программой. Ответственность за задание правильного размера буфера лежит на вызывающей программе. При использовании заданных вызывающей программой буферов для возвращения структур данных переменной длины (таких, как строки) может возникнуть проблема. Возможно, реализация метода захочет возвратить больше данных, чем ожидает вызывающая программа. Рассмотрим следующий код Windows SDK, который отображает текст редактирующего управляющего элемента, то есть текстового окна, позволяющего набирать и редактировать текст:

void Show(HWND hwndEdit)

{

TCHAR sz[1024];

GetWindowText(hwndEdit, sz, 1024);

MessageBox(0, sz, _TEXT(«Hi!»), MB_OK);

}

Заметим, что разработчик Show полагает, что редактирующий управляющий элемент никогда не будет содержать больше 1024 символов. Каким образом он или она узнали об этом? Самым точным образом. Можно было бы подумать, что такая реализация была бы надежнее:

void Show(HWND hwndEdit)

{

int cch = GetWindowTextLength(hwndEdit);

TCHAR *psz = new TCHAR[cch+1];

GetWindowText(hwndEdit, psz, cch);

MessageBox(0, sz, _TEXT(«Hi!»), MB_OK);

delete[] psz;

}

но как в данном примере вызывающая программа может быть уверена, что пользователь не напечатает еще символ после вызова GetWindowTextLength, но до вызова GetWindowText? Тот факт, что размещение основано на потенциально устаревшей информации, делает данную идиому чувствительной к условиям гонки.

Предшествующие идиомы программирования, возможно, и годятся для HWND, но совершенно неприменимы для объектов СОМ. В отличие от HWND, к объектам СОМ весьма вероятен одновременный доступ со стороны многих участников. Кроме того, стоимость двух вызовов метода для выполнения одной операции, как показано выше, очень быстро уменьшила бы производительность, особенно в распределенной среде, где задержка, вызванная передачей и приемом пакетов информации, создает огромные проблемы при циклических вызовах метода. В силу этих двух факторов при передаче типов данных с переменной длиной из реализации метода в вызывающую программу через [out]-параметр правильно организованный интерфейс СОМ предписывает реализации метода выделить пространство для результата, используя СОМ-распределитель памяти задачи. Это необходимо, поскольку фактический размер результата может быть известен только внутри реализации метода. Этот динамически выделенный буфер возвращается программе, вызвавшей метод, и после того, как буфер уже не нужен, вызывающая программа должна освободить этот буфер распределителем памяти задачи в вызываемом процессе. Чтобы выразить эту идиому для строкового параметра, приведем следующий корректно работающий код IDL:

HRESULT Method29([out, string] OLECHAR **ppwsz);

из которого следует такая реализация со стороны сервера:

HRESULT CFoo::Method29(OLECHAR **ppwsz)

{

const OLECHAR wsz[] = OLESTR(«Goodbye»);

int cb = (wcslen(wsz) + 1) * sizeof(OLECHAR);

*ppwsz = (OLECHAR*)CoTaskMemAlloc(cb);

if (*ppwsz == 0) return E_OUTOFMEMORY;

wcscpy(*ppwsz, wsz);

return S_OK;

}

Для правильного использования этого метода необходим такой код со стороны клиента:

void f(IFoo *pFoo)

{

OLECHAR *pwsz = 0;

if SUCCEEDED(pFoo->Method29(&pwsz)) {

DisplayString(pwsz);

CoTaskMemFree(pwsz);

}

}

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

Синтаксис массива, приведенный в этом разделе, является совершенно разумным для программистов на С и C++. К сожалению, в то время, когда пишется этот текст, Visual Basic не способен работать ни с какими массивами переменной длины и может воспринимать только массивы фиксированной длины. Для того чтобы позволить Visual Basic посылать и получать массивы переменной длины, файлы СОМ IDL определяют среди прочих составной тип, именуемый SAFEARRAY. SAFEARRAY – это довольно редко используемая структура данных, которая позволяет передавать в качестве параметров многомерные массивы, совместимые с типом VARIANT. Для определения размеров массива SAFEARRAY в СОМ предусмотрен тип данных SAFEARRAYBOUND:

typedef struct tagSAFEARRAYBOUND {

ULONG cElements;

// size_is for dimension

// size_is для размерности

LONG lLbound;

// min index for dimension (usually 0)

// минимальный индекс для размерности (обычно 0)

} SAFEARRAYBOUND;

Тип данных SAFEARRAY внутри использует совместимый массив типа SAFEARRAYBOUND, чтобы придать некоторую форму содержимому массива:

typedef struct tagSAFEARRAY {

USHORT cDims;

// # of dimensions

// число измерений

USHORT fFeatures;

// flags describing contents

// флаги, описывающие содержимое

ULONG cbElements;

// # of bytes per element

// число байтов на элемент

ULONG cLocks;

// used to track memory usage

// применяется для слежения за использованием памяти

void* pvData;

// actual elements

// фактические элементы

[size_is(cDims)] SAFEARRAYBOUND rgsabound[]

} SAFEARRAY;

Приведенный выше IDL в действительности не используется для описания сетевого формата массивов SAFEARRAY, однако он используется для их программного описания.

Чтобы обеспечить пользователю максимальную гибкость в вопросах управления памятью, в СОМ определены следующие флаги, которые могут использоваться с полем fFeatures:

FADF_AUTO

/* array is allocated on the stack */

/* массив размещен в стеке */

FADF_STATIC

/* array is statically allocated */

/* массив размещен статически */

FADF_EMBEDDEO

/* array is embedded in a structure */

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

1 ... 76 77 78 79 80 81 82 83 84 ... 95
Перейти на страницу:
Тут вы можете бесплатно читать книгу Сущность технологии СОМ. Библиотека программиста - Дональд Бокс.
Комментарии