WWW.DISSERS.RU

БЕСПЛАТНАЯ ЭЛЕКТРОННАЯ БИБЛИОТЕКА

   Добро пожаловать!

Pages:     | 1 | 2 || 4 | 5 |   ...   | 8 |

«Андрей Сорокин DELPHI РАЗРАБОТКА БАЗ ДАННЫХ Москва • Санкт-Петербург • Нижний Новгород • Воронеж Ростов-на-Дону • Екатеринбург • Самара • Новосибирск Киев • Харьков • Минск ...»

-- [ Страница 3 ] --

Метод Createlnstance создает новый экземпляр объекта. Часто передается толь Базовые понятия ко идентификатор объекта, на основе которого фабрика класса заполняет ос тальные параметры. Метод LockServer вызывается клиентским приложением для того, чтобы указать, что данный сервер СОМ должен храниться в памя ти. При каждом вызове сервера параметру fLock присваивается значение True, тем самым увеличивая счетчик ссылок. После завершения использования объекта свойству fLock следует присвоить значение Fal se. Когда счетчик дос тигнет нуля, сервер будет выгружен из памяти.

Для явного вызова фабрики класса можно использовать функцию CoGet ClassObject. Данной функции в качестве параметров передаются CLSID нуж ного класса и IID интерфейса ICIassFactory. Функция ищет требуемую фаб рику и возвращает указатель на интерфейс. Далее, вызвав метод CoCreate Instance, клиентское приложение инициирует процесс создания объекта дан ной фабрикой класса.

При установке приложения, работающего с СОМ, в системный реестр запи сывается информация обо всех объектах, используемых данным приложени ем и содержащихся в нем. На рис. 4.5 приведено изображение редактора рее стра, в окне которого выбран объект СОМ, и указаны его свойства.

З Ш CLSID Щ И Name : Type Data ;

S EHOOOOOOCl^FEF-Ji: H]2|(Defauit) REG_SZ D:\WINDOWS\System32\xvid.ax I -JBBH ^ThreadingModel RES SZ Both ;

