Библиотека Интернет Индустрии I2R.ru |
|||
|
UDT (user defined types) & Automation. Часть 1ВведениеUDT являются полезным средством в тех случаях, когда необходимо сгруппировать части информации в единое целое. В C++ для UDT используется ключевое слово struct, в Visual Basic - Type, а, скажем, в Pascal - record. Такое группирование может быть полезным в целом ряде практических задач. Каждый из современных языков умеет обращаться со структурами весьма удобным и надёжным образом, а понимании концепции структуры нет ничего сложного. Однако не всё так просто обстоит в случаях, когда структурой необходимо воспользоваться, не зная заранее ничего о самой структуре. Описанию решения этой проблемы и посвящена эта статья. ЗадачаБольшинство публикуемых мной статей возникло <по мотивам> решённых мной актуальных задач современного программирования. Возможно, это несколько нескромно - заявлять об этом, но я стараюсь решать возникающие проблемы максимально прогрессивными средствами (из-за чего бывает, встречается непонимание некоторыми из заказчиков того факта, что старое - оно и есть старое: совсем недавно от меня потребовали использовать в качестве СУБД Sybase SQL Anywhere 5.0, в то время, как я предлагал MS SQL Server 2000) хотя бы из-за того, что не хочется тратить время на устаревшие технологии. Впрочем, каждому - своё. Итак, задача заключается в следующем: требуется создать компонент, который смог бы правильно организовать доступ к элементам структуры, вид которой станет известен только во время исполнения. Потребуется разобрать вид этой структуры и далее считать данные из неё и поместить в disconnected ADO-recordset. Другим отягчающим условием задачи является то, что язык, на котором будет реализована эта структура, также неизвестен и единственное разумное предположение, которое можно сделать на момент написания компонента - это то, что клиент может быть (но не обязан) написан на Visual Basic а механизм передачи структур должен быть Automation-совместимым. Таким образом, задача сводится к следующему: нечто, являющееся структурой и переданное в компонент извне, необходимо правильно идентифицировать и забрать из него данные. АнализОчевидно, что решить эту задачу можно лишь имея возможность передать вместе со структурой некое её описание. Также понятно, что описание это должно быть настолько стандартным, насколько это вообще возможно, поскольку возможна масса сценариев использования этого компонента (рис 1).
Как известно, в COM присутствует стандартный механизм описания типов. Он так и называется: библиотека типов (имеет, как правило, расширение .tlb). Имеется богатый выбор интерфейсов, позволяющих выполнять различные запросы относительно типов, хранящихся в библиотеке, словом, как получить описание, понятно. Не менее понятно, как это описание создать: в случае использования VC++ для написания <создателя> структуры (см. рисунок), достаточно приблизительно следующего .idl-файла, описывающего эту структуру:
Таблица 1. idl-файл, содержащий необходимую структуру (StructTest1) Обратите внимание на uuid, идентифицирующий структуру: не всякий midl-компилятор сможет правильно обработать этот атрибут. В частности, тот midl, который идёт в стандартной поставке Visual Studio с этой задачей не справляется, поэтому возьмите последнюю версию midl из Platform SDK. Итак, к этому моменту известно, как идентифицировать структуру, храняющуюся в tlb: c помощью uuid, имеющего в данном случае <номер> . Разберёмся теперь, как использовать этот идентификатор: Начиная с NT 4 SP4 (приблизительно декабрь 1998 года), DCOM расширен возможностью маршаллинга определённых пользователем типов (UDT) и, как следствие, расширением COM API (для устаревших систем Windows 95 потребуется DCOM 1.2). Среди новых возможностей появилась и такая, как создание SAFEARRAY по описателю структуры при помощи интерфейса IRecordInfo, а в VARTYPE был включён элемент VT_RECORD, являющийся индикатором (какого-то) пользовательского типа. Таким образом, DCOM предоставляет все средства для того, чтобы дать возможность серверу добавить описание структуры, а клиенту - это описание правильно понять. CheckpointДля определения COM UDT в <до-.NET> - эпоху необходимо:
РешениеЗадача заключается не просто в том, чтобы получить данные из какой-то определённой структуры, а в том, чтобы получить эти данные из структуры с заранее неизвестными полями, поэтому кодируя компонент, которые будет работать с этой структурой, необходимо принять максимальные меры предосторожности. Для использования UDT в
клиенте, написанном на VC++, я воспользовался директивой
#import, как наиболее простым и функциональным средством,
позволяющим в одну строку сделать всё, что надо: Обратите внимание на атрибут
inject_statement.Без него импортированный заголовок будет
начинаться так: Теперь, используя интерфейсы ITypeLib и IRecordInfo, можно организовать обработку данных. Ключевую роль в схеме работы моего компонента, работающего со структурами, неизвестными на момент компиляции, играет хорошо известный тип SAFEARRAY. Расширенный возможностью работы с UDT, программный интерфейс SAFEARRAY позволяет делать с пользовательскими структурами всё, что угодно. Следующие функции играют ключевую роль в работе с UDT:
В процессе написания также будут использованы функции обычные функции доступа SafeArrayAccess/Unaccess-Data и SafeArrayDestroy. Главное причиной, которая побудила меня использовать именно SAFEARRAY, является его замечательная способность возвращать хранимые данные в <сыром виде>. Это значит, что ничто не помешает поместить в SAFEARRAY, скажем, long, а затем обратиться к этому <лонгу>, как к четырём последовательным байтам. Идея далеко не нова, но будет в дальнейшем очень плодотворно использована. Поясню её следующим рисунком:
Что применимо к словам, то применимо и к структурам: помещаем в SAFEARRAY запись, а извлекаем последовательность байт. Дальше в ход идёт обычная арифметика указателей, с которой не возникнет никаких проблем. Итак, идея, полагаю, понятна. Рассмотрим практическое её воплощение на следующей подзадаче:
Получение описанияСледующий фрагмент кода
получает описание структуры, помещённой (кем-то) в
tlb: Здесь для простоты изложение принимается, что UUID библиотеки равен , а структуры - . К моменту написания этой статьи мне был неизвестен более простой способ кодирования UUID, чем через <подставные структуры>. Впрочем, здесь нет ничего экстраординарного, этот способ документирован и повсеместно применяется. Создание вектораЦелью вышеприведённого кода
было получить интерфейс IRecordInfo, <хранящий> описание
структуры с тем, чтобы было можно начать работу с SAFEARRAY.
Используя функцию SafeArrayCreateVectorEx теперь можно
разместить вектор импортированных из библиотеки записей. Это
делает нижеприведённый код. Этим вызовом создаётся вектор на 10 элементов-записей, описание которых <хранится> в pRecInfo - указателе на IRecordInfo. Поясню, почему я в который уже раз заключаю слово <хранится> в кавычки. Я всего лишь желаю сохранить изложение как можно более точным, и поскольку инкапсулирование в COM более, чем строгое, то говорить о хранении не приходится. Вот и всё. Работа с векторомРазместив вектор записей вызовом SafeArrayCreateVectorEx, можно теперь обращаться к его элементам в обычной манере: STRUCTURESLib::StructTest1 * t = 0; SafeArrayAccessData(sa, (PVOID*)&t); for (long a = 0; a < 10; a++) { t[a].FirstField = a + 1; t[a].SecondField = (DATE)a + 1; t[a].ThirdField = SysAllocString(L"Это просто такая строка"); t[a].FourthField = a + 1; t[a].FifthField = a + 1; t[a].SixthField = a * 2.0 + 1; t[a].SeventhField = a * 2.0 + 1; } SafeArrayUnaccessData(sa); Со структурами, описанными в библиотеке типов, можно было работать с давних пор и наверняка кто-то из читателей уже хочет спросить - так что же здесь нового? Ведь до настоящего момента переданный последним аргументом pRecInfo так нигде и не использовался, да и зачем он, если и так известны поля структуры? А что, если предположить, что структура неизвестна? Как вы поступить в этом случае? Вот здесь на помощь и приходит тот самый pRecInfo - передав его в качестве аргумента в создаваемый вектор однажды в одном компоненте, это описание может быть получено где угодно, даже в том компоненте, который ничего не знает ни о библиотеке, из которой берётся структура, ни даже о том языке, на котором эта структура была описана. Как именно должен выглядеть компонент, перерабатывающий такие <анонимные> структуры, будет показано в следующей статье. |
|
2000-2008 г. Все авторские права соблюдены. |
|