Delphi 3. Библиотека программиста

       

Интерфейс IDataObject хранит данные


Интерфейс IDataObject управляет содержанием перетаскиваемых данных, а также представлением их в формате, понятном для запрашивающего объекта IDropTarget. Он используется при перетаскивании, а также при обмене данными с буфером (clipboard). После того как вы наладите работу интерфейса IDataObject с первым типом передачи данных, со вторым особых проблем не возникнет. Впрочем, трудность (как правило) заключается в том, чтобы заставить IDataObject работать хотя бы в одном варианте. И что еще хуже, возникающие проблемы оказываются на редкость изощренными.

Интерфейс IDataObject предоставляет средства для передачи данных и сообщений об изменениях. Его методы предназначены для занесения данных в объект, представления их в различных (как правило, зависящих от конкретного устройства) форматах, возврата информации о поддерживаемых форматах и уведомления других объектов об изменении данных. Хотя для перетаскивания файлов в окно Windows Explorer или File Manager необходимо полностью реализовать лишь три метода IDataObject, в совокупности эти три метода оказываются весьма объемными.

Итак, чтобы создать сервер для перетаскивания файлов, нужно реализовать три метода IDataObject: QueryGetData, GetData и EnumFormatEtc. А чтобы реализовать метод EnumFormatEtc, понадобится реализовать и интерфейс IEnumFormatEtc. Я же говорил, что с реализацией интерфейсов все стремительно усложняется.

Метод QueryGetData вызывается приемником перетаскивания. Ему передается структура TFormatEtc, которая описывает формат данных, желательный для приемника. QueryGetData должен сообщить приемнику о том, может ли объект представить данные в требуемом формате. Он возвращает S_OK в том случае, если последующий вызов GetData с большой долей вероятности закончится успешно. В некоторых случаях (например, при нехватке памяти) последующий вызов GetData все равно может закончиться неудачей.

Когда приемник хочет получить данные, он вызывает метод GetData. Приемник передает структуру TFormatEtc с описанием желательного формата данных и структуру TStgMedium, в которую GetData поместит запрашиваемые данные. Вызывающая сторона (то есть приемник) должна освободить структуру TStgMedium после того, как обработка данных будет завершена. Этот момент чрезвычайно важен. Поскольку клиент уничтожает данные, возвращаемые GetData, метод должен передавать копию данных объекта. Если GetData передаст настоящие данные, клиент благополучно уничтожит их, и следующая попытка клиента (или самого объекта-источника) обратиться к данным приведет к катастрофе.

Метод EnumFormatEtc сообщает о том, в каких форматах объект может воспроизвести свои данные. Информация передается в виде объекта IEnum FormatEtc, а это означает, что для реализации IDataObject нам придется реализовать и интерфейс IEnumFormatEtc. Среди примеров OLE SDK приведено немало реализаций IEnumFormatEtc— но все они написаны на C или C++ и выглядят, мягко говоря, устрашающе. К счастью, классы TOleForm и TOleContainer из OLECNTNRS.PAS содержат более простой вариант, которым я воспользовался как шаблоном для своей реализации. После этого примеры IEnumFormatEtc из OLE SDK начали обретать для меня смысл, но без OLECTRNS.PAS я бы до сих пор рвал на себе волосы от отчаяния.



Интерфейс IEnumFormatEtc содержит четыре метода: Next, Skip, Reset и Clone, с помощью которых приложения могут перебирать и просматривать поддержи ваемые форматы данных, а также копировать список этих форматов. Универсальная реализация IEnumFormatEtc выглядит очень сложно, поскольку она должна уметь динамически выделять память под структуры TFormatEtc и копировать внутренние данные, содержащиеся в этих структурах. Такие сложности нам не нужны, поэтому предполагается, что рабочий массив TFormatEtc содержит статические данные. Для наших целей сойдет и так, но во многих приложениях это условие приведет к излишне строгим ограничениям. Предлагае мая реализация IEnumFormatEtc приведена в листинге 4.3.

Листинг 4.3. ENUMFMT.PAS: простейшая реализация интерфейса IEnumFormatEtc

{

ENUMFMT.PAS -- реализация интерфейса IEnumFormatEtc.

Автор: Джим Мишель

Дата последней редакции: 30/05/97

Приведенная реализация IEnumFormatEtc недостаточно надежна.

Она предполагает, что список FormatList, поддерживаемый объектом

TEnumFormatEtc, хранится в виде статического массива. Для простых

объектов наподобие сервера для перетаскивания файлов этого достаточно, но во многих

приложениях такое ограничение оказывается неприемлемым.

} unit EnumFmt; interface uses Windows, ActiveX; type { TFormatList -- массив записей TFormatEtc } PFormatList = ^TFormatList; TFormatList = array[0..1] of TFormatEtc; TEnumFormatEtc = class (TInterfacedObject, IEnumFormatEtc) private FFormatList: PFormatList; FFormatCount: Integer; FIndex: Integer; public constructor Create (FormatList: PFormatList; FormatCount, Index: Integer); { IEnumFormatEtc } function Next (celt: Longint; out elt; pceltFetched: PLongint): HResult; stdcall; function Skip (celt: Longint) : HResult; stdcall; function Reset : HResult; stdcall; function Clone (out enum : IEnumFormatEtc) : HResult; stdcall; end; implementation constructor TEnumFormatEtc.Create ( FormatList: PFormatList; FormatCount, Index : Integer ); begin inherited Create; FFormatList := FormatList; FFormatCount := FormatCount; FIndex := Index; end; { Next извлекает заданное количество структур TFormatEtc в передаваемый массив elt. Извлекается celt элементов, начиная с текущей позиции в списке. } function TEnumFormatEtc.Next ( celt: Longint; out elt; pceltFetched: PLongint ): HResult; var i : Integer; eltout : TFormatList absolute elt; begin i := 0; while (i < celt) and (FIndex < FFormatCount) do begin eltout[i] := FFormatList[FIndex]; Inc (FIndex); Inc (i); end; if (pceltFetched <> nil) then pceltFetched^ := i; if (I = celt) then Result := S_OK else Result := S_FALSE; end; { Skip пропускает celt элементов списка, устанавливая текущую позицию на (CurrentPointer + celt) или на конец списка в случае переполнения. } function TEnumFormatEtc.Skip ( celt: Longint ): HResult; begin if (celt <= FFormatCount - FIndex) then begin FIndex := FIndex + celt; Result := S_OK; end else begin FIndex := FFormatCount; Result := S_FALSE; end; end; { Reset устанавливает указатель текущей позиции на начало списка } function TEnumFormatEtc.Reset: HResult; begin FIndex := 0; Result := S_OK; end; { Clone копирует список структур } function TEnumFormatEtc.Clone ( out enum: IEnumFormatEtc ): HResult; begin enum := TEnumFormatEtc.Create (FFormatList, FFormatCount, FIndex); Result := S_OK; end; end.

Содержание раздела