ж ЁЗ {oooooolo-oooo-oc ?;

: щ Ш {oooooon-oooo-ocvs5Ji Рис. 4.5. Информация об объекте C M O В системном реестре хранится информация об объекте:

о идентификатор класса, который однозначно определяет класс объекта;

О тип сервера — внутренний, локальный или удаленный;

о имя динамической библиотеки или исполняемого файла, а также путь к ним для локальных и внутренних серверов;

О сетевой адрес и путь для удаленных серверов;

О тип потока выполнения (Threading Model).

Используя сведения, содержащиеся в реестре, приложения могут обращаться к объектам СОМ через CSLID и создавать экземпляры объектов, используя информацию о параметрах, содержащуюся в реестре.

Библиотека типов В процессе разработки может возникнуть необходимость получить информа цию о том, какие интерфейсы имеет объект, какие методы имеют его интер фейсы, информацию об их входных и выходных параметрах. Эта информа ция содержится в библиотеке типов, которая выполнена в виде специального файла, содержащего информацию об объекте СОМ. Вся информация описы 138 Урок 4. Основы технологии C M O вается в строго определенном формате на языке IDL (Interface Definition Language). Библиотека может содержать информацию о свойствах и методах, а также сведения об используемых заглушках и заместителях.

Для создания библиотеки типов, описываемой с помощью языка IDL, исполь зуются специальные компиляторы. Доступ к библиотеке осуществляется по идентификатору CLSID класса. Кроме того, библиотека имеет свой собствен ный GUID, который хранится в системном реестре при регистрации объекта, для которого создана библиотека.

Каждая библиотека типов имеет интерфейс ITypeLib, который дает возмож ность работать с ней как с единым объектом. Для доступа к информации об отдельном интерфейсе используется интерфейс ITypelnfo. Если необходимо получить доступ к библиотеке по GUID, то используется функция LoadReg TypeLib. А если клиентскому приложению известно имя библиотеки, то мож но воспользоваться функцией LoadTypeLi b.

При создании серверов СОМ с помощью мастеров Delphi библиотеки типов и соответствующий им код генерируются автоматически, избавляя от необ ходимости писать его вручную.

СОМ и потоки выполнения Любой процесс является совокупностью виртуальной памяти, кода, данных и системных ресурсов. Поток — это код, который последовательно выполня ется внутри процесса. Любое приложение Windows имеет как минимум один поток, который выполняется процессором. Этот поток называется главным.

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

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

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

Каждый поток в процессе может работать с глобальными переменными и ре сурсами. Планировщик потоков определяет частоту использования конкрет ного потока, опираясь на комбинацию приоритетов процесса и данного пото ка. Разработчик может вручную настроить приоритет потока, вызвав функ цию SetPriorityClass для установки приоритета процесса и функцию Set ThreadPriority для установки приоритета потока.

Клиент и сервер СОМ могут выполняться в разных процессах или потоках, и к серверу одновременно может обращаться несколько клиентов. В СОМ Реализация С М в Delphi О изящно решен данный вопрос. Все объекты СОМ процесса помещаются в спе циальные группы, называемые апартаментами (Apartments). Объект СОМ су ществует только внутри одного апартамента, и его методы могут быть вызва ны только тем потоком, которые принадлежат этому апартаменту. Другие потоки могут вызывать методы данного объекта СОМ только через прокси.

Апартаменты бывают однопоточные (Single Threaded Apartments), многопо точные (Multiple Threaded Apartments) и нейтральные (Neutral Apartment).

Их стоит рассмотреть подробнее:

О Однопоточный апартамент (Single Threaded Apartments) состоит из одно го потока. Организуется очередь вызовов методов, и каждый из них обра батывается только после того, как будут обработаны предшествующие вы зовы. Внутри апартамента все объекты взаимодействуют напрямую. Ис пользование апартамента данного типа является предпочтительным для ре ализации однопоточных серверов СОМ.

О В случае использования многопоточного апартамента (Multiple Threaded Apartments) может быть создано множество потоков и объектов, причем каждый поток не привязывается к какому-либо потоку и любой метод объекта может быть вызван в любом из потоков. Сервер СОМ через служ бу RPC создает пул свободных потоков, к которым могут подключаться клиентские приложения. При вызове со стороны клиента многопоточный апартамент выбирает свободный поток из пула и предоставляет его в пользование клиенту. Когда клиент завершает свою работу, апартамент вновь помещает поток в пул. В этом случае не требуется затрачивать ре сурсы на то, чтобы заново создать поток. Указатели на интерфейсы внутри апартамента передаются напрямую. Сервер СОМ, использующий данный апартамент, способен обеспечить более высокое быстродействие по срав нению с однопоточным (особенно на многопроцессорных системах), но его реализация является гораздо более трудоемкой.

О Нейтральные апартаменты (Neutral Apartments) используются при созда нии серверов СОМ+. Методы объектов СОМ вызываются в потоке, из которого к ним обратился клиент. Использование данного типа апартамента позволяет снизить количество переключений потоков.

Реализация СОМ в Delphi Объект СОМ описывается обычным классом TCOMObject, который порожден непосредственно от класса TObject. Все свойства и методы объекта описыва ются в объявлении класса. При создании объекта СОМ с ним связывается вспомогательный класс CoClass, который описывает все его интерфейсы. При создании объекта к имени его класса добавляется приставка Со и создается CoClass этого объекта.

Описание CoCl ass содержится в библиотеке типов. Стандартное объявление класса обеспечивает создание кода объекта, который будет скомпилирован 140 Урок 4. Основы технологии C M O в двоичный код. CoCl ass обеспечивает представление экземпляра класса в со ответствии со спецификой СОМ и гарантирует корректное обращение кли ента к объекту.

Класс TComObject Класс TComObject является базовым классом, на основе которого создаются простые классы СОМ, такие как, например, расширения оболочек. Класс TComObject является СОМ-объектом, который поддерживает интерфейсы IUn known и ISupportErrorlnfo. Класс реализует простые объекты, обладающие ба зовым списком возможностей:

О Объект обладает идентификатором класса CLSID, который используется для создания экземпляров класса с использованием фабрики класса.

О Объект поддерживает агрегирование при помощи методов интерфейса IUnknown.

О Объект поддерживает безопасные вызовы и обработку исключительных ситуаций OLE, используя интерфейс IProvideErrorlnfo.

О Объект использует в работе интерфейс IErrorlnfo.

Класс TComObject может использоваться как базовый класс для создания клас сов объектов СОМ, которые должны иметь идентификатор класса (CLSID).

Идентификатор CLSID, как было сказано ранее, используется для регистра ции класса в реестре и для создания его экземпляра извне при помощи вызо ва фабрики класса.

Объект СОМ, как и любой другой объект, создается конструктором Create.

Конструктор создает объект как самостоятельный экземпляр класса, не вхо дящий в агрегат. Под агрегатом следует понимать совокупность объектов СОМ, предоставляющих свои интерфейсы и имеющих один общий — IUnknown.

Для создания объекта СОМ и включения его в агрегат используется метод CreateAggregated, который создает новый объект как часть агрегата. В параметре Controller указывается общий управляющий интерфейс IUnknown и передает ся свойству Controller.

Метод CreateFromFactory используется для создания объекта и его инициали зации. В свойстве Factory указывается фабрика класса объекта. После созда ния объекта метод Initial ize позволяет произвести его инициализацию. В ходе инициализации счетчик ссылок на объект увеличивается на единицу.

В свойстве RefCount содержится число ссылок на объект. Свойство RefCount определяет, когда созданный объект может быть уничтожен. Когда свойству присваивается нулевое значение, объект уничтожается, так как с ним не ра ботает ни один клиент. Значение свойства увеличивается методом AddRef ин терфейса IUnknown и реализуется методом ObjAddRef. Уменьшение значения свойства производится при помощи метода ObjRelease, реализуя метод Release интерфейса IUnknown.

Реализация С М в Delphi О Метод ObjQuerylnterface позволяет выяснить, имеет ли данный объект СОМ интерфейс с идентификатором, заданным IID. Данный метод является реа лизацией метода Querylnterface интерфейса IUnknown.

Класс TTypedComObject Класс TTypedComObject является прямым наследником класса TComObject. Он предназначен для создания объектов СОМ с использованием библиотеки типов. Класс TTypedComObject имеет интерфейс IProvideClassInfo, в состав ко торого входит единственный метод, предназначенный для получения указа теля на CoClass объекта.

Метод GetClassInfo является реализацией метода GetClassInfo интерфейса IProvideClassInfo.

Интерфейс IUnknown Интерфейс IUnknown является базовым для всех интерфейсов. Он является прямым потомком класса Interface, на основе которого в Delphi строятся все интерфейсы.

В состав интерфейса входят методы AddRef, Release и Querylnterface, которые рассматривались ранее.

В коде Delphi интерфейс IUnknown подменяет собой класс Interface.

Класс TComObjectFactory Класс TComObjectFactory является фабрикой класса для объектов СОМ, порож денных от класса TComObject. Класс TComObjectFactory обеспечивает функцио нирование интерфейсов IUnknown, IClassFactory и IClassFactory2. Интерфейс IClassFactory создает объект, принимая в качестве параметра его идентифи катор CLSID. Интерфейс IC1 assFactory2 используется для обеспечения лицен зирования объекта СОМ. Создание объекта фабрики класса может быть ини циировано извне при помощи функции API CoCreateClassObject. Для созда ния объекта также могут быть использованы функции CreateComObject и Create OleObject. Если создается несколько объектов одного класса, эффективнее вызывать фабрику класса, получая указатель на его интерфейс IClassFactory и используя его собственные методы. Для управления фабриками классов на сервере СОМ используется специальный класс TComClassManager, доступ к ко торому можно получить, используя функцию ComClassManager.

Метод CreateComObject инициирует вызов конструктора класса TComObjectFactory.

Конструктор Create создает фабрику класса во время запуска сервера. Конст руктор фабрики класса описывается в секции инициализации модуля, вклю чающего сервер СОМ. В параметре ComServer указывается сервер СОМ, в со ставе которого будет функционировать объект. В параметре ComClass указы вается тип класса, который используется методом GetFactoryFromClass менед 142 Урок 4. Основы технологии C M O жера классов для идентификации фабрики. Параметр ClassID задает иденти фикатор класса, создаваемого этой фабрикой, а параметр Instancing задает способ создания объекта. Этот способ регламентируется одним из перечис ленных ниже значений:

О Значение ci Internal указывает, что объект создается в процессе как сервер СОМ. Другие приложения не могут создать экземпляр объекта напрямую, но могут использовать методы приложения для создания экземпляра до кумента.

О Значение ciSinglelnstance указывает, что для каждого клиента, обратив шегося к серверу СОМ, создается собственный экземпляр объекта. Все объекты создаются в единственном экземпляре сервера.

О Значение ciMulti Instance указывает, что для каждого клиента создается соб ственный экземпляр сервера, в котором содержится экземпляр объекта.

В параметре Threadi ngModel задается способ взаимодействия сервера и клиен та. Значения параметра перечислены ниже:

О Значение tmSingle указывает, что сервер последовательно выполняет за просы клиентов.

О Значение tmApartment свидетельствует, что вызов объекта осуществляется только в том потоке, в котором он создан. Различные объекты некоторого сервера могут быть вызваны в различных потоках, при этом один объект может обслуживать одновременно только одного клиента.

О Значение tmFree указывает, что объект может одновременно использовать ся произвольным числом клиентов.

О Значение tmBoth указывает, что клиент может использовать модели tmApart ment или tmFree.

О Значение tmNeutral указывает, что к данному объекту одновременно могут обращаться несколько клиентов в различных потоках. Но защиту от воз можных конфликтов обязан обеспечить программист, используя критиче ские секции, моникеры и иные соответствующие средства. Данная модель доступна только для технологии СОМ+. При использовании СОМ приме няется модель tmApartment.

Метод RegisterClassObject регистрирует класс создаваемого объекта. Прило жения, содержащие сервер СОМ, вызывают этот метод при запуске соответ ствующего сервиса. Метод UpdateRegistry вызывается для регистрирования объекта СОМ или удаления его регистрации. Регистрация объекта произво дится при первом запуске приложения. В свойстве ClassID указывается иден тификатор класса, а свойство CI assName определяет имя класса объекта.

В свойстве ErrorllD содержится GUID интерфейса, в котором произошла ошибка. Свойство ShowErrors включает или отключает показ сообщений об ошибках при создании объекта. Если свойство имеет значение True, то сооб щение о возникающих ошибках выводится в информационном окне.

Реализация С М в Delphi О Класс TTypedComObjectFactory Класс TTypedComObjectFactory является прямым наследником класса TComObject Factory. Он используется для создания объектов класса TypedComObject.

Свойство Classlnfo позволяет получить информацию о типе без необходимо сти применения загрузки библиотеки типов. Для получения информации об объекте используется интерфейс ITypelnfo.

Метод GetlnterfaceTypelnfo возвращает информацию об объекте СОМ, создан ном данной фабрикой.

Класс TComClassManager Класс TComClassManager используется для управления фабриками классов.

Экземпляр класса TComClassManager возвращается функцией ComClassManager, которая содержится в модуле ComObj.pas. Этот экземпляр управляет фабри ками классов объектов, владельцем которых является данный сервер СОМ.

Экземпляр класса получает от сервера СОМ список фабрик и обновляет его при создании нового объекта или уничтожении старого. Разработчику предо ставляются методы, позволяющие управлять фабриками классов.

Функция ComClassManager возвращает ссылку на экземпляр класса. А метод ForEachFactory последовательно выполняет определенные действия над всеми фабриками классов данного сервера СОМ. В параметре ComServer указывает ся сервер СОМ, а в параметре FactoryProc — ссылка на метод, выполняющий необходимые действия.

Метод GetFactoryFromCI ass возвращает фабрику класса для класса, указанного в параметре ComClass. А метод GetFactoryFromCIassID возвращает фабрику класса, принимая в качестве входного параметра идентификатор класса ClassID.

Класс TComServer Класс TComServer предназначен для создания экземпляров серверов СОМ. При создании объекта СОМ в его модуль автоматически добавляется модуль ComServ, содержащий реализацию класса TComServer. Класс TComServer содержит всю необходимую информацию о сервере. В нем упоминаются библиотека типов, имя, файл справки и многие другие параметры. Также сервер содер жит информацию о том, как он должен быть загружен и как он должен быть выгружен из адресного пространства памяти. Класс сервера применяется для создания экземпляров фабрик классов объектов, используя их идентифика торы CLSID.

Создается объект сервера СОМ методом Create. Метод Initialize вызывается при создании сервера. При первом запуске он производит регистрацию всех связанных объектов СОМ в системном реестре.

Режим запуска сервера определяется в свойстве StartMode. Его возможные значения перечислены далее:

144 Урок 4. Основы технологии COM О Значение smAutomation указывает, что сервер запускается как сервер авто матизации в ответ на запрос от контроллера автоматизации.

О Значение smRegServer указывает, что сервер запускается с целью регистра ции в системном реестре (первый запуск) либо с использованием ключа /regserver.

О Значение smStandal one указывает, что сервер запускается пользователем как отдельное приложение.

О Значение smllnregServer указывает, что сервер запускается с целью удале ния регистрации из системного реестра (используется ключ /unregserver).

Свойство IsInprocServer указывает, является ли данный сервер СОМ внутрен ним сервером или нет. Для внутреннего сервера используется значение True.

В свойстве ServerKey указывается, является ли данный сервер СОМ внутрен ним или локальным. Свойство содержит строку InprocServer32, если сервер является внутренним и оформлен в виде библиотеки DLL. Если сервер явля ется локальным и существует в форме ЕХЕ-файла, то используется значение LocalServer32.

Для загрузки библиотеки типов следует вызвать метод LoadTypeLi b. В случае возникновения ошибки будет возвращен объект класса исключения Е01е SysError с информацией об ошибке.

Получить доступ к библиотеке типов, связанной с данным сервером, можно через свойство TypeLib, возвращающее интерфейс ITypeLib. Выяснить количе ство объектов, запущенных в данном сервере СОМ, можно при помощи свой ства ObjectCount. Значение свойства увеличивается на единицу, когда новый объект создается фабрикой класса, и уменьшается, когда он уничтожается.

В свойстве ServerName содержится имя сервера. Задать его можно методом SetServerName. Если сервер использует библиотеку типов, то метод не будет выполнен, так как свойство получит значение автоматически из библиотеки типов.

В свойстве ServerFileName содержится полный путь к файлу сервера и его имя.

А путь к соответствующему файлу справки содержится в свойстве HelpFileName.

Создание внутреннего сервера СОМ и работа с ним Для создания серверов COM Delphi предоставляет широкий выбор мастеров, автоматизирующих выполнение трудоемких задач. Мастера доступны на вклад ке ActiveX репозитория объектов. Перед созданием внутреннего сервера СОМ в виде DLL необходимо создать специальную библиотеку, которая имеет не сколько функций и описывается по правилам СОМ.

Репозиторий объектов можно активировать при помощи команды меню File • New • Other. Для дальнейшей работы потребуется вкладка ActiveX. Нужно соз дать новый проект, выбрав значок ActiveX Library. Окно репозитория показано на рис. 4.6.

Реализация СОМ в Delphi / New Items IntraWeb | W b evc s \ B s e s j W b n p I Web D c m ns \ C r a e S ti e ui s n eSa ou e t ob New ActiveX \ Multitiet j Project! | F r s | Dialogs i Projects \ Data M d e om ousl A tv Fr A tv S re A t e ci e om ci e ev r ci X v Automation O et bc j C nr l o to Ojc be t •H C M Object C M E e t C M Po et P g Transactional O 0 + vn O* r p ry a e Object Subscript. Ojc be t T p Library ye Hl ep Cancel Рис. 4.6. Репозиторий объектов Созданный проект нужно сохранить с именем TestServ. В листинге 4.2 приве ден код созданной библиотеки.

Листинг 4.2. Код библиотеки TestServ library TestServ;

uses ComServ;

exports DilGetClassObject, DllCanUnloadNow, DllRegisterServer, DilUnregisterServer:

{$R *.RES} begin end.

Созданная библиотека экспортирует функции Dl lGetCI assObject, Dl 1 CanUnl oadNow, DllRegisterServer и DilUnregisterServer, необходимые для работы с СОМ-тех нологией. Эти функции объявляются в модуле ComServ:

О Метод DllGetClassObject возвращает фабрику класса для объекта СОМ.

О Метод Dl I CanUnl oadNow производит проверку на предмет того, возможно ли выгрузить из памяти DLL-библиотеку данного сервера СОМ. Функция возвращает значение SOK, если выгрузка возможна. В противном случае возвращается значение S_FALSE.

Урок 4. Основы технологии C M O О Метод DllRegisterServer регистрирует сервер СОМ в системном реестре.

Метод DTIUnregisterServer удаляет регистрацию данного сервера СОМ из о системного реестра.

После этого необходимо создать объект СОМ. Для этого в репозитории объек тов на вкладке ActiveX следует выбрать значок C M Object. В результате появится O диалоговое окно, показанное на рис. 4.7, в котором необходимо заполнить несколько ключевых полей. Объект будет создаваться с учетом данных, вве денных разработчиком.

... •......

. • r у • "• \ - - ' J V ".'•' - • - • •. ' -. - :- - '• < X ' - \ j •-•-.•-.',-. - ;

. - -.. -•, •' " ' '•'•' • r-'.".

L ^. -...-•..... •. • •.••..•. •.. ••.•.-.

;

T e s t C o m •i l a s s N a m e :

j M u l t i p l e I n s t a n c e i i n s t a n c i n g :

J A p a r t m e n t I T h r e a d i n g M o d e l " ~ 3 • i i m p l e m e n t e d L i s t } d T e s t C o m I I n t e r f a c e :

i d e s c r i p t i o n :

p ;

•" O p t i o n s eu t o m a t i o n ;

j j V I n c l u d e l y p fi L i b r a r y f v * M a r k i n t e r f a c e 0 t e :

O K C a n c e l j H e l p j Рис. 4.7. Мастер создания СОМ-объекта В поле ClassName указывается имя создаваемого класса. Под этим именем он будет зарегистрирован в реестре, и оно же будет использовано для названия класса CoClass. На рисунке видно, что было выбрано имя TestCom. В списке Instancing определяют способ создания объекта. Следует установить значе ние Multiple Instance. В списке Threading Model указывается способ взаимодей ствия сервера и клиента. В данном случае требуется использовать значение Apartament. В поле Implemented Interface указываются интерфейсы, входящие в состав создаваемого объекта СОМ. По умолчанию для объекта создается собственный интерфейс. Также интерфейс можно выбрать из списка, получа емого при нажатии кнопки List. Активируемое диалоговое окно показано на рис. 4.8. Установка значка Include Type Library приводит к включению в сервер библиотеки типов. Если флажок установлен, то объект наследуется от класса TTypedComObject, если снят — от класса TCOMObject. Установка флажка Mark interface Oieautomation указывает, что сервер СОМ создается как совместимый с технологией OLE.

Созданный модуль нужно сохранить с именем ComUnit. После того как к сер веру будет добавлен объект, в секции Uses библиотеки появится указатель на него и на библиотеку типов сервера, как показано в листинге 4.3.

Реализация С М в Delphi О / Interface Scle/.tlon Wirard l, - | i X, I n t e r f a c e j T y p e L i b f a r y.. f V e r s i o n j P a t h m \ • A u t a T e x t E n t r y M S W O R D. O L B 8. 2 D A P r o a r a m F i l e s...

A u t o T e x t E n t r i e s M S W O R D. O L B 8. 2 [ D A P i o g r a m F i l e s \ M i c r o s o f t T e x t R e t t i e v a l M o d e 8. M S W O R D. O L B D A P r o g r a m F i l e s.. j DAProgram Files... Г S h a d i n g 8. M S W O R D. O L B DAProgram Files... H B o f d e r s 8. M S W O R D. O L B DAPiogram Files.. | C o m m e n t M S W O R D. O L B 8. DAPiogram Files... ;

V :

E n d n o t e M S W O R D. O L B 8. DAPiogiam Files..

F o o t n o t e 8. M S W O R D. O L B DAPiogiam Files..., : j :

C o m m e n t s M S W O R D. O L B 8. D:\ProgramFiles.. i E n d n o t e s 8 M S W O R D. O L B \ DAPiogram Files... i F o o t n o t e s M S W O R D. O L B 8 ;

: DAPiogram Files...

T w o f n i t i a l C a p s E x c e p t i o n 8. M S W O R D. O L B DAProgram Files. I :

T w o l n i t i a l C a p s E x c e p t i o n s 8. M S W O R D. O L B DAProgram Files. ;

^*;

:

8. F i t s t L e t t e r E x c e p t i o n M S W O R D. O L B i AddUblarj j ;

\ Cancel Help P l e a s e w a i t w h i l e t h e i n t e r f a c e s a r e l o a d e d Рис. 4.8. Список интерфейсов, зарегистрированных на компьютере Листинг 4.3. Секция Uses библиотеки uses ComServ, TestServ_TLB i n 'TestServ_TLB.pas', ComUnit in 'ComUnit.pas' {TestCom: CoClass};

Код модуля объекта COM, включенный в проект после его создания, приве ден в листинге 4.4.

Листинг 4.4. Код объекта TestCom unit ComUnit;

{$WARN SYMBOL_PLATFORM OFF} i interface uses Windows. ActiveX, Classes. ComObj. TestServJIB, StdVcl type TTestCom = class(TTypedComObject, ITestCom) protected {Declare ITestCom methods here} end;

продолжение Урок 4. Основы технологии C M O Листинг 4.4 (продолжение) implementation uses ComServ;

initialization TTypedComObjectFactory.Create(ComServer, TTestCom, Class_TestCom, ciMultiInstance, tmApartment);

end.

Как видно из строки TTestCom = class(TTypedComObject, ITestCom), предком класса TTestCom является класс TTypedComObject. В секции инициализации располага ется фабрика класса объекта. В листинге 4.5 приведен код автоматически со зданной библиотеки типов, которая была сохранена в модуле TestServ_TLB.pas.

Листинг 4.5. Код модуля TestServ_TLB.pas unit TestServJIB;

/ WARNING / // // The types declared in this file were generated from data read from / a Type Library. If this type library is explicitly or indirectly / // (via another type library referring to this type library) re-imported, / or the 'Refresh' command of the Type Library Editor activated while editing / // the Type Library, the contents of this file will be regenerated and all / manual modifications will be lost.

/ / / ************************************************************************ II P S L T : 1. A TW R // File generated on 05.10.2004 0:45:41 from Type Library described below.

// ************************************************************************ i I II Type Lib: D:\Documents and Settings\AHflpeii\Desktop\KHHra\TeKCTbi программ\4.1 Создания внутреннего сервера COM\Cepeep\TestServ.tlb (1) // LIBID: {2BF7428F-C768-43C9-94A2-B7AAA4B74AA5} // LCID: / Helpfile:

/ / HelpString: TestServ Library / // DepndLst:

// (1) v2.0 stdole. (D:\WIND0WS\System32\STD0LE2.TLB) Реализация СОМ в Delphi {STYPEDADDRESS OFF} / Unit must be compiled without type-checked pointers.

/ {$WARN SYMBOL_PLATFORM OFF} {SWRITEABLECONST ON} {SVARPROPSETTER ON} interface uses Windows, ActiveX, Classes, Graphics, StdVCL, Variants:

// GUIDS declared in the TypeLibrary. Following prefixes are used:

// Type Libraries : LIBID_xxxx // CoClasses : CLASS_xxxx // DISPInterfaces : DIID_xxxx // Non-DISP interfaces: IID_xxxx const // TypeLibrary Major and minor versions TestServMajorVersion = 1;

, TestServMinorVersion = 0:

LIBIDJestServ: TGUID = '{2BF7428F-C768-43C9-94A2-B7AAA4B74AA5}':

IIDJTestCom: TGUID = ' {C03B7EBC-8BB8-4A25-8BCD-542D91D078F4}':

CLASSJestCom: TGUID = '{73CB7E38-B15A-40CC-A2F2-ECB81DACBB0E}':

type /1 **********************************************************•***********/ / // Forward declaration of types defined in TypeLibrary ITestCom = interface:

/I *********************************************************************/j II Declaration of CoClasses defined in Type Library // (NOTE: Here we map each CoClass to its Default Interface) TestCom = ITestCom;

продолжение •& Урок 4. Основы технологии C M O Листинг 4.5 (продолжение) II Interface: ITestCom // Flags: (256) OleAutomation // GUID: {C03B7EBC-8BB8-4A25-8BCD-542D91D078F4} ITestCom = interface(IUnknown) [' {C03B7EBC-8BB8-4A25-8BCD-542D91D078F4}'] end;

л********************************************************************/j /j II The Class CoTestCom provides a Create and CreateRemote method to // create instances of the default interface ITestCom exposed by // the CoClass TestCom. The functions are intended to be used by // clients wishing to automate the CoClass objects exposed by the // server of this typelibrary.

/I *********************************************************************/j CoTestCom = class class function Create: ITestCom;

class function CreateRemote(const MachineName: string): ITestCom:

end;

implementation uses ComObj;

class function CoTestCom.Create: ITestCom;

begin Result := CreateComObject(CLASS_TestCom) as ITestCom;

end;

class function CoTestCom.CreateRemote(const MachineName: string): ITestCom;

begin Result := CreateRemoteComObject(MachineName, CLASS_TestCom) as ITestCom;

end;

end.

В библиотеке типов содержится описание интерфейса ITestCom и указывается его GUID. Класс CoTestCom содержит два метода. Метод Create применяется для создания объекта СОМ, используемого для работы с сервером, располо женным на данной машине, а метод CreateRemote — для создания объекта, предназначенного для работы с удаленным сервером.

Реализация С М в Delphi О Теперь необходимо добавить созданному интерфейсу функциональности, то есть создать новый метод. Для этого нужно запустить редактор библиотеки типов, выполнив команду меню View • Type Library. Окно редактора показано на рис. 4.9.

В списке указаны интерфейс ITestCom и класс TestCom. В данном редакторе можно добавлять новые методы и интерфейсы к выбранному объекту.

Ф TestSe Ati ue I F g \ T x trb t s a s et l Nm:

ae ie t o f sC m l GO U;

i V ro :

es ni ho. Pr n Inteiface: 5 nnw ae t Uko n l jri : Hp el :

! Hp Sr g ^nef c ( f Ts o O et e ti : I t ra e o et m bc ln C j I Hp C net e o t x:

l : Hp Sr g C net e tn o t x li I Рис. 4.9. Редактор библиотеки типов В списке надо выбрать интерфейс ITestCom, а затем выполнить команду кон текстного меню New • Method. К интерфейсу будет добавлен новый метод, ко торый в рассматриваемом примере получил имя SimpleMethod. В левой части окна нужно выбрать вкладку Parameters, как показано на рис. 4.10.

T sS r e t ev Ati ue Prm t r j F g \ T x j trb t s aa e s a s et el Р ITeslCom • &> SimpleMethod R t r T p : jlong eun y e • fj» MultNumber. r aan t f P r r eeS Ш TestCom ! Щате JType ' jl | ||M| ui |MU»,IV „ | long Рис. 4.10. Установка параметров метода В списке Parametrs можно определять входные и выходные параметры метода, их типы и имена. Добавить новый параметр можно кнопкой Add. В поле Name нужно указать имя Mult, в поле Туре потребуется выбрать значение Long. После этого нужно дважды щелкнуть мышью в поле Modifier. Появится окно, показан ное на рис. 4.11, в котором будет предложено отметить тип параметра.

Урок 4. Основы технологии C M O Рис. 4.11. Окно выбора типа параметра В данном случае нужно выбрать тип In. В списке Return Type следует указать значение long. Для того чтобы создать болванку метода, нужно нажать кноп ку Refresh Implementation, расположенную на панели верхней части окна. За тем к данному интерфейсу нужно добавить свойство типа Write Only. Для это го потребуется выбрать из списка, расположенного рядом с кнопкой New Property, нужное значение. Новому свойству надо дать имя MultNumber. Оста нется настроить единственный входной параметр, для которого в списке Return Туре нужно выбрать значение НRESULT.

В приложение нужно добавить еще один интерфейс — ISecond. На вкладке Attributes в списке Parent Interface потребуется выбрать IUknown. Затем можно перейти на вкладку Flags и сбросить флажки Dual и OLe Automation. В интер фейс нужно добавить метод Count и в списке Return Type указать для него зна чение long.

Теперь нужно выбрать значок класса TestCom, перейти на вкладку Implements и выполнить команду контекстного меню Insert Interface. Из появившегося списка нужно выбрать интерфейс ISecond. Это позволит связать второй ин терфейс с объектом. Останется лишь нажать кнопку Refresh. В листинге 4.6.

показан код модуля ComUnit, содержащего реализацию методов и свойств сер вера.

Листинг 4.6. Код сервера С М О unit ComUnit;

{$WARN SYMBOL_PLATFORM OFF) interface uses Windows, ActiveX, Classes, ComObj, TestServJIB. StdVcl;

type Реализация СОМ в Delphi TTestCom - class(TTypedComObject, ITestCom, ISecond) protected function SimpleMethod(Mult: Integer): Integer: stdcall;

function Set_MultNumber(Mult: Integer): HResult: stdcall;

function Count: Integer: stdcall:

{Declare ITestCom methods here} end:

implementation uses ComServ:

var Number : Integer:

MultNumber : Integer:

function TTestCom.SimpleMethod(Mult: Integer): Integer:

begin Result:=Mult*Number:

end;

function TTestCom.Set_MultNumber(Mult: Integer): HResult:

begin Number:=Mult:

end;

function TTestCom.Count: Integer;

begin Result:=Number+MultNumber;

end;

initialization TTypedComObjectFactory.Create(ComServer. TTestCom, Class_TestCom, ciMultiInstance. tmApartment);

end.

В соответствии с внесенными добавлениями будет модифицирована библио тека типов. Стоит обратить внимание на то, что в нее были добавлены описа ния интерфейсов и их методов. Теперь проект можно откомпилировать. Пос ле этого необходимо зарегистрировать библиотеку в системном реестре. Для этого достаточно выполнить команду меню Run • Register ActiveX Server. В ре зультате будет выведено сообщение о том, что библиотека зарегистрирована в системном реестре.

154 Урок 4. Основы технологии COM Теперь нужно создать простое клиентское приложение. Если посмотреть на листинг внимательнее, станет понятно, что первый интерфейс перемножает два числа, а второй складывает их. Одно из чисел передается в перемножа ющий метод SimpleMethod через свойство MultNumber.

После создания нового проекта надо разместить на форме три компонента TEdit и две кнопки. Для того чтобы обратиться к серверу, необходимо добавить в про ект файл библиотеки типов TestServ_TLB.pas. В листинге 4.7 приведен код кли ентской части.

Листинг 4.7. Код клиентского приложения implementation uses TestServ_TLB;

var TestCom : ITestCom;

Second : ISecond;

{$R *.dfm} procedure TForml.MultBtnClick(Sender: TObject);

begin TestCom:=CoTestCom.Create;

TestCom.QueryInterface(IID_ISecond, Second);

.

TestCom.Set_MultNumber(StrToInt(Editl.Text)):

Edit3.Text:=IntToStr(TestCom.SimpleMethod(StrToInt(Edit2.Text))):

end;

procedure TForml.SumBtnClickCSender: TObject);

begin TestCom:=CoTestCom.Create:

Edit3.Text;

=IntToStr(Second.Count);

end;

end.

Следует обратить внимание на следующий фрагмент кода:

TestCom:=CoTestCom.Create;

TestCom.QueryInterface(IID_ISecond, Second);

...

: • •.

J 3 F """" [ 1 Умножение j Суммирование^ Рис. 4.12. Клиентская часть В этом фрагменте создается CoClass класса TTestCom. Его конструктор возвра щает указатель на базовый интерфейс ITestCom в переменную TestCom. Для Реализация СОМ в Delphi получения указателя на второй интерфейс была использована функция Query Interface первого интерфейса, в качестве параметров которой были переданы идентификатор интерфейса I ID I Second и переменная, в которую был возвра щен указатель. На рис. 4.12 изображено окно клиентского приложения.

Создание локального сервера СОМ и работа с ним Для разработки локального сервера потребуется создать новый проект и со хранить его под именем Serv. На главной форме нужно расположить компо нент TEdit. Сам модуль надо сохранить с именем LocalServProj.

Теперь к проекту нужно добавить объект СОМ. Для этого на вкладке ActiveX репозитория объектов следует выбрать значок C M Object и добавить соответ O ствующий объект в проект. В поле QassName окна мастера создания объекта СОМ нужно ввести значение Editor. В списке Instancing потребуется выбрать значение Multiple Instance, а в списке Threading Model — значение Apartament.

Модуль объекта СОМ нужно сохранить с именем ComUnit.

Выполнив пункт меню View • Type Library, нужно вызвать библиотеку типов объекта. К интерфейсу IEditor следует добавить метод ReplaceStr. На вкладке Parametrs нужно установить значения параметров метода. В поле Name долж но оказаться значение Str, в поле Туре нужно выбрать значение BSTR. В поле Modifier нужно установить значение In, а в списке Return Type выбрать значе ние HRESULT. После этого нужно нажать кнопку Refresh Implementation для формирования шаблона метода и его описания.

Данный СОМ-объект будет взаимодействовать с формой приложения. С по мощью метода ReplaceStr будет изменяться содержимое редактора TEdit. Код объекта приведен в листинге 4.8. Для того чтобы зарегистрировать сервер в си стеме, достаточно просто запустить его.

Листинг 4.8. Код локального сервера СОМ unit ComUnit:

{$WARN SYMBOL_PLATFORM OFF} interface uses Windows. ActiveX, Classes, ComObj, ServJIB, StdVcl;

type TEditor = class(TTypedComObject, IEditor) protected function ReplaceStr(const Str: WideString): HResult: stdcall;

продолжение •& Урок 4. Основы технологии C M O Листинг 4.8 (продолжение) {Declare IEditor methods here} end;

implementation uses ComServ, LocalServProj;

function TEditor.ReplaceStr(const Str: WideString): HResult;

begin Main.Editl.Text:=Str;

end;

initialization TTypedComObjectFactory.Create(ComServer. TEditor. Class_Editor..

ciMultiInstance, tmApartment);

end.

Теперь нужно разработать клиентскую часть приложения. В новом проекте на главной форме нужно расположить компонент TEdit и одну кнопку. Чтобы иметь возможность обращаться к серверу, необходимо подключить библиоте ку типов. Для этого в секции uses надо подключить библиотеку Serv_TLB. Если сервер располагается в ином каталоге, нежели клиентская часть, то необхо димо скопировать библиотеку типа в каталог с клиентской частью. Код кли ентского приложения приведен в листинге 4.9.

Листинг 4.9. Код клиентской части var Forml: TForml;

implementation uses ServJIB;

маг Locallnterface : IEditor;

{$R *.dfm} procedure TForml.SendBtnClick(Sender: TObject):

begin Local Interface:=CoEditor.Create;

Автоматизация Local Interface.Rep1aceStr(SendEdit.Text);

end;

end.

В методе SendBtn создается CoClass объекта и в переменную Local Interface пе редается указатель на интерфейс IEditor — базовый интерфейс класса. После этого вызывается метод, в качестве параметра которому передается строка. На рис. 4.13 показана основная форма приложения и окно сервера.

(Тестовая строка" Отослать Рис. 4.13. Взаимодействие клиентского приложения и сервера Можно запустить несколько экземпляров клиентского приложения. Следует обратить внимание на то, что сервер будет выгружен из памяти только тогда, когда отключится последний клиент.

Автоматизация Автоматизация (Automation, ранее называвшаяся OLE Automation) является технологией, позволяющей пакетам приложений предоставлять свои методы другим приложениям. Автоматизация основана на технологии СОМ и широ ко применяется в системе Windows. Взаимодействие между приложениями осуществляется с помощью вызовов методов интерфейсов, основанных на интерфейсе IDispatch.

Основным отличием автоматизации от родительской технологии СОМ явля ется способ вызова методов интерфейсов. При использовании СОМ вызов интерфейса возможен только после получения указателя на метод в вирту альной таблице. Этот способ называет ранним связыванием. При использо вании автоматизации тоже можно использовать раннее связывание, но воз можно использование и другого способа, когда обращение к нужному методу производится при помощи вызова'специального метода Invoke, возвращающего указатель на нужный метод по его идентификатору. Этот метод называют поздним связыванием.

Объекты приложений, или программные средства, построенные с использо ванием автоматизации, называются объектами ActiveX. Приложения, обраща Урок 4. Основы технологии C M O ющиеся к этим объектам, называются ActiveX-клиентами или контроллера ми автоматизации. В общем случае реализация технологии автоматизации имеет ту же структуру, что и реализация СОМ. Ярким примером пакета, по строенного на использовании данной технологии, является пакет MS Office.

Интерфейс IDispatch Интерфейс IDispatch является базовым интерфейсом автоматизации. Код его объявления в модуле System.pas приведен в листинге 4.10.

Листинг 4.10. Объявление интерфейса IDispatch IDispatch = interface(IUnknown) [' {00020400-0000-0000-СООО-000000000046}'] function GetTypelnfoCountCout Count: Integer): HResult: stdcall;

function GetTypeInfo(Index, LocalelD: Integer;

out Typelnfo): HResult;

stdcall:

function GetIDsOfNames(const IID: TGUID: Names: Pointer:

NameCount, LocalelD: Integer;

DispIDs: Pointer): HResult;

stdcall:

function Invoke(DispID: Integer;

const IID: TGUID;

LocalelD: Integer;

Flags: Word;

var Params: VarResult. Exceplnfo, ArgErr: Pointer):

HResult;

stdcall;

end:

Как видно из объявления, интерфейса является наследником IUnknown и, в до полнение к его трем методам, имеет четыре собственных.

Метод Invoke используется для обращения к методам интерфейса. В парамет ре DispID указывается уникальный идентификатор вызываемого метода. В па раметре I ID указывается GUID-идентификатор интерфейса, метод которого вызывается. Если приложение поддерживает многоязычность, в параметре LocalelD указывается идентификатор системной локали. На практике этот параметр обычно игнорируется. В параметре Fl ags указывается способ вызо ва метод. Он может быть вызван как обычный метод, как метод чтения или как метод записи. В параметре Params хранится указатель на структуру pDisp Params, содержащую параметры вызываемого метода. Указатель на результат, возвращенный вызванным методом, содержится в параметре VarResult. Если метод ничего не возвратил, указатель передает значение Nul 1.

Если при вызове метода возникла ошибка, в параметр Exceplnfo помещается указатель на структуру pExcepInfo, которая содержит информацию об ошиб ке. В параметр ArgErr помещается индекс неверного заданного параметра, указанного разработчиком в структуре pDispParams.

Метод GetlDsOfNames возвращает в параметре DispIDs указатель на массив rgDispId, содержащий идентификаторы методов, имена которых были переда ны методу. Эти идентификаторы перечисляются в массиве rgszNames, указа тель на который содержится в параметре Names. В параметре Name указывается Автоматизация количество обработанных методом имен. Часто этот метод используется для определения размерности массива rgszNames.

Метод GetTypelnfo возвращает указатель на интерфейс ITypelnfo библиотеки типов. Этот указатель хранится в параметре Typelnfo. В случае успешного выполнения метода параметр Index принимает нулевое значение.

Метод GetTypelnfoCount позволяет выяснить, может ли возвращать данный объект информацию о типе во время выполнения. В параметре Count может быть возвращено одно из двух значений. Нулевое значение свидетельствует о том, что объект не возвращает информацию о типе во время выполнения.

В этом случае для работы с объектом можно использовать виртуальную таб лицу или интерфейс IDispatch. Единичное значение указывает, что объект воз вращает информацию о типе во время выполнения.

Интерфейсы диспетчеризации и дуальные интерфейсы Интерфейс диспетчеризации представляет собой интерфейс, в котором содер жится список всех доступных свойств и методов, к которым можно обратить ся с помощью метода I nvoke. Когда контроллеру необходимо использовать тот или иной метод интерфейса, он вызывает метод Invoke и передает ему в каче стве параметра идентификатор интерфейса DispID. В листинге 4.11 приведено типовое описание интерфейса.

Листинг 4.11. Код описания диспетчерского интерфейса в библиотеке типов IExampleDisp = di spinterface ['{1499FEFE-9AAC-44AB-86D8-6399D75EF4F1}1] procedure Methodl: dispid 201;

procedure Method2;

dispid 202:

property Propertyl: Integer readonly dispid 203;

property Property2: Integer writeonly dispid 204;

end;

Диспетчерский интерфейс является наследником интерфейса IDispatch. При его объявлении используется ключевое слово Di spinterface. При передаче параметров и результатов от клиента к серверу (и обратно) производится приведение данных к вариантному типу и обратное преобразование. Поэто му параметры свойств и методов могут иметь только те типы, которые могут быть свободно конвертированы в Variant и обратно.

Дуальный интерфейс сочетает в себе свойства обычного и диспетчерского ин терфейса. К его методам можно обратиться как через вызов метода Invoke, так и через виртуальную таблицу. Для контроллеров, которые могут взаимодейство вать только с теми объектами, которые предоставляют информацию о типах только во время выполнения, будет использован диспетчерский интерфейс.

В другом случае для тех объектов, что также поддерживают раннее связыва ние, наиболее эффективным будет использование виртуальной таблицы.

160 Урок 4. Основы технологии C M O На рис. 4.14 приведена схема, на которой показано, как производится обра щение к методам объекта автоматизации.

lM DispD2-»[ Метод "' DispD3—•! Метод Интерфейс Рис. 4.14. Механизм вызова методов объекта автоматизации Класс TAutoObject Класс TAutoObject реализует объект автоматизации в Delphi. Он является ба зовым классом для всех объектов автоматизации. Этот класс инкапсулирует интерфейс IDispatch, а значит, реализует его методы GetlDsOfNames, GetTypelnfo, GetTypelnfoCount и Invoke.

Свойство AutoFactory позволяет обратиться к фабрике класса, с помощью кото рой создан данный объект.

Метод I n i t i a l i z e вызывается при создании объекта. В данном классе он не используется, но может быть переопределен в потомках для инициализации нужных свойств или выполнения нужных действий.

Класс TAutoObjectFactory Класс TAutoObjectFactory реализует собой фабрику класса объекта автомати зации TAutoObject. Он является наследником класса TTypedComObjectFactory.

Для создания фабрики класса объекта автоматизации используется метод Create. Свойство Di splntf Entry содержит указатель на структуру TInterf aceEntry, предоставляющую информацию, необходимую для вызова интерфейсов дис петчеризации.

Автоматизация Метод GetlntfEntry позволяет получить указатель на структуру PInterfaceEntry для диспетчерского интерфейса, определенного параметром Guid. А через свой ство DispTypelnfo можно получить указатель на интерфейс ITypelnfo, который содержит описание диспетчерского интерфейса. Данное свойство использу ется для получения информации о типах во время создания объекта автома тизации.

TAutoIntfObject Класс TAutoIntfObject используется для создания объектов автоматизации, поддерживающих диспетчерские интерфейсы. Класс TAutoIntfObject включа ет в себя интерфейсы IDispatch и ISupportErrorlnfo. Если объект автоматиза ции будет использоваться только внутри приложения, то для его создания можно использовать класс TAutoIntfObject, не требующий использования биб лиотеки типов. Класс TAutoIntfObject не имеет фабрики класса и поэтому для создания объекта вызывает конструктор Create. Конструктор создает объект с глобальным идентификатором Displntf на основе информации о типе, содер жащейся в параметре TypeLib.

После создания объекта автоматизации его глобальный идентификатор по мещается в свойство DispIID. Для получения информации о диспетчерском интерфейсе следует обратиться к свойству Displntf Entry, которое содержит указатель на структуру PInterfaceEntry. Если же необходимо получить тип диспетчерского интерфейса, то надо обратить внимание на свойство Disp Typelnfo, содержащее указатель на интерфейс ITypelnfo.

Класс TAutoIntfObject похож на класс TAutoObject, так как оба класса реализу ют интерфейсы IDispatch и ISupportErrorlnfo. А также оба они требуют нали чия библиотеки типов для создания дуальных интерфейсов. Класс TAuto IntfObject отличается от класса TAutoObject тем, что не имеет фабрики класса.

Сервер автоматизации и пример его реализации Под сервером автоматизации следует понимать любое приложение, в состав которого входит объект автоматизации. Также это приложение должно быть зарегистрировано в операционной системе. Сервер автоматизации может быть внутренним (In-process), локальным (Local) или удаленным (Remote). В этом разделе будет приведен пример создания подобного приложения.

Для начала, как всегда, -потребуется создать проект. В состав приложения не обходимо включить объект автоматизации. Для этого при помощи пункта меню File • New • Other надо открыть репозитарий объектов и перейти на его вкладку ActiveX. На этой вкладке надо активировать элемент Automation Object. После его активации будет отображено диалоговое окно, показанное на рис. 4.15.

В поле CoClass необходимо определить имя нового объекта. В списке Instancing надо выбрать значение Multiple Instance, которое задает способ создания объек та. Элементы списка Threading Model указывают, как будут взаимодействовать 6 Зак. Урок 4. Основы технологии COM сервер и контроллеры. В данном случае надо выбрать значение Apartment.

После подтверждения правильности введенных данных при помощи кнопки ОК надо сохранить добавленный к проекту модуль объекта с именем AutoObject.

CQsNm: S p l o as a e j m e il i sa c g nt n i :

n j M t e I sa c up nt n e li l T r a n M d: ] A at et he dg o e pr n i l m • O tn ps o i :

P Gnrt e t s p ot cd e ea v n u p r oe e Hp el C ne acl Рис. 4.15. Окно мастера создания объекта автоматизации Теперь нужно перейти в редактор библиотеки типов, выполнив команду меню View • Type Library. В редакторе надо добавить три метода без параметров с име нами Stop, Start и Reset. Также потребуется одно свойство CurentTimeValue типа Read Only, которое будет возвращать значение типа Integer. В окне параметров свойства CurentTimeValue и в поле Return Type нужно выбрать значение Long.

В свойстве Modifier указываются значения out, retval. На рис. 4.16 приведено изображение окна библиотеки типов.

Маш J Ar ue Prmtr j Fg j T x | tb t s aa e s a s et i el Р ISimple ! * stop • R t r Tp : pn eun y e og \ * Start [ aa ee?

Prm t r : • *a Reset *0 CurentTimeValue "T p fye j o ia M df - & Simpie :

Vu ae l llalLassalL.

long :

Рис. 4.16. Библиотека типов сервера автоматизации После нажатия на кнопку Refresh будет сгенерирован код объекта и библио теки типов. На главной форме приложения потребуется разместить три кнопки, два компонента TLabel и один компонент TTimer. В качестве примера будет разработан секундомер, управление которым будет осуществляться при по мощи кнопок Старт, Стоп и Сброс. Кнопками будет управляться компонент TTimer.

В листинге 4.12 приведен код модуля главной формы сервера автоматизации.

Листинг 4.12. Код модуля главной формы сервера автоматизации var Time : Integer;

Автоматизация.procedure TMainForm.TimerlTimer(Sender: TObject);

begin Time:=Time+l;

TimeLabel.Caption:=IntToStr(Time);

end;

procedure TMainForm.StartBtnClick(Sender: TObject);

begin Timerl.Enabled:-True;

end;

procedure TMainForm.StopBtnC"lick(Sender: TObject);

begin Ti mer1.Enabled:=False;

end;

procedure TMainForm.ResetBtnClick(Sender: TObject);

begin Time:=0;

TimeLabel.Caption:='0';

end;

end.

end.

На рис. 4.17 приведено изображение окна сервера автоматизации.

Рис. 4.17. Сервер автоматизации В листинге 4.13 приведен код объекта автоматизации.

Листинг 4.13. Код модуля unit AutoObject;

{ W R SM O _ L T O M OFF} $ A N Y B LP A F R продолжение 164 Урок 4. Основы технологии C M O Листинг 4.13 (продолжение) interface uses ComObj, ActiveX. MainJLB. StdVcl;

type TSimple = class(TAutoObject, ISimple) protected procedure Start;

safecail;

procedure Stop;

safecall;

procedure Reset;

safecall:

function Get_CurentTimeValue: Integer;

safecall end;

implementation uses ComServ, AutoServProj, SysUtils;

procedure TSimple.Start;

begin MainForm.StartBtn.Click:

end;

procedure TSimpie.Stop:

begin Mai nForm.StopBtn.Click;

end;

procedure TSimple.Reset;

begin Ma i nForm.ResetBtn.Click;

end;

< function TSimple.Get_CurentTimeValue: Integer;

begin Result:=StrToInt(MainForm.TimeLabel.Caption);

end;

Автоматизация initialization TAutoObjectFactory.Create(ComServer, TSimple, Class_Simple, ciMultiInstance, tmApartment);

end.

Легко заметить, что код, показанный в листинге 4.13, похож на код обычного сервера СОМ. В листинге 4.14 приведен код библиотеки типов Листинг 4.14. Код библиотеки типов unit MainJIB;

{STYPEDADDRESS OFF} // Unit must be compiled without type-checked pointers.

{$WARN SYMBOL_PLATFORM OFF} {SWRITEABLECONST ON} {$VARPROPSETTER ON} interface uses Windows, ActiveX. Classes. Graphics, StdVCL. Variants;

/1 *********************************************************************/ / // GUIDS declared in the TypeLibrary. Following prefixes are used:

// Type Libraries : LIBID_xxxx // CoClasses : CLASS_xxxx // DISPInterfaces : DIID_xxxx // Non-DISP interfaces: IID_xxxx /1 *********************************************************************/ const // TypeLibrary Major and minor versions MainMajorVersion = 1;

MainMinorVersion = 0;

LIBID_Main: TGUID = '{E703122F-1A3C-45F3-B150-73CB2A106A74}':

IID_ISimple: TGUID = '{EE729D51-8409-4AAB-9017-BCA15EE86A7B}';

CLASSJimple: TGUID = '{C554CB1E-A957-4DAE-85AF-AD0BA52F32F2}';

type / / *********************************************************************/ / // Forward declaration of types defined in TypeLibrary // **************************************************************** /i ISimple = interface;

продолжение ^ 166 Урок 4. Основы технологии C M O Листинг 4.14 (продолжение) ISimpleDisp = dispi interface;

11 ********************************************************************* II Declaration of CoClasses defined in Type Library / (NOTE: Here we map each CoClass to its Default Interface) / / / ********************************************************************* / / Simple - ISimple;

/1 I":********************************************************************/ I II Interface: ISimple // Flags: (4416) Dual OleAutomation Dispatchable // GUID: {EE729D51-8409-4AAB-9017-BCA15EE86A7B} /1 * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * / ISimple » interface(IDispatch) ['{EE729D51-8409-4AAB-9017-BCA15EE86A7B}'] procedure Stop;

safecall;

procedure Start;

safecall;

procedure Reset;

safecall;

function Get_CurrentTimeValue: Integer;

safecall;

property CurrentTimeValue: Integer read Get_CurrentTimeValue;

end;

/ ******************************************************************* i / II Displntf: ISimpleDisp // Flags: (4416) Dual OleAutomation Dispatchable / GUID:

/ {EE729D51-8409-4AAB-9017-BCA15EE86A7B} / *********************************************************************ii / ISimpleDisp = dispinterface ['{EE729D51-8409-4AAB-9017-BCA15EE86A7B}'] procedure Stop;

dispid 201;

procedure Start;

dispid 202:

procedure Reset;

dispid 203;

property CurrentTimeValue: Integer readonly dispid 204;

end;

/ *********************************************************************;

/ / // The Class CoSimple provides a Create and CreateRemote method to // create instances of the default interface ISimple exposed by Автоматизация // the CoClass Simple. The functions are intended to be used by // clients wishing to automate the CoClass objects exposed by the // server of this Type Library.

CoSimple = class class function Create: ISimple;

class function CreateRemote(const MachineName: string): ISimple:

end:

implementation uses ComObj;

class function CoSimple.Create: ISimple:

begin Result := CreateComObject(CLASS_Simple) as ISimple:

end:

class function CoSimple.CreateRemote(const MachineName: string): ISimple;

begin Result := CreateRemoteComObject(MachineName, CLASS_Simple) as ISimple;

end;

end.

Нужно обратить внимание на тот факт, что для интерфейса ISimple был соз дан диспетчерский интерфейс ISimpleDisp, содержащий список методов с и х идентификационными номерами.

Контроллер автоматизации и пример его реализации Контроллером автоматизации называют любое приложение, которое исполь зует методы сервера автоматизации. Используя библиотеку типов, можно получить информацию об интерфейсах данного сервера. Если библиотека типов создана в Delphi, то можно просто подключить ее модуль. В ином слу чае придется применять импортирование. Для импортирования библиотеки следует использовать команду меню Project • Import Type Library. Появится окно, показанное на рис. 4.18.

В диалоговом окне отображаются зарегистрированные в данной системе биб лиотеки типов. Используя кнопки Add и Remove, можно добавлять или уда лять регистрацию о них. Нужно нажать кнопку Add и в диалоговом окне выб рать файл сервера автоматизации main.exe, который был создан в предыдущем 168 Урок 4. Основы технологии COM разделе. В списке Class names перечисляются объекты данного сервера авто матизации. В поле Palette Page указывается, на какую страницу компонентов будет помещен объект, если будет перемещен в компонентную оболочку. Со ответственно, в поле Unit dir name указывается директория, в которую будут помещаться модули библиотек типов.

Import T p Library ye Import Type Library | M P a 1.0 Tp L r r ( es n A I l y e bay V r o. ) Mi i i m p a 1.0 Tp L r r ( es n a w p y e i ay V r o. ) i b i Mr ue OE C nr l m de ( es n aqe L o to o u V r i. ) l o M t w rs A h n m r LD b G S ( es n ah ok: p a u ec E y M V r o. ) l i i M t w rs A g a Gue b G S ( es n ah ok: n u r ag y M V r o. ) l i | ah ok: Ko b G S ( es n M ! w r s nb y M V r o. ) i I DC p e \ a. exe : e B pMn \ i Add.. Remove Class names: TSimple Palette page: sActiveX Unit dir name: J :

Search path: | Insist., j Create Unit Hp I el generate Component Wrapper Рис. 4.18. Импорт библиотеки типов Щелчок на кнопке Install создает модуль с описанием интерфейсов и автома тически регистрирует в системе все необходимые компоненты. Нажатие на кнопку Create Unit создает модуль библиотек типов с описанием интерфейсов, но не устанавливает его в среду.

Теперь можно перейти к разработке приложения. Как всегда нужно создать новый проект, который можно сохранить с именем ClientProj. Затем потребу ется создать библиотеку типов и зарегистрировать ее в Delphi. Когда будет выбрана необходимая библиотека, нужно нажать кнопку Install. Появится диалоговое окно, предлагающее зарегистрировать компонент в среде. Так и сто ит сделать.

На странице компонентов ActiveX среды разработки появился новый компо нент — TSimple. Его нужно перенести на форму. Его свойству AutoConnect нуж но присвоить значение True, а свойству ConnectKind — ckNewInstance. На форме надо разместить четыре кнопки и один компонент TEdit. На рис. 4.19 показа ны основные окна контроллера и сервера.

В листинге 4.15 приведен код приложения контроллера.

Автоматизация Контроллер автоматизации I Стоп J Старт Сброс Текущее значение :| Секундомер —у. ••':'. :.' Сброс [! i Старт | Стоп Рис. 4.19. Контроллер автоматизации Листинг 4.15. Код модуля контроллера uses Windows, Messages, SysUtils, Variants, Classes, Graphics, Controls, Forms.

Dialogs. OleServer, StdCtrls, MainJIB;

type TForml = class(TForm) StartBtn: TButton;

StopBtn: TButton:

Button3: TButton;

TimeValueEdit: TEdit;

ResetBtn: TButton;

Simplel: TSimple;

procedure ResetBtnClick(Sender: TObject):

procedure StartBtnClick(Sender: TObject);

procedure StopBtnClick(Sender: TObject);

procedure Button3Click(Sender: TObject);

procedure FormCreate(Sender: TObject):

private { Private declarations } public { Public declarations } end;

var Forml: TForml;

продолжение Урок 4. Основы технологии COM Листинг 4.15 (продолжение) implementation {$R *.dfm} procedure TForml.ResetBtnClickCSender: TObject);

begin Simp!el.Reset end;

procedure TForml.StartBtnClick(Sender: TObject);

begin Simplel.Start;

end;

procedure TForml.StopBtnClick(Sender: TObject);

begin Simp!el.Stop;

end:

procedure TForml.Button3Click(Sender: TObject);

begin TimeValueEdit.Text:=IntToStr(Simp!el.CurentTimeValue);

end;

Теперь можно реализовать функциональность сервера автоматизации, подклю чив модуль библиотеки типов напрямую. Для начала следует подготовить проект. Прежде всего нужно удалить с основной формы компонент TSimple.

Затем потребуется объявить библиотеку типов Main_TLB в секции Uses. Также нужно добавить переменную Simple, в которой будет размещен указатель на интерфейс ISimple. Код модуля приведен в листинге 4.16.

Листинг 4.16. Код модуля контроллера uses Windows. Messages. SysUtils, Variants. Classes, Graphics, Controls. Forms, Dialogs. OleServer, StdCtrls. MainJIB;

type TForml = class(TForm) StartBtn: TButton;

Автоматизация StopBtn: TButton:

Button3: TButton;

TimeValueEdit: TEdit;

ResetBtn: TButton;

procedure FormCreate(Sender: TObject);

procedure StartBtnClick(Sender: TObject);

procedure ResetBtnClick(Sender: TObject);

procedure StopBtnClick(Sender: TObject);

procedure Button3Click(Sender: TObject);

private { Private declarations } publi с Simple : ISimple:

{ Public declarations } end;

var Forml: TForml;

implementation {$R *.dfm} procedure TForml.FormCreateCSender: TObject);

begin Si mpl e: =CoSi mpl e. Create 0 ;

end;

procedure TForml.StartBtnClick(Sender: TObject);

begi n Simple. Start;

end;

procedure TForml.ResetBtnClick(Sender: TObject);

begin Simple. Reset end;

продолжение Урок 4. Основы технологии C M O Листинг 4.16 (продолжение) procedure TForml.StopBtnClickCSender: TObject);

begin Simple. Stop;

_end;

procedure TForml.Button3Click(Sender: TObject);

begin TimeVa1ueEdit.Text:=IntToStr(Simple.CurentTimeValue);

end:

На этом можно завершить обзор технологии СОМ, так как были рассмотре ны все основные варианты ее реализации.

УРОК Технология DataSnap Технология DataSnap применяется для создания распределенных систем, со стоящих в общем случае из сервера баз данных, сервера приложения и кли ентского приложения, часто называемого «тонким клиентом». Как правило, тонкий клиент представляет собой «облегченное» приложение, предоставля ющее пользователю возможность получать и редактировать данные. Клиент ское приложение взаимодействует с сервером приложения (Application Server), который напрямую взаимодействует с сервером баз данных, передает ему за просы и возвращает полученные данные тонкому клиенту. В рамках техноло гии DataSnap сервер приложения является сервером СОМ, предоставляющим свои интерфейсы конечным пользователям. На рис. 5.1 представлена схема взаимодействия участников соединения.

Клиент Сервер приложения (Application server) Сервер БД Рис. 5.1. Структура распределенного приложения В общем случае сервер приложения располагается на той же машине, что и сер вер СУБД, но может располагаться и на удаленном сервере.

174 Урок 5. Технология DataSnap Технология DataSnap Как было отмечено ранее, эта технология обеспечивает взаимодействие уда ленных клиентов и серверов, используя для этой цели промежуточный слой.

В качестве промежуточного слоя используется сервер приложения. Этот сер вер взаимодействует с сервером БД, используя одну из технологий доступа к данным, предоставляемых Delphi. Как известно, Delphi позволяет для до ступа к данным использовать ADO, BDE, InterBase eXpress и dbExpress.

Удаленные клиентские приложения строятся на основе компонентов палитры DataSnap. Как правило, клиентские компьютеры являются довольно слабыми си стемами, поэтому использование сервера приложений позволяет существенно снизить нагрузку на них и поднять производительность системы в целом.

Передача данных между сервером приложений и клиентом осуществляется с помощью интерфейса IAppServer, предоставляемого сервером приложения.

Этот интерфейс использует компоненты, основанные на классе TDataSetProvider на стороне клиента и компоненты TClientDataSet на стороне сервера.

Сервер приложения Основой сервера приложений является удаленный модуль данных. Он инкап сулирует все необходимые объекты и интерфейсы для разработки многозвен ных баз данных. Удаленный модуль данных может содержать невизуальные компоненты доступа к данным. Также этот модуль данных может включать в себя готовый удаленный сервер. Удаленный модуль данных осуществляет взаимодействие с клиентскими приложениями при помощи интерфейса IApp Server. Интерфейс предоставляет методы, используя которые, клиентское при ложение может получать и передавать данные. В модуле данных также раз мещаются компоненты-провайдеры TDataSetProvider, связываемые со всеми наборами данных. Они получают данные и передают их компонентам TCI ient DataSet, расположенным на клиентской стороне.

В состав Delphi входят несколько удаленных модулей данных. Они располо гаются на страницах палитры компонентов Multitier, WebServices и WebSnap:

О Компонент CORBA Data Module позволяет реализовать сервер CORBA.

О Компонент Remote Data Module создает сервер автоматизации.

о Компонент Transactional Data Module является расширением Remote Data Module и реализует сервер MTS.

О Компонент WebSnap Data Module позволяет реализовать веб-сервер.

о Компонент Soap Server Data Module используется для создания сервера SOAP (Simple Object Access Protocol).

Клиентское приложение Клиентское приложение строится на базе компонента-соединения, предостав ляющего доступ к серверу приложения через интерфейс IAppServer. В Delphi Технология DataSnap реализовано несколько компонентов соединений, взаимодействующих по раз личным протоколам:

О Компонент TDCOMConnection использует для взаимодействия технологию DCOM.

о Компонент TSocketConnection для той же цели использует сокеты.

О Компонент TWebConnection использует протокол HTTP.

О Компонент TSOAPConnection использует протокол SOAP.

Компоненты TClientDataSet получают данные и работают с ними в режиме кэ ширования. На рис. 5.2 приведена схема, иллюстрирующая порядок работы.

Набор данных Набор данных сервера (TCLientDataSet) к Протокол \ передачи TDataSetProvider TXXXConnection Удаленный модуль Тонкий клиент данных Рис. 5.2. Схема трехзвенного распределенного приложения Компонент TDCOMConnection Технология Microsoft Distributed Component Object Model (DCOM) является расширением COM, позволяющим взаимодействовать объектам СОМ, распо ложенным на разных машинах. Используя эту технологию, взаимодейству ющие объекты СОМ могут функционировать в локальной сети, в Интернете и иных подключенных системах.

Заглушка Proxy-объект Объект СОМ Клиент «CoCreatelnstance» t «CoCreatelnstance» Провайдер Security PRC Provider безопасности OLE Стек протоколов (Удаленная) Активация Рис. 5.3. Структура D O CM 176 Урок 5. Технология DataSnap На рис. 5.3 представлена схема работы технологии DCOM. Одной из цент ральных частей технологии является механизм установления соединения и соз дание нового экземпляра объекта. Клиент вызывает метод CoCreatelnstance, в качестве параметра которому передает идентификатор CLSID требуемого объекта. Если CLSID и имя сервера известны, библиотека СОМ вызывает менеджер служб (Service Control Manager) клиентской машины, который пе редает запрос менеджеру служб сервера и инициирует создание объекта СОМ.

Далее взаимодействие происходит через службу RPC.

Компонент TDCOMConnection используется для создания транспортного канала между клиентским приложением и сервером СОМ на основе технологии DCOM.

В свойстве ComputerName указывается имя компьютера, на котором будет загру жен сервер автоматизации. Если поле не содержит значения, компонент T C M DO Connection полагает, что сервер приложения функционирует на локальной ма шине. Если же это не так, то значение свойства можно получить из системного реестра.

В свойстве ServerName указывается имя сервера приложения, а в свойстве ServerGUID — его идентификатор GUID. Для установления или разрыва со единения следует использовать свойство Connected. Присвоение значения True приводит к установке соединения, a False — к разрыву. Впрочем, для тех же целей можно использовать методы Open и Cl ose.

До и после создания и разрыва соединения инициируются события Before Connect, AfterConnect, BeforeDisconnect и AfterDi sconnect соответственно.

Метод, вызываемый при возникновении события OnLogin, выполняется после открытия соединения, если свойство LoginPrompt имеет значение True. Пара метры Username и Password должны содержать имя пользователя и пароль, ука занные в диалоге авторизации.

При помощи свойства AppServer компонент предоставляет интерфейс IAppServer.

Этот интерфейс обеспечивает взаимодействие между клиентским приложе нием и сервером. Для получения интерфейса можно использовать метод Get Server. Клиентские наборы данных вызывают данный метод для получения значения свойства AppServer.

Компонент TSocketConnection Компонент TSocketConnection реализует соединение клиента с сервером при ложения при помощи сокетов, используя протокол TCP/IP. Для установле ния соединения на стороне сервера приложения должен работать сервер со кетов ScktSrvr.exe. Его окно показано на рис. 5.4.

Компонент TSocketConnection идентифицирует серверную машину по ее IP адресу или имени узла. Для связи с определенным сервером СОМ, располо женным на серверной машине, также необходимо указать порт, который бу дет прослушиваться для получения запроса. По умолчанию в этих целях ис пользуется порт 211. Три соответствующих свойства компонента TSocket Connection позволяют определить эти параметры.

Технология DataSnap Bfif land S c e S re o k t ev r P r Cneos ot oncn s t i Pr ot j Po ets * Ue r pr e sr i s 212 :

•Port listen onPort: |2П -Ч Many values o( Pott are associated by convention wrth a particular service such a* ftp or http. Pott is the ID of the connection on which the server listens for client requests -Thread Caching Thread Cache Size: fiO Thread Cache Si2e is the maximum number of threads that can be reused fox new client connections.

TimeOUt- •--••-• • •• -;

;

;

• ' ' • Inactive Timeout: ]0 ~~i inactive Timeout specifes the number of minutes a client can be inactive before being disconnected (0 indicates infinite) -Intercept GUID -- ;

. ;

-----.---;

.•- —•-.

. GUIO: ]{FAA3a06-EF3A-43D5-BFBB-0953E4iAD16'D} Intercept 6UID is the GUIO for a data interceptor C M object.

O See help for the TSocketCormection for details.

Рис. 5.4. Сервер сокетов В свойстве Address указывается IP-адрес сервера, которому будет послан зап рос на установление соединения с клиентским приложением. В свойстве Host указывается имя сервера. Значением свойства может служить либо сетевое имя сервера, либо его IP-адрес. В свойстве Port необходимо указать номер порта сервера сокетов, который будет прослушиваться диспетчером.

В свойстве ServerGUID указывается идентификатор GUID сервера приложения, с которым будет взаимодействовать клиентское приложение. Если на сервер ной машине существует сервер СОМ с таким GUID, то свойству ServerName автоматически присваивается имя сервера автоматизации.

Для установления или разрыва соединения следует использовать свойство Connected. Присвоение значения True приводит к установке соединения, a Fal se — к разрыву. Впрочем, для тех же целей можно использовать методы Open и Close.

Метод GetServerList позволяет получить список зарегистрированных серве ров. Список возвращается в виде вариантного массива.

В свойстве InterceptGUID определяется идентификатор GUID объекта СОМ, который может манипулировать данными до того, как они будут отосланы клиентскому приложению. Объект служит перехватчиком сообщений между компонентом соединения и сервером приложения. Данный объект СОМ дол-, жен реализовывать интерфейс IDatalntercept. Используя методы данного ин терфейса можно реализовать шифрование пакетов с данными. На стороне сервера тогда необходимо зарегистрировать объект СОМ, выполняющий обрат ную операцию. Для этого тоже используется сервер сокетов, идентификатор 178 Урок 5. Технология DataSnap GUID которого указывается в поле InterceptGUID. Свойство InterceptName со держит программный идентификатор объекта СОМ, манипулирующего пе ресылаемыми данными.

Метод GetlnterceptorList позволяет получить список объектов-перехватчиков, зарегистрированных на сервере.

Свойство AppServer позволяет компоненту предоставлять интерфейс IAppServer, при помощи которого осуществляется взаимодействие между клиентским приложением и сервером. Для получения интерфейса можно использовать метод GetServer. Клиентские наборы данных вызывают данный метод для за полнения свойства AppServer.

Метод CreateTransport создает транспортный канал между клиентским при ложением и сервером приложения. Метод CreateTransport получает в качестве параметров значения свойств Host, Address и Port. Возвращаемое значение имеет тип интерфейса ITransport.

Свойство ObjectBroker содержит указатель на экземпляр класса TSimpleObject Broker, в котором располагается список доступных серверов.

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

В свойстве Servers содержится список серверов, которые могут быть предо ставлены компоненту соединения. Этот список заполняется на этапе разра ботки с помощью специализированно редактора. Значение свойства является коллекцией объектов класса TServerltem, каждый из которых описывает со единение с конкретным сервером. На рис. 5.5 показано окно редактора, в ко тором показаны имена двух соответствующих серверов.

У объектов класса TServerltem в свойстве ComputerName указывается имя ком пьютера, на котором расположен сервер приложения. Значение, содержащее ся в свойстве, зависит от типа компонента соединения, запрашивающего дан ные от брокера. Если используется компонент соединения TOCOMConnection, то значение свойства содержит сетевое имя компьютера. Если применяется TSo cketConnection, то свойство содержит IP-адрес или сетевое имя хоста.

Задать имя сервера можно при помощи свойства Di spl ayName. А в свойстве Port указывается порт, используемый для соединения с сервером приложения по Технология DataSnap протоколу TCP/IP. Значение этого свойства возвращается методом GetPort ForComputer компонента TSimpleObjectBroker.

01 -2...

1 - L c Ae S te o a r a ev t l Рис. 5.5. Редактор списка серверов Когда компонент соединения не может установить связь с сервером прило жения, он информирует об этом компонент TSimpleObjectBroker, который при сваивает свойству HasFailed значение True. А если свойство имеет значение False, то клиентское приложение все же может использовать данный сервер.

При помощи свойства ЕпаЫ ed можно включать в список данный сервер или убирать его из списка.

Свойство LoadBalanced указывает, как компонент TSimpleObjectBroker выбира ет сервер из своего списка. Если свойство имеет значение True, то компонент выбирает сервер случайным образом. Если установить значение False, то бу дет использоваться самый первый доступный сервер.

Метод GetComputerForGUID возвращает строку, в которой содержится сетевое имя компьютера, на котором зарегистрирован сервер с данным GUID. Метод Get ComputerForProglD возвращает строку, в которой содержится имя компьютера, на котором зарегистрирован сервер с данным программным идентификатором.

Компонент TConnection Broker Компонент используется для централизованного соединения клиентских на боров данных с сервером приложения. Связав клиентские наборы данных через свойство ConnectionBroker, можно изменять значение свойства Connection, в ко тором содержится ссылка на соответствующий компонент соединения. Таким образом, вместо того чтобы изменять значения свойств RemoteServer всех ком понентов клиентских наборов данных, в случае изменения протокола переда чи данных достаточно изменить значение в компоненте TConnectionBroker. Этот компонент обеспечивает централизованное использование интерфейса IApp Server и позволяет определять реакцию на сообщения, возникающие при от крытии и закрытии соединения с сервером приложения.

В свойстве Connection указывается компонент соединения. При помощи зна чения свойства AppServer можно обратиться к интерфейсу IAppServer. Также для этой цели можно воспользоваться методом GetServer.

180 Урок 5. Технология DataSnap Компонент TLocalConnection Компонент Tl oca I Connect ion позволяет объявлять соединение между клиент ским набором данных (TClientDataSet) и провайдером (TDataSetProvider), рас положенными в одном модуле данных. Используя свойство AppServer, можно получить доступ к интерфейсу IAppServer. При помощи этого интерфейса можно вызывать методы провайдера данных, до которых в обычной ситуации добраться нельзя из-за ограниченной области видимости. Для тех же целей можно использовать метод GetServer.

Свойство Providers содержит список компонентов-провайдеров, расположен ных втом же модуле данных, что и компонент TLocalConnection. Обратиться к провайдеру можно по его имени, которое указывается в параметре Provider Name. В список помещаются только те компоненты-провайдеры, свойство Expor ted которых имеет значение True.

В свойстве ProviderCount хранится количество провайдеров, на которые содер жатся ссылки в свойстве Providers.

Компонент TSharedConnection Компонент TSharedConnection используется для соединения клиентского при ложения с дочерним модулем данных. Он управляет соединением с дочерним модулем данных. Конечно, клиентские приложения могут напрямую обратить ся к дочернему модулю данных. Но чаще используется единственное соеди нение с главным удаленным модулем данных. Это позволяет клиентскому приложению использовать единое соединение для всех модулей данных. Ес тественно, этот механизм более предпочтителен, чем использование отдель ного соединения для каждого модуля данных. На серверной стороне указа тель на интерфейс дочернего модуля данных содержится в свойстве интер фейса главного модуля данных. Компонент TSharedConnection использует имя в свойстве для указания того, какой дочерний модуль будет использован.

В свойстве ParentConnection указывается компонент соединения, который связан с главным модулем данных сервера приложения. Имя дочернего модуля дан ных задается в свойстве ChildName. Для того чтобы обратиться к интерфейсу IAppServer дочернего модуля данных, можно использовать свойство AppServer.

Также этот интерфейс можно получить от метода GetServer.

Сервер приложения Многозвенные приложения баз данных позволяют организовать эффектив ный доступ удаленных клиентов к данным, используя для этой цели различ ные протоколы. Промежуточным звеном, служащим для взаимодействия меж ду сервером баз данных и клиентским приложением, является сервер прило жения. Основой сервера приложения является модуль данных, осуществля ющий взаимодействие с клиентскими приложениями через интерфейс IApp Сервер приложения Server. За обмен данными с сервером отвечают компоненты соединений и про вайдеры TDataSetProvider, связываемые с наборами данных. На рис. 5.6 приве дена схема работы этого сервера.

Сервер приложения Интерфейс lAppServer Компонент-провайдер TDataSetProvider Набор данных Компонент соединения с сервером баз данных Удаленный модуль данных Рис. 5.6. Структура сервера приложения Как видно из схемы, в модуле данных размещаются компоненты наборов дан ных, связываемые через компонент соединения с базой данных. Также в мо дуль данных помещаются провайдеры TDataSetProvider, которые получают данные от компонентов наборов данных и пересылают их клиентскому при ложению. Как правило, сервер приложения обеспечивает авторизацию пользо вателей, реализует часть бизнес-логики распределенного приложения и обра батывает запросы пользователей, возвращая им результат.

Интерфейс IAppServer Клиентские наборы данных реализуют многие свои свойства и методы при помощи интерфейса IAppServer. Этот интерфейс осуществляет базовое взаи модействие между клиентским набором данных и компонентом-провайдером, через который они передают данные и сохраняют изменения в базе данных.

Когда клиентский набор данных использует интерфейс для соединения с провайдером, в качестве одного из параметров он передает имя провайдера при обращении ко всем его методам.

182 Урок 5. Технология DataSnap По умолчанию интерфейс не сохраняет значений своих свойств, то есть по следующие вызовы функций не могут получить ранее полученный результат.

Им приходится получать его самостоятельно. Поэтому интерфейс не имеет свойств, в которых бы хранилась информация о состоянии. Однако в некото рых случаях клиентские наборы данных получают информацию о положении курсора базы данных для получения следующего пакета записей.

Метод ASExecute выполняет запрос или хранимую процедуру. В параметре CommandText указывается имя хранимой процедуры или текст SQL-запроса.

Метод AS_GetRecords возвращает пакет данных с запрошенными записями.

В параметре Count указывается количество записей, которые должен будет воз вратить метод. Если параметр имеет значение -1, то возвращаются все записи набора данных. Если параметр имеет нулевое значение, то метод вернет мета данные. Любое положительное значение параметра определяет реально -воз вращаемое количество записей. В параметре CommandText указывается SQL запрос, который необходимо выполнить на сервере. Данный параметр будет использован только в том случае, если свойство Options включает значение роАП owCommandText. Параметр TParams позволяет передавать любые параметры, в том числе значения параметров хранимых процедур. В параметре RecsOut возвращается количество переданных записей.

Для получения текущих значений параметров набора данных, ассоциирован ных с провайдером, следует использовать метод AS_GetParams. А для получе ния списка провайдеров удаленного модуля данных следует обратиться к ме тоду AS_GetProviderNames. Полученные значения отображаются в свойстве ProviderName клиентского набора данных.

Метод ASRowRequest возвращает текущую запись набора данных, определен ную в параметре Row.

Для того чтобы сохранить измененные данные в базе, следует вызвать метод ASAppl yUpdates, который передаст их от клиентского набора данных соответ ствующему провайдеру. В параметре Delta содержатся измененные данные, которые будут переданы СУБД для внесения соответствующих записей в ба зу данных. В параметре MaxErrors указывается максимально допустимое ко личество ошибок. При превышении этого параметра обновление будет оста новлено и транзакция будет отменена. В параметре ErrorCount возвращается число ошибок, возникших в процессе внесения изменений.

Удаленный модуль данных Как было сказано ранее, основой сервера приложения является удаленный модуль данных. Он инкапсулирует сервер приложения. В этом разделе будет рассмотрен удаленный модуль данных TRemoteDataModule, реализующий сер вер автоматизации. Для создания удаленного модуля данных нужно выпол нить команду меню File • New • Other. В репозитории объектов нужно перейти на вкладку Multitier и дважды щелкнуть мышью на значке Remote Data Module.

Это активирует диалоговое окно создания удаленного модуля данных, вне шний вид которого показан на рис. 5.7.

Сервер приложения Remote Ьли Module Wizard \ T eslS ervej. 1 CoClass N ame:

,.j Instancing;

| Multiple Instance A\ ! Threading Mode!: ijApartment \ :•••. ". OK ].Cancel Help j :

Рис. 5.7. Создание удаленного модуля данных В поле CoClass Name указывается имя базового интерфейса автоматизации удаленного модуля данных. В листинге 5.1 приведен код удаленного модуля данных.

Листинг 5.1. Код удаленного модуля данных interface uses Windows, Messages, SysUtils, Classes, ComServ, ComObj. VCLCom. DataBkr.

DBClient, ServProj_TLB, StdVcl;

type TTestServer = classdRemoteDataModule, ITestServer) private { Private declarations } protected class procedure UpdateRegistryCRegister: Boolean;

const ClassID, ProgID:

string);

override;

public { Public declarations } end;

implementation {$R *.DFM} class procedure TTestServer.UpdateRegistryCRegister: Boolean;

const ClassID, ProgID: string);

begin if Register then begin продолжение Урок 5. Технология DataSnap Листинг 5.1 (продолжение) inherited UpdateRegistry(Register, ClassID, ProgID);

EnableSocketTransport(ClassID);

EnableWebTransport(ClassID);

end else begin DisableSocketTransport(ClassID);

DisableWebTransport(ClassID);

inherited UpdateRegistry(Register, ClassID, ProgID);

end:

end;

initialization TComponentFactory.Create(ComServer. TTestServer, Class_TestServer, ciMultiInstance, tmApartment):

end.

При запуске удаленный модуль данных регистрируется в системном реестре как сервер автоматизации, для чего используется процедура UpdateRegi stry. За тем вызываются процедуры EnabieSocketTransport и EnableWebTransport, кото рые производят запись в системном реестре о том, что к данному удаленному модулю данных можно получить доступ через сокеты или через Интернет. Со ответственно, процедуры DisableSocketTransport и DisableWebTransport осуще ствляют обратное действие. Экземпляр сервера приложения создается фаб рикой класса, расположенной в секции инициализации. В листинге 5.2 при веден код библиотеки типов.

Листинг 5.2. Код библиотеки типов unit ServProj_TLB:

{STYPEDADDRESS OFF} // Unit must be compiled without type-checked pointers.

{$WARN SYMBOL_PLATFORM OFF} {SWRITEABLECONST ON} {SVARPROPSETTER ON} interface uses Windows, ActiveX, Classes, Graphics, Midas, StdVCL, Variants;

const // TypeLibrary Major and minor versions ServProjMajorVersion - 1;

Сервер приложения ServProjMinorVersion = 0;

LIBID_ServProj: TGUID - '{A51C3468-631B-4C48-A6D9-8371A4F91668}';

IIDJTestServer: TGUID = ' {CCF362F1-132A-4DFB-AFA3-017D47AE25DC}";

CLASSJestServer: TGUID = '{7239CDA0-FEC3-4DFB-85A7-1987817F7B51}';

type /i ********************************************************************* ii II Forward declaration of types defined in Type Library /1 ********************************************************************* ITestServer » interface;

ITestServerDisp = dispinterface;

11 *********************************************************************// // Declaration of CoClasses defined in Type Library // (NOTE: Here we map each CoClass to its Default Interface) 11 *********************************************************************/ j TestServer - ITestServer;

/1 ********************************************************************* ji II Interface: ITestServer // Flags: (4416) Dual OleAutomation Dispatchable // GUID: {CCF362F1-132A-4DFB-AFA3-017D47AE25DC} /1 *********************************************************************/ ITestServer = interface(IAppServer) ['{CCF362F1-132A-4DFB-AFA3-017D47AE25DC}'] end;

I j *********************************************************************/ / // Displntf: ITestServerDisp // Flags: (4416) Dual OleAutomation Dispatchable // GUID: {CCF362F1-132A-4DFB-AFA3-017D47AE25DC} // *********************************************************************// ITestServerDisp = dispinterface ['{CCF362F1-132A-4DFB-AFA3-017D47AE25DC}'] function AS_ApplyUpdates(const ProviderName: WideString;

Delta:

OleVariant;

MaxErrors: Integer;

продолжение 186 Урок 5. Технология OataSnap Листинг 5.2 (продолжение) out ErrorCount: Integer;

var OwnerData:

OleVariant): OleVariant;

dispid 20000000;

function AS_GetRecords(const ProviderName: WideString;

Count: Integer;

out RecsOut: Integer;

Options: Integer;

const CommandText: WideString;

var Params: OleVariant;

var OwnerData: OleVariant): OleVariant;

dispid 20000001;

function AS_DataRequest(const ProviderName: WideString;

Data: OleVariant):

OleVariant;

dispid 20000002;

function AS_GetProviderNames: OleVariant;

dispid 20000003;

function AS_GetParams(const ProviderName: WideString;

var OwnerData:

OleVariant): OleVariant;

dispid 20000004;

function AS_RowRequest(const ProviderName: WideString;

Row: OleVariant;

RequestType: Integer;

var OwnerData: OleVariant): OleVariant;

dispid 20000005;

procedure AS_Execute(const ProviderName: WideString;

const CommandText:

WideString;

var Params: OleVariant;

var OwnerData: OleVariant);

dispid 20000006;

end:

CoTestServer = class class function Create: ITestServer;

class function CreateRemote(const MachineName: string): ITestServer;

end;

implementation uses ComObj;

class function CoTestServer.Create: ITestServer;

begin Result := CreateComObjectCCLASS_TestServer) as ITestServer:

end;

class function CoTestServer.CreateRemote(const MachineName: string):

ITestServer;

begin Сервер приложения Result := CreateRemoteComObjecKMachineName, CLASS_TestServer) as ITestServer;

end;

end.

Как было отмечено ранее, при создании сервера автоматизации для него соз дается интерфейс ITestServer. Данный интерфейс является наследником ин терфейса IAppServer и наследует ряд его методов. Так как удаленный мо дуль данных реализует сервер автоматизации, дополнительно к дуальному интерфейсу ITestServer был создан диспетчерский интерфейс ITestServerDisp.

Создание экземпляров класса осуществляется методами Create и CreateRemote.

Оба метода возвращают ссылку на основной интерфейс класса. В данном слу чае возвращается ссылка на ITestServer.

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

Как было сказано ранее, для того чтобы получить доступ к дочернему моду лю данных через компонент TSharedConnection, необходимо возвращать указа тель на него. Это реализуется довольно просто. К базовому интерфейсу при мера к интерфейсу ITestServer, добавляется свойство SecModule типа Readonly.

На вкладке Attributes в поле Туре необходимо выбрать базовый интерфейс до чернего модуля данных, на который будет получен указатель. На рис. 5.8 при веден пример выбора правильного указателя.

i мшнллсь ^ raramaeis \ nags j iext ITestServer '% SecModule. Name: jSecModule ISecondModule TestServei |ЗОГ ID:

SecondModule Type:

Picture " SAFEARRAY(long) SCODE Рис. 5.8. Получение указателя на дочерний модуль данных Далее на вкладке Parameters появится новый параметр. В поле Name указыва ется произвольное имя параметра, в поле Туре будет содержаться ссылка на интерфейс ISecondModule **, а в поле Modifier будут указаны флаги Out и Retval.

В листинге 5.3 приведен код реализации свойства SecModul e.

Листинг 5.3. Код свойства SecModule function TTestServer.Get_SecModule: ISecondModule;

begin продолжение •& Урок 5. Технология DataSnap Листинг 5.3 (продолжение) Result:=CoSecondModule.Create;

end;

Как видно из приведенного кода, свойство возвращает указатель на интер фейс ISecondModule. Для получения указателя необходимо вызвать метод Create соответствующего кокласса. Теперь в свойстве ChildName компонента TShared Connection можно выбрать значение SecModule и получить доступ к дочернему модулю данных SecondModule.

Провайдеры данных Провайдер TDataSetProvider предназначен для получения данных от сервера БД и передачи их клиентскому приложению. Как правило, данный компонент располагается в удаленном модуле данных сервера приложения и является частью трехзвенной архитектуры. Компонент TDataSetProvider упаковывает получаемые данные и пересылает их в виде пакетов клиентскому набору дан ных TClientDataSet. Когда клиентский набор данных получает пакет сданны ми от сервера, он помещает их в оперативную память. Если пользователь модифицировал данные и отсылает их на сервер, компонент TDataSetProvider распаковывает их и при помощи транзакций вносит в базу данных необходи мые изменения. Клиентские наборы данных взаимодействуют с компонентом провайдером через интерфейс IAppServer.

В свойстве DataSet указывается набор данных, из которого компонент TData SetProvider будет получать данные и вносить в них определенные изменения.

Свойство Resol veToDataSet позволяет определить режим внесения изменений в базу данных. Когда свойство имеет значение True, изменения вносятся в свя занный с компонентом TDataSetProvider набор данных и только потом в саму базу данных. Если свойство имеет значение False, изменения, переданные компоненту-провайдеру, вносятся напрямую в базу данных, минуя связанный набор данных. В этом режиме несколько увеличивается скорость работы при ложения.

Для того чтобы разрешить или запретить клиентским приложениям исполь зовать интерфейс IAppServer, следует использовать свойство Exported. Когда используется значение True, клиентские приложения могут напрямую обра щаться к провайдеру, а в ином случае это запрещается.

В свойстве Constraints указывается, будут ли переданы ограничения от сер верного набора данных в клиентское приложение. Для передачи ограничений необходимо присвоить свойству значение True.

Свойство Options позволяет определить параметры взаимодействия между клиентским набором данных и компонентом-провайдером. Возможные значе ния свойства перечислены в списке:

О Значение poFetchBiobsOnDemand указывает, что содержимое BLOB-полей не включается в пакет с данными. Вместо этого при необходимости клиенте Сервер приложения кое приложение должно запрашивать их. Если свойство FetchOnDemand ком понента TClientDataSet имеет значение True, то запрос производится авто матически. В ином случае клиентское приложение должно вызывать ме тод FetchBlobs.

О Значение poFetchDetailsOnDemand указывает, что когда провайдер представ ляет в отношениях ссылочной целостности родительскую таблицу, то вло женные таблицы не включаются в пакет данных. В случае необходимости клиентские приложения запрашивают записи связанных таблиц. Если свой ство FetchOnDemand компонента TCI ientDataSet имеет значение True, то клиент ское приложение запрашивает эти данные автоматически, иначе для полу чения данных вложенных таблиц необходимо вызывать метод FetchDetails.

О Значение poIncFieldProps указывает, что в пакет данных включается инфор мацияо свойствах Alignment, DisplayLabel, DisplayWidth, Visible, DisplayFormat, EditForraat, MaxValue, MinValue, Currency, EditMask и DisplayValues, присущих каждому полю.

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

О Значение poCascadeUpdates указывает, что если таблицы сервера находятся в отношении ссылочной целостности, то при изменении значения ключе вого поля в главной таблице автоматически изменяются его значения в под чиненных.

О Значение poReadOnl у указывает, что набор данных сервера переводится в ре жим «только для чтения».

О Значение poAl I owMul tiRecordUpdates включает режим одновременного изме нения нескольких записей. Если свойство имеет значение Fai se, то пакеты обновлений с несколькими записями будут автоматически забракованы.

О Значение poDisablelnserts указывает, что клиенты не могут добавлять но вые записи.

О Значение poDi sabl eEdits не позволяет клиентам вносить в записи изменения.

О Значение poDisableDeletes указывает, что клиенты не могут удалять записи.

О Значение poNoReset сбрасывает флаг Reset, требующий, чтобы записи в па кете данных начинались с первой. Данный флаг устанавливается в пара метре Options метода ASGetRecords интерфейса IAppServer.

О Значение poAutoRefresh включает автоматическое обновление записей кли ентского набора данных.

О Значение poPropogateChanges указывает, что изменения, сделанные в мето дах, обрабатывающих события BeforeUpdateRecord и AfterUpdateRecord, отсы лаются обратно клиентскому набору данных и сливаются с ним.

190 Урок 5. Технология DataSnap О Значение poAl I owCommandText позволяет выполнять SQL-запросы, определя емые клиентским приложением, то есть выполнять хранимые процедуры и запросы с параметрами.

О Значение poRetainServerOrder запрещает клиентскому приложению изме нять порядок сортировки записей.

До и после того как провайдер сохранил изменения, полученные от клиент ского набора данных, в базе инициируются события BeforeApplyUpdates nAf terApplyUpdates.

Тип TRemoteEvent обеспечивает работу механизма обмена сообщениями между клиентскими наборами данных и компонентами-провайдерами. Взаимодей ствие осуществляется поверх методов интерфейса IAppServer. Перед тем как команда будет передана компоненту-провайдеру, будет отослано соответству ющее сообщение.

Перед тем и после того как будет выполнен SQL-запрос или хранимая проце дура, возникают события AfterExecute и BeforeExecute. А события AfterGetRecords, AfterGetParams, BeforeGetRecords и BeforeGetParams инициируются до и после того, как будут отправлены пакеты с записями и их параметры.

Событие BeforeRowRequest вызывается до того, как провайдер создаст пакет с дан ными о текущей записи (или блоке записей), а событие AfterRowRequest иниции руется после того, как был отослан пакет с запрашиваемыми записями.

В момент поступления запроса на получение данных вызывается метод-обра ботчик события OnDataRequest. А перед моментом обновления единичной за писи на сервере и после него инициируются события BeforeUpdateRecord и Af terUpdateRecord.

Метод-обработчик события OnGetData вызывается уже после того, как компо нент-провайдер получил данные от сервера, но еще перед отсылкой их клиент скому приложению. А при сохранении изменений в наборе данных сервера вызывается метод-обработчик OnUpdateData. Если во время сохранения изме нений на сервере произошла ошибка, то инициируется событие OnUpdateError.

Метод-обработчик события OnGetTabieName вызывается, когда компонент-про вайдер получает имя таблицы, в которую будут внесены изменения. А собы тие OnGetDataSetProperties возникает, когда провайдер добавляет дополнитель ную информацию в создаваемый пакет данных.

Пример разработки сервера приложения Для создания сервера приложения нужно открыть новый проект и добавить в него удаленный модуль данных. Создаваемый класс должен получить имя TestServer, под которым его и нужно сохранить.

В модуле данных надо разместить компоненты TADOConnection, TADOTabie и TData SetProvider с именем Customer. В компоненте TADOConnection надо настроить соединение с базой данных dbdemos. Свойству IsolationLevel требуется при своить значение iChaos. Компоненты TADOTable и TADOConnection надо связать Сервер приложения между собой и свойству TableName последнего присвоить значение Customer.

Также потребуется связать компоненты TADOTable и TDataSetProvider.

В проект нужно добавить еще один дочерний удаленный модуль данных с име нем SecondModule. В нем нужно создать тот же набор компонентов, что и в ро дительском модуле. Через компонент TADOConnection следует установить со единение с СУБД SQL Server 2000 и открыть базу данных Northwind. Свой ство IsolationLevel должно получить значение iChaos. Также потребуется ус тановить соединение компонента TADOTable с базой данных через его свойство Connection и связь с компонентом-провайдером TDataSetProvider, получившим имя DSPEmpl оеуе.

На форме надо расположить компонент TDataSource, который потребуется связать с компонентом ADOTablel. Также нужно добавить второй компонент TADOTable. Он должен быть связан с компонентами TADOConnection и TData SetProvider. В свойстве TableName надо выбрать таблицу Customers и установить соединение с базой, присвоив свойству Active значение True.

Теперь требуется настроить отношения ссылочной целостности. В свойстве MasterSource компонента AD0Table2 нужно выбрать компонент TDataSource, а в свойстве MasterFields установить связь по полям CustomerlD и EmpLoyeelD. После этого можно перейти в редактор библиотеки типов, выполнив команду меню View • Type Library.

К интерфейсу ITestServer нужно добавить два метода и свойство типа Read only.

Через это свойство интерфейсу ITestServer будет передан указатель на интер фейс ISecond. Таким образом, к нему можно будет обратиться через компонент TSharedConnecti on.

В методах будет реализован подсчет количества клиентов, работающих в этот момент с базой данных. Первый метод AddUser будет увеличивать счет чик, а метод DeleteUser, соответственно, уменьшать значение счетчика. Для этого нужно разместить на форме компонент TTimer и несколько компонен тов TLabel.

В листинге 5.4 приведен код главного удаленного модуля данных. Для реги страции сервера приложения достаточно просто запустить его. При распро странении приложений на стороне сервера будет необходимо зарегистриро вать библиотеку midas.dll командной regsvr32 midas.dll.

Листинг 5.4. Код главного модуля данных class procedure TTestServer.UpdateRegistry(Register: Boolean;

const ClassID, ProgID: string);

begin if Register then begin inherited UpdateRegistry(Register. ClassID. ProgID);

EnableSocketTransport(ClassID);

продолжение & Урок 5. Технология DataSnap Листинг 5.4 (продолжение) EnableWebTransport(ClassID);

end else begin DisableSocketTransport(Class ID);

DisableWebTransport(ClassID);

inherited UpdateRegistry(Register, ClassID, ProgID);

end;

end;

function TTestServer.Get_SecModule: ISecondModule;

begin Result:=CoSecondModule.Create;

end;

procedure TTestServer.AddUser;

var Count : Integer;

begin Count:=StrToInt(ServerForm.Label 4.Caption);

ServerForm.Label4.Caption:=IntToStr(Count+l);

end;

procedure TTestServer.DeleteUser;

var Count : Integer;

begin Count:=StrToInt(ServerForm.Label 4.Capti on);

ServerForm.Label4.Caption:=IntToStr(Count-l);

end;

function TTestServer.GetJZountsUser: Integer;

begin Result:=StrToInt(ServerForm.Label 4.Capti on);

end;

initialization TComponentFactory.Create(ComServer, TTestServer, Class_TestServer. ciMultiInstance, tmApartment):

end.

Клиентское приложение На рис. 5.9 показано окно сервера приложения.

В данный момент 21:39: й пользователей.

с сервером работают Рис. 5.9. Окно сервера приложения Клиентское приложение Как правило, в многозвенных базах данных клиентское приложение не исполь зует много ресурсов и называется тонким клиентом. На клиентское приложе ние возлагаются функции представления данных в удобном виде и организа ции хорошего интерфейса, предназначенного для работы с ними. Клиентское приложение через интерфейс IappServer взаимодействует с сервером приложе ния, получает от него запрашиваемые данные и возвращает измененные. Осно вой клиентского приложения является компонент TClientDataSet, который взаимодействует с компонедтом-провайдером TDataSetProvider. Соединение с удаленным модулем данных обеспечивают компоненты TSocketConnection иTDCOMConnection. Данные, получаемые компонентом TClientDataSet, хранятся в кэше. Таким образом, пользователь работает именно с кэшем, используя ме ханизм отложенного обновления. Как и обычное приложение баз данных, кли ентское приложение содержит компоненты отображения данных и компонен ты пользовательского интерфейса. На 5.10 показана соответствующая схема.

Сервер приложения lAppServer Клиентское приложение TSoketConnection или TDCOMConnection TetaSt CDt e iln a • •/ КЭШ J А v TDataSource Компоненты отображения данных Рис. 5.10. Структура клиентского приложения 7 Зак. 194 Урок 5. Технология DataSnap Компонент TClientDataSet Компонент TClientDataSet представляет собой набор данных, размещаемый в памяти. Клиентский набор данных, который реализует компонент TClient DataSet, может быть использован как полнофункциональный автономный набор данных, хранилищем данных для которого является файл. Подобная функ циональность обычно применяется в однозвенных приложениях баз данных.

Иногда подобный набор данных используется локально в оперативной памя ти, получая данные от другого набора данных. Эта функциональность приме няется в многозвенных приложениях баз данных.

Класс TClientDataSet является потомком класса TDataSet, поэтому наследует свойства базового набора данных. Соединение с удаленным модулем данных осуществляется при помощи свойства RemoteServer, в котором указывается соответствующий компонент соединения.

В свойстве ProviderName выбирается компонент-провайдер, который передает в компонент TClientDataSet данные от сервера и возвращает ему внесенные изменения.

После того как были произведены все необходимые настройки, можно уста новить соединение с базой данных. Для этого следует присвоить свойству Active значение True или вызвать метод Open.

После установления соединения с удаленным модулем данных через свойство AppServer можно получить доступ к интерфейсу IAppServer, при помощи кото рого вызываются методы удаленного модуля данных.

Используя свойство PacketRecords, можно указать компоненту-провайдеру, какое количество записей включать в один пакет сданными. Если свойство имеет значение -1 по умолчанию, то все записи будет переданы в клиентский набор в одном пакете в момент его открытия. Нулевое значение свойства за ставляет передавать только метаданные базы данных. Если свойство содер жит положительное значение, то оно будет указывать число записей, поме щаемых в пакет.

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

По умолчанию свойство FetchOnDemand имеет значение True. Это значит, что клиентский набор данных получает дополнительные пакеты данных по тре бованию. Например, при прокрутке значений в компоненте TDBGrid будет осу ществляться их загрузка.

До и после получения записей инициируются события BeforeGetRecords и AfterGetRecords.

Для получения содержимого полей BLOB в тех случаях, когда информация не передается автоматически, можно вызвать метод FetchBlobs. Также для получения записей связанных наборов данных, когда они не передаются ав томатически, используется метод FetchDetails.

Клиентское приложение Используя свойство CommandText можно указать SQL-запрос, имя таблицы или хранимой процедуры, которые будут переданы на сервер. Для выполнения запроса следует вызвать метод Execute, который инициирует выполнение со ответствующей команды. Метод может быть использован в тех случаях, ког да запрос или хранимая процедура не возвращают набор данных. Для того чтобы компонент-провайдер мог использовать данную возможность, необхо димо присвоить значение True параметру poAlIowCommandText свойства Options компонента TDataSetProvider.

До того как запрос будет выполнен на сервере, он пересылается провайдеру.

В результате инициируется событие BeforeExecute. После того как запрос бу дет выполнен, вызывается метод-обработчик события AfterExecute. А при по мощи свойства Params запросы и хранимые процедуры передают свои пара метры серверу.

Получить текущие значения параметров компонента-провайдера можно при помощи метода FetchParams. На этапе разработки параметры можно просмот реть, выполнив команду Fetch Params контекстного меню компонента TClient DataSet.

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

До и после сохранения изменений в базе данных инициируются события Befo reApplyUpdates и AfterApplyUpdates. Все внесенные в кэшируемый набор данных изменения сохраняются в специальном буфере, доступ к которому можно получить через свойство Delta. Количество записей, содержащихся в буфере Delta, указано в свойстве ChangeCount. Метод ApplyUpdates передает информа цию из этого буфера компоненту-провайдеру. Для того чтобы очистить свой ство Del ta и локально отменить изменения, можно воспользоваться методом Cancel Updates. Метод RevertRecord по своему действию аналогичен методу Cancel Updates, но используется только для отката изменений текущей записи.

Для того чтобы получить копию данных с сервера, необходимо вызвать ме тод Ref reshRecord, который произведет обновление всех записей, содержащихся в клиентском наборе данных. Все внесенные пользователем изменения будут потеряны. Перед обновлением записей инициируется событие BeforeRowRequest, а после обновления — AfterRowRequest.

Каждая запись клиентского набора данных имеет определенный статус, кото рый возвращается методом UpdateStatus. Возвращаемое значение принадлежит к перечислимому типу. В списке указаны возможные значения:

О Значение usUnmodified указывает, что запись не была изменена.

о Значение usModified указывает, что запись была изменена.

О Значение us Inserted указывает, что запись была добавлена, но на данный момент не сохранена в базе данных.

196 Урок 5. Технология DataSnap О Значение usDeleted указывает, что данная запись была удалена, но измене ния не были сохранены в базе данных, а содержаться в локальном кэше.

Каждое поле в клиентском наборе данных может иметь три значения. Пер вым является базовое значение, то есть то, которое хранилось в локальной копии до начала редактирования. Второе возможное значение — текущее. Это то значение, которое указывает пользователь. Третьим значением является измененное и подтвержденное пользователем значение. В свойстве OldValue содержится базовое значение поля. В свойстве CurValue содержится текущее значение поля, включающее в себя изменения, сделанные другими пользова телями базы данных. Значение данного поля используется в тех случаях, ког да возникают ошибки сохранения данных. Например, значение поля могло быть изменено другой транзакцией и не может быть изменено текущей в си лу налагаемых ограничений. А значение поля, которое будет сохранено, со держится в свойстве NewVal ue.

Компонент TClientDataSet позволяет сохранять данные в файле или в потоке, а затем читать их оттуда. Используя данное свойство компонента, можно орга низовывать простые файловые СУБД и даже обмен данными на сменных носителях, копируя часть записей во временный набор. Имя файла, в кото ром будет сохранен набор данных, указывается в свойстве FileName. Для за грузки набора данных из файла и записи данных в него применяются методы LoadFromFile и SaveToFile. В параметре FileName указывается имя файла, в ко тором будет храниться набор данных. Если параметру не присвоено значе ние, то оно будет взято из свойства FileName. В параметре Format определяется формат, в котором будет сохранен набор данных. Существуют три возмож ных значения формата:

О Значение dfBi nary указывает, что будет использоваться двоичный формат данных.

О Значение dfXML указывает, что будет использоваться формат XML.

О Значение dfXMLUTF8 указывает, что будет использоваться формат данных XML в кодировке UTF8.

Для сохранения набора данных в потоке и чтения из него предназначены методы SaveToStream и LoadFromStream соответственно. В параметре Stream ука зывается имя потока.

Как и обычный компонент набора данных, компонент TClientDataSet имеет возможность работать с индексами. Для создания нового индекса следует воспользоваться методом Addlndex. В параметре Name указывается имя созда ваемого индекса, а в параметре Fields перечисляются входящие в него поля.

Используя параметр Options, можно задать необходимые атрибуты индекса.

Возможные значения этого параметра перечислены ниже:

О Значение ixPrimary указывает, что индекс является первичным.

О Значение ixUnique указывает, что каждое значение индекса уникально.

О Значение ixDescending указывает, что индекс будет сортировать записи в по рядке убывания.

Клиентское приложение О Значение ixExpression указывает, что индекс основывается на использова нии ключевого выражения. Эта возможность применяется только для таб лиц Dbase.

о Значение ixCaselnsensitive указывает, что индекс сортирует записи, не учитывая регистр символов.

О Значение ixNonMaintained указывает, что индекс не будет автоматически перестраиваться при изменении данных.

В параметре DescFields указывается список полей, по которым будет произ водиться сортировка в порядке убывания. Фактически, этот параметр дубли рует значение ixDescending параметра TIndexOptions. В параметре CaselnsFields указывается список полей, по которым будет производиться сортировка. Но при этом регистр символов учитываться не будет. В параметре GroupingLevel указывается уровень группировки записей.

Для того чтобы подключить созданный индекс или сделать его активным, следует указать его имя в свойстве IndexName. В свойстве IndexFieldNames через точку с запятой указывается список полей, которые будут использоваться в ка честве индекса. Свойства IndexName и IndexFieldNames'ne могут использоваться одновременно.

Для получения информации об индексах клиентского набора данных можно использовать свойство IndexDefs. Используя свойство IndexFields, можно по лучить список полей, входящих в текущий индекс. Для получения их коли чества следует обратиться к свойству IndexFieidCount. Для получения списка индексов набора данных можно использовать метод GetlndexNames.

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

Для обеспечения связи родительской и дочерней таблиц в свойстве MasterSource необходимо выбрать компонент TOataSource, указывающий на набор данных, представляющий главную таблицу. После этого можно определить связи че рез свойство MasterFields.

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

Метод, выполняющийся при возникновении этого события, имеет несколько параметров. В параметре DataSet содержится ссылка на клиентский набор данных, в котором произошла ошибка. Если ошибка произошла в подчинен ном наборе, то метод главного набора данных вернет в параметре DataSet ука Урок 5. Технология DataSnap затель на подчиненный набор. Получить доступ к значениям данной записи можно при помощи свойств CurValue, OldValue и NewValue. В параметре Е содер жится указатель на объект класса исключения EReconcileError, предоставля ющий информацию об ошибке. В параметре UpdateKind содержится описание причины ошибки. Параметр может принимать три значения:

о Значение ukModify указывает, что ошибка возникла при изменении данных.

о Значение uklnsert свидетельствует, что добавление новой записи привело к ошибке.

О Значение ukDel ete указывает, что ошибка произошла при удалении записи.

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

О Значение raSkip отменяет операцию сохранения для текущией записи и воз вращает ее в буфер записей.

О Значение raAbort полностью отменяет операцию сохранения для записи.

О Значение гаМегде объединяет значение вызвавшей ошибку записи со зна чением записи на сервере.

О Значение raCorrect повторно сохраняет изменения.

О Значение raCancel отменяет сделанные изменения и устанавливает полям их текущие значения.

О Значение raRefresh отменяет все сделанные изменения, присваивая полям их базовые значения, то есть значения полей, хранящиеся на сервере.

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

В листинге 5.5 приведен пример использования данного метода.

Листинг 5.5. Пример использования метода HandleReconcileError procedure TForml.ClientDataSetReconcileError(DataSet: TCustomCiientDataSet;

E:

EReconcileError;

UpdateKind: TUpdateKind;

var Action TReconcileAction);

begin Action := Hand1eReconci1eError(DataSet, UpdateKind, E):

end;

На рис 5.11 показано диалоговое окно, вызываемое этим методом.

Так же как и обычные компоненты наборов данных, компонент TClientDataSet наследует методы-обработчики, вызывающиеся при модификации данных, содержащихся в кэше. При возникновении ошибки удаления записи иниции руется событие OnDeleteError. В случае возникновения ошибки ввода новой Клиентское приложение записи или модификации существующей записи вызывается метод-обработ чик OnPostError. Событие OnEditError инициируется, если приложение пыта ется модифицировать данные.

С Caned ( i l to o P M R KY c n ta t P _ u t ms Cn o i s r Г" Correct Voai n f R A Y E o sr i ' KC so «. a n t n et I n ' I u E ae k y in o j c " ut m r' d p ct e b t C s es e o С Refresh Г* Merge Fed Nm il a e I s re V l e..... _:.......

n et d a u Conflicting Value Os mr • ut el oD Cmay a e o pnNm Тестовая строка1 Тестовая строка Show changedfieldsonjji Show conflicting fields only Cancel Рис. 5.11. Окно обработки ошибок сохранения данных в БД Соответствующие методы содержат указатель на тип TDataSetErrorEvent. Тип TDataSetErrorEvent похож на тип TReconcileErrorEvent. Параметр DataSet ука зывает на набор данных, в котором произошла ошибка. В параметре Е содер жится указатель на объект класса исключения EDatabaseError, с помощью ко торого можно получить информацию об ошибке. В параметре Action можно определить реакцию на возникшую ошибку. Параметр может принимать не сколько строго определенных значений:

О Значение daFail приводит к отмене операции и отображению сообщения об ошибке.

О Значение daAbort отменяет операцию без отображения сообщения об ошибке.

О Значение daRetry приводит к повтору операции. В этом случае следует написать обработчик, который будет позволять корректировать введенные данные.

Пример разработки клиентского приложения с использованием DCOM К новому проекту DCOMPrj следует добавить модуль данных. В нем надо раз местить три компонента TClientDataSet, три компонента TDataSource и компо ненты TDCOMConnection, TSimpleObjectBroker, TSharedConnection и ConnectionBroker.

В свойстве Connection компонента TConnectionBroker нужно выбрать соответ ствующий компонент TDCOMConnection. Это поможет связать с ним все компо ненты TClientDataSet. В случае изменения настроек соединения не придется 200 Урок 5. Технология DataSnap перенастраивать все компоненты по одному. Достаточно будет изменить только компонент TConnectionBroker.

Для компонента TSimpleObjectBroker нужно выбрать свойство Servers, добавить новый экземпляр коллекции и в его свойстве ComputerName указать имя компью тера, как это показано на рис. 5.12.

| m l O e t r k 1S v r S p b cBo e. ee iei ii I Po et s ^ E et ] r p ri e v rs Eae nbdl I Tu ie Pr ot i Рис. 5.12. Настройка компонента TSimpleObjectBroker У компонента TDCOMConnection в свойстве ObjectBroker нужно выбрать компо нент TSimpleObjectBroker. В его свойстве ServerGUID следует указать GUID-иден тификатор объекта.

В списке ServerName надо выбрать удаленный модуль данных ServProj.TestServer.

В списке доступен также другой модуль данных — ServProj.SecondModule. Но в данном случае будет использоваться только один компонент соединения, доступ к которому будет осуществляться при помощи компонента TShared Connection. Его свойству LoginPrompt нужно присвоить значение False, а свой ству Connected — значение True.

Теперь необходимо выбрать компонент TSharedConnecti on. В его свойстве Parent Connection нужно указать компонент TDCOMConnection, а в свойстве ChildName выбрать из списка значение SecModule. Это значение является именем свой ства, возвращающего указатель на дочерний модуль данных.

В свойстве RemoteServer компонента ClientDataSetl нужно выбрать компонент TConnectionBroker, а в свойстве ProviderName — значение Customer. Свойство Active должно получить значение True. После этого нужно связать компонент Cl ient DataSetl с компонентом DataSourcel.

В свойстве RemoteServer компонента ClientDataSet2 нужно выбрать компонент TSharedConnecti on, а в свойстве ProviderName указать таблицу DSPEmploeey. Этот компонент нужно связать с DataSource2. Точно так же следует настроить ком понент Cl ientDataSet3, указав в его свойстве ProviderName значение DSPCustomers.

Теперь нужно определить отношения ссылочной целостности. В свойстве MasterSource компонента ClientDataSet3 нужно выбрать компонент DataSource2, а потом установить связь между таблицами в свойстве MasterFields по полям EmployeelD и Customer ID. Оба компонента следует активировать.

На главной форме надо разместить три компонента TDBGrid и три кнопки.

Модуль данных следует объявить в модуле главной формы приложения. Ком Клиентское приложение поненты TDBGrid нужно связать с компонентами TDataSource. В примере пока зана связь таблиц и работа с ними. На рис. 5.13 изображено работающее при ложение. Код главной формы приложения приведен в листинге 5.6.

CsoCmay u jo p NI n {MduS ' JAddri" " • " 61;

ne ar S B C py :921 Everglades Way 25 dr t C A o a P0BoxSn U we U mn 61 i Auc D a 32 qa a tr im 61;

Te Dg C py ;

P0 Box 56 h v o a ;

P0 Box ii mn n 68 ;

o ee S B L td I7865NE Barter CI.

52 Nr sr C A m wt U i e ' i 61: arpu SUA Cnr ;

POBox 82 tsot CB et We e 94 Js 81 et т 9842:Тестовая строка :

"":: ^i* " - "• " ••" • -г- ••• •• ' JFirstName JTM» EmployeelD JLastName ;

4:Peacock1 i Margaret Sales Representative Mrs.

- : Steven Sales Manager :Mr' 5;

Buchanan : Michael Sates Representative Mr.

6iSuyama 7 i King :Sales Representative Mr..., Robert Inside Sales Coordinator Ms. ;

j Laura 8'ICdiahari Ms. Щ —. Sales Representative ;

Anne SlDodswortH yu 10 Ц > jCustomeflDfCcimpariyName | C o n t a c t a m e i^^Q :1 естовая строка • Ш Удалить Сохранить Принять В данный момент 23:37: 1. с сервером работают 2 пользователей.

Рис. 5.13. Трехзвенное приложение с двумя клиентами Листинг 5.6. Код клиентского приложения uses ConnectionModule, Math;

{$R *.dfm} procedure TMainForm.FormShow(Sender: TObject);

продолжение •& 202 Урок 5. Технология DataSnap Листинг 5.6 (продолжение) begin ConnectModule.DCOMConnecti on.AppServer.AddUser;

end:

procedure TMainForm.SpeedButtonlClick(Sender: TObject);

begin if ConnectModule.ClientDataSetl.State in [dsInsert, dsEdit] then begin ConnectModule.Cli entDataSetl.Post;

end;

if ConnectModule.ClientDataSet2.State in [dslnsert, dsEdit] then begin ConnectModule.Cli entDataSet2.Post;

end;

end;

procedure TMainForm.SpeedButton2Click(Sender: TObject);

begin / if DBGridl.Tag - 1 then begin ConnectModule.Cli entDataSetl.Del ete;

end;

if DBGrid2.Tag = 1 then begin ConnectModule.Cli entDataSet2.Del ete;

end;

if DBGrid3.Tag = 1 then begin ConnectModule.Cli entDataSet3.Del ete;

end;

end:

procedure TMainForm.SpeedButton3Click(Sender: TObject);

begin ConnectModule.ClientDataSetl.ApplyUpdates(-1);

ConnectModule.Cli entDataSet2.ApplyUpdates(-1);

ConnectModule.Cli entDataSet3.ApplyUpdates(-1);

end:

procedure TMainForm.FormCloseCSender: TObject;

var Action: TCloseAction);

begin ConnectModule.DCOMConnecti on.AppServer.Del eteUser;

end;

Клиентское приложение procedure TMainForm.DBGridlCellClickCColumn: TColumn);

begin DBGridl.Tag:=l;

DBGrid2.Tag:=0;

DBGrid3.Tag:=0;

end;

procedure TMainForm.DBGrid2CellC]ick(Column: TColumn);

begin DBGridl.Tag:=O;

DBGrid2.Tag:=l;

DBGrid3.Tag:=0;

end;

procedure TMainForm.DBGrid3Cen Click (Col umn: TColumn);

begin DBGridl.Tag:=O:

DBGrid2.Tag:=0:

DBGrid3.Tag:=l;

end;

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

Пример разработки клиентского приложения с использованием сокетов Теперь можно разработать то же самое приложение, но связь с сервером при ложения устанавливать при помощи сокетов. Для начала следует запустить сервер сокетов scktsrvr.exe и выбрать для работы какой-либо порт. По умолча нию используется порт 211. В поле GUID необходимо указать GUID-иденти фикатор сервера.

Pages:     | 1 | 2 || 4 | 5 |   ...   | 8 |



© 2011 www.dissers.ru - «Бесплатная электронная библиотека»

Материалы этого сайта размещены для ознакомления, все права принадлежат их авторам.
Если Вы не согласны с тем, что Ваш материал размещён на этом сайте, пожалуйста, напишите нам, мы в течении 1-2 рабочих дней удалим его.