Шаг 31.
Серверы и контроллеры автоматизации. Реализация методов объекта автоматизации

    На этом шаге мы рассмотрим процесс реализации методов объекта автоматизации.

    Итак, мы описали свойства и методы нашего объекта и теперь должны приступить к их реализации. Для этой цели в окне редактора библиотеки типов следует щелкнуть на кнопке Refresh панели инструментов. В модуль реализации (сгенерированный ранее мастером, использованным при создании сервера) будут добавлены заготовки методов. Перед тем как начать реализацию кода, следует учесть, что доступ к одному экземпляру сервера может осуществляться несколькими клиентами одновременно (в раскрывающемся списке Instancing окна мастера объектов автоматизации выбран пункт Multiple Instance) и из разных потоков (в раскрывающемся списке Threading Model выбран пункт Free). При таком доступе необходима синхронизация обращений клиентов к общим переменным (содержимому поля Memo1, свойствам Width и Visible) и методам, с ними работающим. Поэтому сначала внесем изменения в модуль, в котором реализуется код формы. Для этого в секции private объявим переменную типа TRTLCriticalSection, а в секции publiс определим два метода, как показано ниже:

  private
    { Private declarations }
    CS:TRTLCriticalSection;
  public
    { Public declarations }
    procedure LockForm;
    procedure UnlockForm;
  end;

    Переменную CS: TRTLCriticalSection необходимо инициализировать. Сделаем это в обработчике события формы OnCreate. Инициализация резервирует системные ресурсы - поэтому по окончании работы их надо освободить. Это делается в обработчике события OnDestroy формы. Реализация кода для формы выглядит следующим образом:

procedure TForm1.LockForm;
begin
  EnterCriticalSection(CS);
end;

procedure TForm1.UnlockForm;
begin
  LeaveCriticalSection(CS);
end;

procedure TForm1.FormCreate(Sender: TObject);
begin
  InitializeCriticalSection(CS);
end;

procedure TForm1.FormDestroy(Sender: TObject);
begin
  DeleteCriticalSection(CS); 
end;

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

    Далее можно перейти к написанию кода в заготовках методов, объявленных в редакторе библиотеки типов (прежде всего сошлемся на модуль, который содержит описание формы):

unit Unit2;

{$WARN SYMBOL_PLATFORM OFF}

interface

uses
  ComObj, ActiveX, AxCtrls, Classes, AutoServ_TLB, StdVcl;

type
  TTest = class(TAutoObject, IConnectionPointContainer, ITest)
  private
    { Private declarations }
    FConnectionPoints: TConnectionPoints;
    FConnectionPoint: TConnectionPoint;
    FEvents: ITestEvents;
    { note: FEvents maintains a *single* event sink. For access to more
      than one event sink, use FConnectionPoint.SinkList, and iterate
      through the list of sinks. }
  public
    procedure Initialize; override;
  protected
    { Protected declarations }
    property ConnectionPoints: TConnectionPoints read FConnectionPoints
      implements IConnectionPointContainer;
    procedure EventSinkChanged(const EventSink: IUnknown); override;
    function Get_Text: WideString; safecall;
    function Get_Visible: WordBool; safecall;
    function Get_Width: Integer; safecall;
    procedure AddLine(const Line: WideString); safecall;
    procedure NewFile; safecall;
    procedure OpenFile(const FileName: WideString); safecall;
    procedure SaveFile(const FileName: WideString); safecall;
    procedure Set_Text(const Value: WideString); safecall;
    procedure Set_Visible(Value: WordBool); safecall;
    procedure Set_Width(Value: Integer); safecall;
  end;

implementation

uses ComServ, Unit1;

procedure TTest.EventSinkChanged(const EventSink: IUnknown);
begin
  FEvents := EventSink as ITestEvents;
end;

procedure TTest.Initialize;
begin
  inherited Initialize;
  FConnectionPoints := TConnectionPoints.Create(Self);
  if AutoFactory.EventTypeInfo <> nil then
    FConnectionPoint := FConnectionPoints.CreateConnectionPoint(
      AutoFactory.EventIID, ckSingle, EventConnect)
  else FConnectionPoint := nil;
