На этом шаге мы рассмотрим, как объявляются интерфейсы.
Для реализации интерфейсов в 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.
Текст этого приложения можно взять оператора 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.
На следующем шаге мы рассмотрим реализацию интерфейсов.