На этом шаге мы рассмотрим алгоритм создания клиента для нотификационного сервера.
Теперь создадим новый проект для клиентского приложения с именем 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.
Текст этого приложения можно взять здесь (5,5 Кб).
В секции private объявляется переменная IT: ITest, в которой будет храниться ссылка на интерфейс
СОМ-сервера. На самом деле, ссылка хранится в качестве значения свойства DefaultInterface класса TTest.
Однако, по-видимому, вследствие ошибки Delphi это свойство не инициализировано - при отсоединенном сервере там хранится значение,
не равное nil. В обработчике Button1Click мы подключаемся к серверу, если соединения с ним нет,
и наоборот, отсоединяемся, если соединение есть. При получении от сервера нотификации OnTextChange у сервера
запрашивается новый текст и помещается в компонент ТМеmо. При получении сообщения OnClose выполняется отсоединение от сервера.
Для тестирования проекта лучше запустить несколько экземпляров созданного приложения, соединиться с СОМ-сервером и попробовать
изменять содержимое компонента ТМеmо на сервере. Клиенты будут воспроизводить все изменения (рисунок 3.
Рис.3. Получение клиентами нотификаций от СОМ-сервера
Можно закрыть сервер щелчком на кнопке закрытия, и текст на кнопке клиента изменится на Connect, свидетельствуя об отсоединении от сервера.
Следует иметь в виду, что теоретически несколько клиентских приложений могут подключиться к одному интерфейсу для получения
нотификационных сообщений. Например, это возможно при использовании более "мягкого" метода - GetActiveObject вместо используемого
в CoTest.Create метода CreateComObject. В этом случае клиентскому приложению будет возвращена
ссылка на уже созданный интерфейс ITest. Для того чтобы можно было вызвать метод Advise интерфейса
IConnectionPoint, необходимо сделать перечисленные ниже изменения в сервере AutoServ.
- В реализации метода Initialize класса TTest изменить флаг ckSingle на ckMulti.
- В секции public класса TTest объявить предназначенное только для чтения свойство
ConnectionPoint типа TConnectionPoint. - Для вызова метода нотификациониого интерфейса использовать, например, следующую конструкцию:
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.