На этом шаге мы рассмотрим алгоритм создания клиента для нотификационного сервера.
Теперь создадим новый проект для клиентского приложения с именем CliNote, выбрав в меню Delphi команду File | New | Application. Сразу же следует оговориться, что этот проект обязательно надо сохранять в ином каталоге, нежели каталог, в котором хранится код для сервера AutoServ. Причина - смешение версий файла AutoServ_TLB, одна из которых хранится в каталоге с сервером, а другая - в каталоге $DELPHI\Imports. Для корректной работы клиентского приложения необходима версия, которая будет храниться в каталоге $DELPHI\Imports.
Поместим на форму компоненты ТМеmо и TButton. Выберем команду Project | Import Type Library и в списке появившегося диалогового окна выберем пункт AutoServ Library.
Рис.1. Импорт библиотеки типов сервера
Установим флажок Generate Component Wrapper и закроем окно щелчком на кнопке Install. После этого Delphi задаст несколько вопросов, связанных с регистрацией нового компонента. Если все будет успешно выполнено, то на странице ActiveX палитры компонентов появится значок класса TTest. Поместим его на форму.
Рис.2. Приложение на этапе разработки
Компонент TTest - это сгенерированный Delphi контейнер для сервера автоматизации. В нем во время выполнения доступны все свойства и методы, которые ранее были определены на сервере в интерфейсе ITest. Кроме того, на странице Events окна инспектора объектов можно увидеть названия методов нотификационного интерфейса - OnTextChange и OnClose. Создавая обработчики этих событий, мы реализуем нотификациониый интерфейс на клиенте. Как и в предыдущих проектах, вызов сервера будет осуществляться динамически. Реализуем следующий код для клиента:
type TForm1 = class(TForm) . . . . private IT: ITest; . . . . end; . . . . implementation procedure TForm1.Button1Click(Sender: TObject); begin if Assigned(IT) then begin IT := nil; Test1.Disconnect; Button1.Caption := 'Connect'; end else begin Test1.Connect; IT := Test1.DefaultInterface as ITest; Button1.Caption := 'Disconnect'; end; end; procedure TForm1.Test1Close(Sender: TObject); begin IT := nil; Test1.Disconnect; Button1.Caption := 'Connect'; end; procedure TForm1.Test1TextCahnge(Sender: TObject); begin Memo1.Text := Test1.Text; end; end.
В секции private объявляется переменная IT: ITest, в которой будет храниться ссылка на интерфейс СОМ-сервера. На самом деле, ссылка хранится в качестве значения свойства DefaultInterface класса TTest. Однако, по-видимому, вследствие ошибки Delphi это свойство не инициализировано - при отсоединенном сервере там хранится значение, не равное nil. В обработчике Button1Click мы подключаемся к серверу, если соединения с ним нет, и наоборот, отсоединяемся, если соединение есть. При получении от сервера нотификации OnTextChange у сервера запрашивается новый текст и помещается в компонент ТМеmо. При получении сообщения OnClose выполняется отсоединение от сервера.
Для тестирования проекта лучше запустить несколько экземпляров созданного приложения, соединиться с СОМ-сервером и попробовать изменять содержимое компонента ТМеmо на сервере. Клиенты будут воспроизводить все изменения (рисунок 3.
Рис.3. Получение клиентами нотификаций от СОМ-сервера
Можно закрыть сервер щелчком на кнопке закрытия, и текст на кнопке клиента изменится на Connect, свидетельствуя об отсоединении от сервера.
Следует иметь в виду, что теоретически несколько клиентских приложений могут подключиться к одному интерфейсу для получения нотификационных сообщений. Например, это возможно при использовании более "мягкого" метода - GetActiveObject вместо используемого в CoTest.Create метода CreateComObject. В этом случае клиентскому приложению будет возвращена ссылка на уже созданный интерфейс ITest. Для того чтобы можно было вызвать метод Advise интерфейса IConnectionPoint, необходимо сделать перечисленные ниже изменения в сервере AutoServ.
procedure TForm1.Memo1Change(Sender: TObject); var L: TList; I,J: Integer; T: TTest; EI: ITestEvents; begin try L := ClientList.LockList; for I :=0 to L.Count-1 do begin T := TTest(L[I]); for J := 0 to T.ConnectionPoint.SinkList.Count - 1 do begin EI := IUnknown(T.ConnectionPoint.SinkList[J]) as ITestEvents; if Assigned(EI) then EI.OnTextChange; end; end; finally ClientList.UnlockList; end; end;
Класс TConnectionPoint в свойстве SinkList содержит ссылки на все клиентские приложения, обратившиеся за иотификациоииым интерфейсом. Опять-таки, здесь присутствует некоторый "обман" Delphi. По спецификации СОМ для получения ссылок на все клиентские приложения надо обращаться к методу Next интерфейса IEnumConnections. Delphi делает это самостоятельно, и программист пользуется результатами вызова метода Next, хранящимися в свойстве SinkList.
В последнее время довольно много внимания уделяется многозвенным приложениям для работы с базами данных, использующим технологию DataSnap. Центральное звено в этой архитектуре называется сервером приложений (Application Server), или сервером доступа к данным (Data Access Server), и этот сервер поддерживает протокол автоматизации для связи с клиентами. При создании в Delphi удаленного модуля данных (объекта автоматизации сервера приложений) в окне мастера, которое при этом появляется, флажок Generate Event support code отсутствует. Причина в том, что для связи с удаленным сервером может использоваться несколько различных технологий - DCOM, протоколы TCP/IP и HTTP. Уведомление же от удаленного сервера без написания дополнительного и довольно объемного кода можно получить лишь при помощи технологии DCOM.
Со следующего шага мы начнем рассматривать создание контроллеров для приложений Microsoft Office.