Сущность технологии СОМ. Библиотека программиста
Шрифт:
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),
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, но совершенно
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
// число измерений