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

       

Как работает программа


В листинге 4.1 реализовано сразу два класса. Первый, TDragDropInfo, наверное, покажется вам знакомым по предыдущей главе. Я немного подправил его, потому что для источника требуются кое-какие дополнительные возможности, но в общем он остался тем же объектом, знакомым по примеру с FMDD.

Другой класс, TFileDropTarget, реализует интерфейс IDropTarget. Определение этого класса выглядит так:

TFileDropTarget = class (TInterfacedObject, IDropTarget)

Если вы по уши влюблены в C++, не спешите торжествовать. Если же вы полагаете, что множественное наследование изобрел сам дьявол для искушения начинающих программистов, не торопитесь убегать с воплями ужаса. То, что вы здесь видите, не является множественным наследованием. Этот странный фрагмент говорит: «TFileDropTarget является потомком TInterfaced Object и реализует интерфейс IDropTarget». Один класс действительно может реализовывать несколько интерфейсов, но ситуация не имеет ничего общего со множественным наследованием.

В файле ACTIVEX.PAS, находящемся в каталоге Delphi Source\RTL\WIN, содержится следующее объявление интерфейса IDropTarget:

IDropTarget = interface(IUnknown)

['{00000122-0000-0000-C000-000000000046}'] function DragEnter(const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HResult; stdcall; function DragOver(grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HResult; stdcall; function DragLeave: HResult; stdcall; function Drop(const dataObj: IDataObject; grfKeyState: Longint; pt: TPoint; var dwEffect: Longint): HResult; stdcall; end;

Первая строка лишь сообщает о том, что интерфейс IDropTarget является наследником IUnknown. Следующая строка определяет глобально-уникальный идентификатор интерфейса (Globally Unique Identifier, GUID). GUID представляет собой 128-битное число, уникальное для каждого типа объекта. Фирма Microsoft назначила GUID всем стандартным интерфейсам OLE. Существуют программы (и даже функция API), генерирующие новые GUID. С точки зрения статистики крайне маловероятно, чтобы два сгенерированных GUID совпали. В любом случае для использования готовых интерфейсов OLE вовсе не обязательно разбираться в механике GUID, но если вы собираетесь создавать собственные интерфейсы, обязательно научитесь генерировать GUID и работать с ними в программах.

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

Следовательно, интерфейсы чем-то похожи на классы — они тоже описывают поведение объектов. Но в отличие от классов интерфейсы не имеют категорий доступа (private, public, protected и т. д.) и не объявляют переменных или свойств. Кроме того (и опять же в отличие от классов), интерфейсы не имеют обязательных реализаций. Ни в ACTIVEX.PAS, ни в каком другом месте вы не найдете такой строки:

function IDropTarget.DragLeave : HResult;

Во всем листинге 4.1 заслуживает внимания лишь одна часть приемника — метод TFileDropTarget.Drop, вызываемый OLE при сбрасывании файлов пользователем. Эта функция должна получить данные от объекта и передать их окну. Передача происходит в процедуре события FOnFilesDropped, вызываемой Drop после получения данных. Эта функция и принцип ее действия очень напоминают TFMDDEvent из предыдущей главы.

С другой стороны, с получением данных дело обстоит несколько сложнее.

Чтобы получить перетаскиваемые данные, Drop заполняет структуру TFormatETC, которая описывает представление данных, и передает ее вместе со структурой TStgMedium методу GetData объекта данных. GetData форматирует данные в соответствии с содержимым структуры TFormatETC и возвращает их в структуре TStgMedium. Затем Drop может работать с данными, что в нашем случае означает создание структуры TDragDropInfo. Когда метод Drop завершает обработку данных, он должен освободить структуру TStgMedium. Последний момент чрезвычайно важен — особенно если вы занимаетесь реализацией источника. За освобождение данных отвечает клиент , то есть приемник. Это означает, что реализация GetData из объекта данных должна предоставить копию данных, а не сами данные. Возможно, сейчас это кажется вам очевидным. Мне это тоже кажется очевидным… после того, как я потратил почти два дня на отладку программы!

Как ни странно, приведенная реализация приемника оказалась проще, чем вариант из главы 3. Видимо, мы нередко склонны преувеличивать сложность задач. И все же признаюсь, что на освоение COM и TInterfacedObject у меня ушло немало времени — намного больше, чем на обработку WM_DROPFILES.



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