Добавлен: 23.04.2023
Просмотров: 173
Скачиваний: 1
Файл IDL содержит в себе три основных элемента:
- Интерфейс. Клиенты применяют его для взаимодействия с сервером.
- CoClass. Класс, реализующий данный интерфейс.
- Библиотека типов. Это откомпилированный файл IDL, который используется для получения информации об интерфейсе.
- Определение пользовательского интерфейса. В рассматриваемом примере [6] файл MySrv.idl содержит определение интерфейса с помощью языка IDL. Оператор import применяется для взятия определений интерфейсов из файла oaidl.idl. Он подобен директиве #include языка C+—+. В квадратных скобках перечисляются атрибуты, которые относятся к описанию интерфейса, идущего ниже. Атрибут object сообщает компилятору IDL, что эта информация является определением скорее интерфейса COM, чем интерфейса RPC. Идентификатор GUID, определенный с помощью атрибута uuid, - уникальный идентификатор интерфейса. Директива importlib напоминает директиву import, но импортирует бинарные (откомпилированные) библиотеки типов. Всем библиотекам типов требуется директивой importlib импортировать библиотеку базовых типов, определенную в файле Stdole32.tlb.
С помощью программы guidgen.exe необходимо создать три идентификатора и заменить ими имеющиеся в файле MySrv.idl [6]. Копировать готовые идентификаторы необходимо в формате реестра (Registry Format).
Файл idl компилируется с помощью программы midl.exe. При компиляции генерируются следующие файлы:
- прокси-файл интерфейса (interface proxy file) mysrv_p.c, в котором содержится программный код передачи интерфейса IMyInterface между процессами, определенный в файле IDL из примера [6];
- файл заголовка mysrv.h, содержащий интерфейс и определения типов C++. Он также объявляет символьные константы для идентификаторов интерфейса ID и класса компонентов CLSID. Из примера это IID_IMyInterface и CLSID_MyComponent;
- файл mysrv_i.c, содержащий определения GUID для идентификаторов IID, CLSID и LIBID, объявленных в файле заголовка;
- бинарная версия файла IDL.
- Реализация интерфейсов. Файл MyComponent.h из примера [6] содержит реализацию интерфейса IUnknown и пользовательского. Заголовочный файл windows.h включен для использования некоторых функций WinAPI. Также включен файл mysrv.h, сгенерированный компилятором IDL. Этот файл содержит определение интерфейса. Класс CMyComponent наследуется от интерфейса и реализует его.
Поле refCount служит для подсчета ссылок на объект класса CMyComponent и инициализируется в конструкторе значением 0. Макросы STDMETHOD и STDMETHOD_ применяются для объявления функций с описанием соглашения о вызовах функций и типом возвращаемого значения. Макрос STDMETHOD описывает возвращаемое значение как HRESULT. Макрос STDMETHOD_ определяет возвращаемое значение в первом параметре. Для функций AddRef и Release он применяется, так как эти функции возвращают значение счетчика ссылок, а не HRESULT. В зависимости от платформы макросы могут быть определены по-разному, поэтому описание функций без использования макросов было бы некорректным.
С помощью метода QueryInterface клиент запрашивает конкретный интерфейс. Метод проверяет наличие такого интерфейса в компоненте и, если он найден, присваивает в указатель, переданный в метод. Сравнивается передаваемый параметр riid с идентификатором интерфейсов IUnknown и MyInterface. Также метод QueryInterface вызывает функцию AddRef для увеличения количества ссылок на объект. Функции AddRef и Release используют функции WinAPI InterlockedIncrement и InterlockedDecrement для получения монопольного доступа к переменной refCount при обеспечении безопасности многопоточности (thread-friendly). Если бы применялся простой инкремент refCount+—+, то конкурирующие потоки могли бы создать проблемы при использовании функций AddRef и Release [2].
Метод HelloWorld является единственным в пользовательском интерфейсе, который вызывает диалоговое окно с надписью «Hello World!».
3.5. Фабрика классов для интерфейса. Создание объекта COM и получение указателя интерфейса происходят через фабрику класса данного компонента. При этом происходят следующие действия:
- ищется бинарный файл, содержащий сервер COM;
- файл загружается в память;
- создается экземпляр компонента и передается указатель интерфейса IUnknown, реализованный в этом компоненте.
Обычно для создания экземпляра сервера COM клиент вызывает WinAPI функцию CoCreateInstance, которая имеет такую сигнатуру:
WINOLEAPI CoCreatelnstance(REFCLSID rclsid
, LPUNKNOWN pUnkOuter , DWORD dwClsContext , REFIID riid , LPVOID* ppv);
Параметр rclsid - это идентификатор CLSID требуемого компонента. Параметр riid - идентификатор требуемого интерфейса. В параметр ppv присваивается указатель интерфейса.
Функция CoCreateInstance непосредственно не создает экземпляр компонента, а передает указатель на реализацию интерфейса IClassFactory, которая отвечает за построение объектов классов компонента. При реализации интерфейса IClassFactory определяются функции:
- HRESULT CreateInstance(IUnknown* unkOuter, REFIID riid, void** ppvObject);
- HRESULT LockServer(BOOL lock).
Метод Createlnstance создает экземпляр объекта и возвращает указатель на требуемый интерфейс. Метод LockServer сохраняет сервер в памяти для последующего быстрого выполнения.
В методе CreateInstance создается экземпляр объекта с помощью оператора new. В самой реализации компонента он удаляется в методе Release при достижении счетчиком значения 0. Также вызывается метод QueryInterface и запрашивается требуемый интерфейс. Если объект не поддерживает требуемый интерфейс, то вызов завершается ошибкой, и клиент получает значение типа HRESULT с кодом ошибки. Метод LockServer увеличивает или уменьшает переменную locks, которая отвечает за удержание объекта в памяти.
3.6. Создание файла DLL. Для окончательного создания сервера COM нужно добавить несколько дополнительных функций API:
- DllMain - точка входа DLL, которая вызывается системой, когда поток или процесс начинает использование DLL. Это позволяет выполнить любую инициализацию и последующую очистку. Данная функция соответствует функции main исполняемых файлов с расширением exe.
- DllGetClassObject - вызывается с помощью функции CoGetClassObject для возврата указателя на интерфейс IClassFactory.
- DllCanUnloadNow - функция возвращает константу S_OK, если количество фиксаций сервера равно 0, в противном случае возвращает S_FALSE.
- DllRegisterServer - используется для регистрации сервера COM в операционной системе. В системах Win32 это означает добавление соответствующих записей в системный реестр Windows.
- DllUnregisterSever - применяется для отмены регистрации сервера COM. В системах Win32 это достигается удалением соответствующих записей из системного реестра Windows.
В файле MySrvDll.cpp из примера [6] экземпляр класса CMyClassFactory определен с помощью синглтона Мейерса [3, 4]. Это единственный экземпляр данного класса, используемый для создания объектов компонента COM.
Функции OpenKey, CreateKey, SetValue и DeleteKey применяются для управления регистрацией и отменой регистрации построенного нами сервера COM. Функция DllGetClassObject проверяет запрашиваемый идентификатор CLSID. Если он равен CLSID_MyComponent, то она использует глобальный экземпляр CMyClassFactory для запроса требуемого интерфейса (riid) и возврата результата. Функции DllRegisterServer и DllUnregisterServer управляют добавлением или удалением требуемых ключей и их значений в системном реестре. Фукция DllRegisterServer создает ключ системного реестра в разделе HKEYS_CLASSES_ROOT_CLSID. Название этого ключа является идентификатором CLSID компонента, заключенным в скобки. Для него установлено значение имени нашего компонента MyComponent. Внутри нового ключа создан подчиненный ему ключ InprocSever32. В нем установлено значение пути к файлу DLL. Ключ ThreadingModel установлен в значение Apartment. Для экспорта вышеуказанных функций создан текстовый файл MySrvDll.def. В нем определены экспортируемые функции DLL:
LIBRARY "MyComponent"
EXPORTS
DllCanUnloadNow PRIVATE
DllGetClassObject PRIVATE
DllRegisterServer PRIVATE DllUnregisterServer PRIVATE
Функции API, предназначенные для использования в системе COM, экспортируются как PRIVATE.
После сборки проекта необходимо зарегистрировать сервер COM, вызвав команду regsvr32 и передав в качестве параметра имя DLL. Программа Regsvr32.exe загрузит модуль DLL и вызовет функцию DllRegisterServer для создания требуемых записей в системном реестре [1].
3.7. Создание клиентского приложения. Клиентское приложение можно создать как Win32 консольный проект, где в функции main будет происходить обращение к серверу COM:
#include "stdafx.h"
#include <iostream>
#include <windows.h>
#include "..\MyComponent\mysrv.h"
#include
"..\MyComponent\mysrv_i.c"
int _tmain(int argc, _TCHAR* argv[])
{
CoInitializeEx(NULL, COINIT_APARTMENTTHREADED);
IMyInterface* my = NULL;
HRESULT hr = CoCreateInstance(CLSID_MyComponent , NULL
, CLSCTX_INPROC_SERVER , IID_IMyInterface , reinterpret_cast<void**>(&my));
if (FAILED(hr))
std::cout << "Com failed"; else {
my->HelloWorld();
my->Release();
}
CoUninitialize(); return 0;
}
Для доступа к интерфейсу IMyInterface нужен файл mysrv.h, сгенерированный компилятором MIDL; для доступа к идентификаторам CLSID и IID - файл mysrv_i.c, также сгенерированный компилятором MIDL. COM инициализируется вызовом функций Coinitialize. Указатель на интерфейс инициализируем с помощью функции CoCreatelnstance. Функцией CoUninitialize COM деинициализируется.
2 АНАЛИЗ ТЕХНОЛОГИИ COM
2.1 Сравнение технологии COM
Сравнение COM с другими похожими технологиями. В первую очередь технология COM интересна с точки зрения распределенных вычислений, когда каждая из компонент COM находится на отдельном сервере и они взаимодействуют друг с другом через сеть.
Наиболее известным конкурентом технологии COM является архитектура брокеров объектных запросов Common Object Request Broker Architecture (CORBA), которую развивает Консорциум OMG [8].
Функции CORBA и COM - это функции промежуточного программного обеспечения объектной среды. Для того чтобы обеспечить взаимодействие объектов и их интеграцию в цельную систему, архитектура промежуточного уровня должна реализовать несколько базовых принципов:
- независимость от физического размещения объекта: компоненты программного обеспечения не обязаны находиться в одном исполняемом файле, выполняться в рамках одного процесса или размещаться на одной аппаратной системе;
- независимость от платформы: компоненты могут выполняться на различных аппаратных и операционных платформах, взаимодействуя друг с другом в рамках единой системы;
- независимость от языка программирования: различия в языках, которые используются при создании компонентов, не препятствуют их взаимодействию друг с другом.
CORBA и COM отличаются, однако сходны в том, каким образом в них достигается реализация базовых принципов. Это клиент-серверные технологии, в которых функциональность объекта предоставляется клиенту посредством обращения к абстрактным интерфейсам. Интерфейс определяет набор методов, которые реализуют функции, присущие данному классу объектов. Интерфейс дает клиенту возможность только вызывать тот или иной метод, скрывая от него все детали его реализации. В обеих технологиях взаимодействие между клиентским процессом и сервером объекта, т. е. процессом, который порождает и обслуживает экземпляры объекта, использует механизм объектного варианта вызова удаленной процедуры (remote procedure call - RPC). Он реализует схему передачи сообщений, в соответствии с которой в распределенном клиент-серверном приложении процедура-клиент передает специальное сообщение с параметрами вызова по сети в удаленную серверную процедуру, а результаты ее выполнения возвращаются в другом сообщении клиентскому процессу.
Для того чтобы реализовать эту схему, на стороне клиента и на стороне сервера поддерживаются специальные компоненты, носящие название клиентский и серверный суррогаты (client stub и server stub). Для того чтобы вызвать ту или иную функцию, клиент обращается к клиентскому суррогату, который упаковывает аргументы в сообщение-запрос и передает их на транспортный уровень соединения. Серверный суррогат распаковывает полученное сообщение и в соответствии с переданными аргументами вызывает нужную функцию или нужный метод объекта, если речь идет об объектном варианте RPC. В СОМ клиентский суррогат называется proxy, а серверный - stub. В CORBA клиентский суррогат не имеет специального названия, а серверный обозначают термином skeleton [8].
Параметры вызова могут формироваться в отличной от серверной языковой и операционной среде, поэтому на клиентский и серверный суррогаты возлагаются функции преобразования аргументов и результатов в универсальное, не зависящее от конкретной архитектуры представление. Тем самым достигается возможность взаимодействия клиента и сервера на различных платформах. Одной из последних технологий для распределенных вычислений является Windows Communication Foundation (WCF) от компании Microsoft. Тотальное принятие стандартных способов реализации распределенных вычислений поменяло мир разработки приложений. К примеру, функции, представляемые в настоящее время, включают в себя безопасность, координацию распределенных транзакций и надежные соединения. Полезные изменения в программировании сервисов должны быть отражены в инструментах, используемых разработчиками. WCF призван предоставить управляемый подход к распределенным вычислениям, широкой функциональной совместимости и прямую поддержку сервисов.
WCF упрощает разработку связанных приложений с помощью новой, ориентированной на сервисы, модели. Он поддерживает множество стилей для разработки распределенных приложений, создавая слоистую архитектуру. В основе архитектура каналов WCF дает асинхронную, нетипизированную передачу сообщений. Над этим надстраивается архитектура для защищенного, надежного, транзакционного обмена данными, дающего большие возможности передачи и кодировки данных. WCF включает в себя возможности сериализации, которая позволяет избежать стыковки и версионирова- ния, и предоставляет интеграцию и функциональную совместимость с существующими распределенными системами .NET, такими как очередь сообщений (MSMQ), COM+, ASP.NET, сетевые сервисы, улучшенные сетевые сервисы (WSE).