end;


function TTest.Get_Text: WideString;
begin
  try
   Form1.LockForm;
   Result := Form1.Memo1.Text;
  finally
   Form1.UnlockForm;
  end;
end;

function TTest.Get_Visible: WordBool;
begin
 try
  Form1.LockForm;
  Result := Form1.Visible;
 finally
  Form1.UnlockForm;
 end;
end;

function TTest.Get_Width: Integer;
begin
  try
    Form1.LockForm;
    Result := Form1.Width;
  finally
    Form1.UnlockForm;
  end;
end;

procedure TTest.AddLine(const Line: WideString);
begin
  try
    Form1.LockForm;
    Form1.Memo1.Lines.Add(Line);
  finally
    Form1.UnlockForm;
  end;
end;

procedure TTest.NewFile;
begin
  try
    Form1.LockForm;
    Form1.SpeedButton1Click(nil);
  finally
    Form1.UnlockForm;
  end;
end;

procedure TTest.OpenFile(const FileName: WideString);
begin
  try
    Form1.LockForm;
    if Length(FileName)>0 then
       Form1.Memo1.Lines.LoadFromFile(FileName)
    else
       Form1.SpeedButton2Click(nil);
  finally
    Form1.UnlockForm;
  end;
end;

procedure TTest.SaveFile(const FileName: WideString);
begin
  try
    Form1.LockForm;
    if Length(FileName) > 0 then
        Form1.Memo1.Lines.SaveToFile(FileName)
    else
        Form1.SpeedButton3Click(nil);
  finally
    Form1.UnlockForm;
  end;
end;

procedure TTest.Set_Text(const Value: WideString);
begin
  try
   Form1.LockForm;
   Form1.Memo1.Text := Value;
  finally
   Form1.UnlockForm;
  end;
end;

procedure TTest.Set_Visible(Value: WordBool);
begin
 try
   Form1.LockForm;
   Form1.Visible := Value;
  finally
   Form1.UnlockForm;
  end;
end;

procedure TTest.Set_Width(Value: Integer);
begin
 try
   Form1.LockForm;
   Form1.Width := Value;
 finally
   Form1.UnlockForm;
end;

end;

initialization
  TAutoObjectFactory.Create(ComServer, TTest, Class_Test,
    ciMultiInstance, tmFree);
end.
Текст этого приложения можно взять здесь (243,4 Кб).

    Код любого метода начинается с метода LockForm и заканчивается методом UnlockForm. При работе с критическими секциями важно, чтобы после каждого вызова процедуры EnterCriticalSection была выполнена обратная процедура - иначе корректная обработка следующего вызова процедуры EnterCriticalSection становится невозможной. По этой причине обращение к методу UnlockForm помещено в защищенный блок try...finally...end.

    Скомпилируем и запустим сервер на выполнение. При этом он зарегистрируется в реестре (рисунок 1).


Рис.1. Запись о сервере автоматизации в реестре Windows

    В действительности в разделах реестра НКЕY_LOCAL_MASHINE\SOFTWARE и HKEY_ CLASSES_ROOT содержится несколько записей, связанных с данным сервером и его интерфейсами, в том числе запись с информацией о местоположении сервера.

    Если в дальнейшем отпадет необходимость в использовании созданного сервера автоматизации, рекомендуется запустить его с параметром командной строки /unregserver. В этом случае соответствующие записи будут удалены из реестра. Если же возникнет необходимость в переносе сервера автоматизации в другой каталог, можно после этого просто запустить его снова - записи в реестре обновятся.

    Итак, мы создали настольное приложение, являющееся сервером автоматизации. Теперь, основываясь на информации о методах класса объекта автоматизации этого сервера, содержащейся в библиотеке типов, можно создавать управляющие этим сервером приложения-контроллеры с помощью довольно широкого спектра средств разработки (включая Delphi, C++Builder, Visual Basic, Visual C++, Visual Studio .NET, и др.).

    Со следующего шага мы начнем рассматривать создание контроллера автоматизации.




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