Шаг 20.
Назначение и принципы COM-технологии. Объявление интерфейсов и их использование при создании приложений

    На этом шаге мы рассмотрим, как объявляются интерфейсы.

    Для реализации интерфейсов в 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.
Текст этого приложения можно взять здесь (4,9 Кб).

    Результат работы приложения изображен на рисунке 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.

    На следующем шаге мы рассмотрим реализацию интерфейсов.




Предыдущий шаг Содержание Следующий шаг