IZONE- http://www.izcity.com/- бесплатный софт, вэб-сервисы, ресурсы для раскрутки, свежие номера журнала "Internet Zone".

UDT (user defined types) & Automation. Часть 2

Из опыта разработки универсальной Automation-молотилки. Часть 2: переработка "анонимных" структур.

Юрий Кулешов

В предыдущей статье было рассказано о поддержке UDT на уровне COM, показано, как строить такие структуры, давать им описания, получать эти описания и формировать векторы из UDT.

«Анонимные» UDT

Анонимность в контексте этого раздела — вещь условная, поскольку предполагается, что структура неизвестна лишь в момент компиляции, в то время, как в момент исполнения пользователю доступно её описание.

Как показано в первой части этой статьи, после того, как библиотека типов зарегистрирована, можно легко получить описание всех находящихся в ней элементов, не исключая и структур, которыми являются UDT. В то же время над UDT, с которым должен работать сервер, довлеют следующие ограничения, отражённые на рис 1.

1
Рисунок 1. Ограничения доступности информации о типе времени исполнения и времени компиляции.

Поскольку клиент в данной нотации часто сам является инициатором создания UDT, то ему не составляет никакого труда пользоваться определённым в tlb типом, как обычной структурой, со всеми вытекающими. С другой стороны, сервер ничего не знает о том, с какой информацией ему придётся работать, полагаясь либо на сырое («бинарное») чтение в хранилища вроде PVOID buffer, либо на общие механизмы, предоставляемые COM. В первом случае, особенно, если у сервера нет никакой возможности «понять», с чем он работает, структура анонимна по-настоящему: нет возможности даже узнать её структуру. Ясно, что это бесполезный подход, годящийся лишь для тупого копирования больших объёмов данных или их массированного сохранения (где-то), как это большей частью и было до появления NT4 SP4. Совсем другое дело, когда сервер может спросить у SAFEARRAY, что именно тот в себе содержит. Процедура «опроса» проста:

HRESULT Server::Method(/*[in] SAFEARRAY * Vector */)
{
IRecordInfo ri = 0;

HRESULT hr = S_OK;

if FAILED(hr = SafeArrayGetRecordInfo(Vector, &ri))
return hr;

// работа с вектором
return hr;
}

Как видно из приведённого фрагмента кода, получить описание элементов SAFEARRAY можно в один вызов. Функция SafeArrayGetRecordInfo возвращает S_OK в том случае, если удалось успешно получить описание элемента, и E_INVALIDARG, если переданный ей SAFEARRAY не содержит UDT. Получив интерфейс IRecordInfo, уже легко отобразить данные «сырого» буфера, получаемого из вызова SafeArrayAccessData, на «нормальные», Automation-compatible поля структуры, которая и подразумевается в клиенте. Идея «произвольности» доступа, в совокупности с информацией о типе, предоставляемой интерфейсом IRecordInfo, обеспечивает возможность проводить осмысленное чтение данных их бинарного буфера.

Суть этого подхода в том, что программисту всегда известны все фундаментальные типы и, как следствие, их длины. Организуя произвольное чтение сырых данных и используя при этом в качестве путеводителя по структуре IRecordInfo, можно получать любые фрагменты упакованной в плоский байтовый вектор структуры, как это показано в первой части. Таким образом, алгоритм работы сервера с данными, находящимися в SAFEARRAY и полученными им от клиента, имеет следующий вид:

Callback

Осталось только остановиться на том, для чего нужны все эти пляски с бубнами. Задача, из которой родилась данная статья, была поставлена так: организовать фильтрование произвольных данных с тем, чтобы уменьшить для пользователя число записей, которые необходимо просмотреть. Обычная задача, многократно решённая. Простая хотя бы уже потому, что ADODB.Recordset имеет свойство Filter, позволяющее проводить любые фильтрования (фильтрирования?) хранящихся в Recordset данных. Всё выглядит и просто и очевидно. Но только до тех пор, пока не требуется отфильтровать нечто, что имеет неизвестную структуру.

2
Рисунок 2. Алгоритм "осмысленного копирования" данных из буфера, предоставляемого SAFEARRAY.

В самом деле, если структура известна компоненту фильтрования, то не составляет никаких проблем создать в Recordset поля, которые в точности соответствовали бы тем, что имеются в описании этой структуры, затем, последовательно скопировав данные из некой записи, описания полей которой нам (повторюсь) известны, заполнить Recordset, установить свойство Filter в то значение, которое было передано клиентом и замечательным образом отправить отфильтрованные данные обратно.

В том же случае, когда структура неизвестна, остаётся полагаться только на то, что есть некая возможность передать информацию о типе от одного компонента другому в т.ч., возможно, и за пределы процесса. UDT здесь подходят как нельзя лучше. Они могут легко создаваться по одному лишь идентификатору (GUID), у них нет проблем с маршаллингом (с некоторых пор) и если они хранятся в SAFEARRAY, то SafeArrayGetRecordInfo всегда и с удовольствием скажет любому своему пользователю данные какой структуры этот массив хранит. Таким образом, выбор очевиден.

Тем не менее, есть некоторые сложности в реализации, умолчать которые было бы не совсем честно… Построенная на изложенных принципах система с блеском справляется с любыми Automation-совместимыми типами элементов структур, но при этом будет, очевидно не в состоянии работать с типами вроде ULONG. Это не страшно, но неприятно. Другой проблемой, не столь глубинной, является то, что в изложенной схеме для правильного функционирования всей системы в целом, необходимо регистрировать библиотеку типов, содержащую описание UDT. Это не очень красивый подход, поскольку нарушает одно из правил композиции: переводит библиотеку типов из категории желательных элементов в обязательные. В настоящее время я работаю над тем, чтобы избавиться от этой проблемы. Ну а пока в приложении к этой статье идёт маленькая программа regtlb32, являющаяся альтернативой regtlib и совсем неплохо выполняющая работу по регистрации библиотек типов, и могущая при этом быть использованной в пакетной регистрации библиотек.

Как и обычно, откликов, соображений и всего прочего, на что способно подвигнуть прочтение этой статьи, я жду по своеобычному адресу: yk@rbcmail.ru.

Источник - SoftТерра, http://www.softerra.ru

 


Copyright © "Internet Zone", http://www.izcity.com/, info@izcity.com