На этом шаге мы рассмотрим, как объявляются интерфейсы.
Для реализации интерфейсов в Delphi имеются эксперты, которые выполняют для программиста черновую работу по реализации интерфейсов, необходимых для работы соответствующих СОМ-объектов - COM Object, Automation Object, ActiveX Control и Property Page. С некоторыми из них мы познакомились на 2 и 3 шагах. Соответствующие эксперты вызываются при выполнении команды File | New | Other... | ActiveX, и далее производится выбор типа создаваемого объекта из репозитария объектов.
Рис.1. Репозитарий объектов
Создание интерфейса начинается с его объявления. Интерфейсы объявляются с помощью зарезервированного слова interface. Например:
type IMyData=interface(IUnknown) procedure GetMyData(var N:integer); procedure SetMyData(N:integer); end;
Данный пример объявляет новый интерфейс IMyData. К существующим методам IUnknown добавляются методы GetMyData и SetMyData. Если предком данного интерфейса является IUnknown, то в объявлении разрешается опускать имя предка - в данном случае легальна запись: IMyData=interface - без указания предка. То же самое можно сделать и при объявлении нового класса, если предком является TObject.
Если данный пример поместить в какой-либо проект, то проект может быть скомпилирован и запущен без какой-либо реализации методов GetMyData и SetMyData. Это еще раз подтверждает тот факт, что все методы интерфейсов являются абстрактными и не требуют реализации.
Выше приведенное объявление интерфейса вполне легально, если он будет использоваться внутри одного модуля. Но интерфейс, как он объявлен в данном примере, не может быть экспортирован в другие модули и из других модулей нельзя получить указатель на данный интерфейс. Причина - импорт и экспорт интерфейса осуществляется через GUID. Объявление с назначением GUID выглядит следующим образом:
type IMyDataExport=interface(IMyData) ['{3C7EAEDE-7C59-11D2-AC74-00605207DE1D}'] {One can insert additional methods here} end;
Данное объявление определяет интерфейс со всеми методами IMyData и IUnknown, но добавляет к нему GUID. Для экспорта интерфейса, помимо GUID, необходимо создать и зарегистрировать фабрику класса. GUID для нового интерфейса должен быть уникальным и генерироваться в соответствии с алгоритмом, определенным Open System Foundation. Для его генерации можно самостоятельно создать небольшое приложение, где вызывается метод CoCreateGUID:
unit Unit1; interface uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms, Dialogs, StdCtrls, ExtCtrls, ActiveX; type TForm1 = class(TForm) Button1: TButton; Edit1: TEdit; procedure Button1Click(Sender: TObject); private { Private declarations } public { Public declarations } end; var Form1: TForm1; implementation {$R *.dfm} procedure TForm1.Button1Click(Sender: TObject); var GUID:TGUID; S:String; begin if CoCreateGuid(GUID)=S_OK then Edit1.Text:=GUIDToString(GUID); end; end.
Результат работы приложения изображен на рисунке 2:
Рис.2. Результат работы приложения
Объявленный таким образом интерфейс может быть использован как параметр в методах:
procedure TForm1.UseDataExport(const DE:IMyData); var N:integer; begin if Assigned(DE) then begin DE.GetMyData(N); Caption:=IntToStr(N); end; end;
Особенно хотелось бы остановиться на использовании оператора AS с интерфейсами. При его применении компилятор Delphi генерирует код, который вызывает метод QueryInterface. В качестве параметра метода QueryInterface применяется GUID. Поэтому оператор AS можно легально использовать только с интерфейсами, в которых был определен IID, и, следовательно, зарегистрированными в системном реестре и имеющими фабрику класса:
procedure TForm1.AnotherUseDataExport(const IU:IUnknown); var N:integer; DE:IMyDataExport; begin if Assigned(DE) then begin DE:=IU as IMyDataExport; DE.GetMyData(N); Caption:=IntToStr(N); end; end;
Код выше является легальным и работоспособным. Строка
DE:=IU as IMyDataExport;
полностью эквивалентна строке
if IU.QueryInterface(IMyDataExport,DE)<>S_OK then raise EOLEError.Create...
Но при попытке использовать интерфейс IMyData вместо IMyDataExport в примере выше:
procedure TForm1.AnotherUseDataExport(const IU:IUnknown); var N:integer; DE:IMyDataExport; begin if Assigned(DE) then begin DE:=IU as IMyData; DE.GetMyData(N); Caption:=IntToStr(N); end; end;
компилятор Delphi остановится на выделенной строке с диагнозом: Operator not applicable to this operand type - оператор неприменим к данным. Причина - отсутствие IID у интерфейса IMyData.
На следующем шаге мы рассмотрим реализацию интерфейсов.