Архитектура Аудит Военная наука Иностранные языки Медицина Металлургия Метрология Образование Политология Производство Психология Стандартизация Технологии |
COM-серверы, какие они бывают и о том, что дал нам Microsoft, чтобы управлять ими
Хранение компонентов До сих пор мы обсуждали требования, предъявляемые к интерфейсам СОМ-компонентов. Однако после реализации компонента на том или ином языке программирования (в нашем случае на C++) он должен выполняться как процесс в определенной операционной системе. СОМ-компоненты хранятся либо в исполняемых файлах (ЕХЕ), либо в файлах динамически загружаемых библиотек Windows (DLL), либо в тех и в других. Каждый вариант хранения имеет свои преимущества, и разработчик компонентов должен реализовывать определенные функции различным образом в зависимости от выбранного им способа размещения компонента. Термин локальный сервер используется для описания компонентов, которые хранятся в исполняемых файлах. Такие файлы, помимо СОМ-компонентов, могут содержать и другие функции. Например, Microsoft Word является локальным сервером. Он не только обеспечивает возможности обработки текстов, но также предоставляет множество СОМ-компонентов, к которым имеют доступ другие приложения. Внутризадачный сервер представляет собой DLL-файл Windows. Выполнение этого компонента происходит в контексте вызывающего процесса и, таким образом, процесс клиента имеет прямой доступ к любому DLL-компоненту. Понятие внутризадачности сервера приобретет особую значимость при рассмотрении пользовательских СОМ-интерфейсов и процессов транспортировки. Удаленный сервер представляет собой компонент, загружаемый и выполняемый на удаленном компьютере. Обычно удаленный сервер строится как исполняемый файл, однако это не является обязательным требованием. Доступ к компонентам, хранящимся только в DLL-файлах, может осуществляться также и в удаленном режиме. В таких случаях модель СОМ создает суррогатный процесс, в котором могут выполняться удаленные DLL-файлы. Одним из преимуществ СОМ является независимость клиента (пользователя компонента) от местоположения компонента. Как указывалось выше, СОМ-сервисы могут быть реализованы в трех различных конфигурациях: внутризадачной посредством DLL-файлов на локальном компьютере, межзадачной на одном и том же компьютере (локальный сервис) или на удаленном компьютере посредством DLL-файла или исполняемого кода. Однако клиенту не требуется знать, каким образом реализован компонент или где находятся необходимые сервисы. Он создает экземпляр компонента, а расположение и запуск возлагаются на средства СОМ. Таким образом, многосвязность является неотъемлемым свойством СОМ-компонентов. При определении способа реализации компонентов следует принять во внимание два основных фактора. Первый из них – эффективность. Поскольку внутризадачные серверы выполняются в адресном пространстве клиента, они являются наиболее эффективными. Кроме того, в данном случае не требуется никаких дополнительных средств для передачи параметров методам компонента.
Транспортировка Транспортировка (marshalling) – это процесс передачи аргументов функций и возвращаемых значений через " границу" процесса или по сети. Для этого требуется скопировать передаваемые значения в общую память таким образом, чтобы другой процесс получил к ним доступ. Транспортировка внутренних типов данных, таких как short и long, не представляет сложностей, но для большинства значений других типов, таких как указатели и структуры, транспортировка осуществляется немного сложнее. Нельзя создать копию указателя, поскольку его значение (адрес) не имеет смысла в контексте другого процесса. В этом случае приходится копировать всю структуру данных, адрес которой хранится в указателе. Только тогда другой процесс сможет получить доступ к этим данным. Поскольку внутризадачные серверы выполняются в адресном пространстве процесса клиента, для них не требуется никакого специального механизма транспортировки (хотя границы потока преодолеваются). Поэтому внутризадачные серверы обеспечивают наиболее эффективный способ взаимодействия с СОМ-компонентами. Наш первый пример компонента будет реализован как внутризадачный сервер. Таким образом, нам не придется беспокоиться о предоставлении поддержки для транспортировки данных. Если компонент является внутризадачным сервером, то клиент имеет прямой доступ к его методам через указатель на виртуальную таблицу интерфейсов компонентов. На рис. 3 показано, как осуществляется транспортировка в случае локального и удаленного серверов.
Рис. 3. Межзадачная транспортировка с помощью RPC (Remote Procedure Call – удаленный вызов процедур)
Фабрики классов В файле, в котором хранится компонент, должны присутствовать также средства, обеспечивающие стандартный, независимый от языка способ создания экземпляров этого компонента по запросу клиента. Средства СОМ предоставляют стандартный интерфейс IClassFactory, который должен обеспечивать создание экземпляров компонентов по запросу извне. Ниже приводится определение интерфейса IClassFactory. Как и все СОМ-интерфейсы, он должен реализовывать интерфейс IUnknown. class IClassFactory: public IUnknown{public: virtual HRESULT CreateInstance(LPUNKNOWN pUnk, REFIID riid, void** ppv)=0; virtual HRESULT LockServer (BOOL fLock) = 0; };
Фабрика классов представляет собой СОМ-компонент, единственной задачей которого является облегчение создания других СОМ-компонентов. Всякое хранилище компонента, будь то исполняемый или DLL-файл, должно обеспечивать реализацию фабрики классов для каждого компонента, который может создаваться по внешним запросам. Основной интерфейс IClassFactory предоставляет два метода: CreateInstance, создающий экземпляр компонента, и LockServer, обеспечивающий блокировку программы сервера в памяти. Блокируя сервер для других программ, клиент получает уверенность в том, что доступ к нему будет быстро получен. Обычно это делается в целях повышения производительности. Рассмотрим интерфейс фабрики классов для компонента Math. class MathClassFactory: public IClassFactory{protected: // Счетчик обращений к экземпляру ClassFactory long m_lRef; public: MathClassFactory(); ~MathClassFactory(); // реализация интерфейса IUnknown virtual HRESULT QueryInterface (REFIID riid, void** ppv); virtual ULONG AddRef(); virtual ULONG Release(); // реализация интерфеса IClassFactory virtual HRESULT CreateInstance(LPUNKNOWN pUnk, REFIID riid, void** ppv); virtual HRESULT LockServer(BOOL fLock); }; HRESULT MathClassFactory:: CreateInstance( LPUNKNOWN pUnkOuter, REFIID riid, void** ppvObj ){ Math *pMath; HRESULT hr; // Инициализация возвращаемого значения // на случай возникновения проблем *ppv0bj = NULL; // Создание нового экземпляра объекта Math pMath = new Math; if ( pMath == 0 ) return( E_OUTOFMEMORY ); // Получить у созданного объекта указатель // на запрашиваемый интерфейс hr = pMath-> QueryInterface( riid, ppvObj ); if ( FAILED( hr ) ) delete pMath; return hr; }
Фабрика классов необходима для каждого компонента, а хранилище компонента должно обеспечивать для средств СОМ способ доступа к этой фабрике. В зависимости от варианта хранения используется одна из двух основных технологий доступа. DLL-файлы должны предоставлять в общее пользование две функции: DllGetClassObject и DllCanUnloadNow, a исполняемые файлы должны регистрировать свои фабрики классов с помощью функции CoRegisterClassObject из библиотеки СОМ API. В данном случае мы реализуем компонент Math как внутризадачный сервер. В функцию DllGetClassObject включим следующую программу: STDAPI DllGetClassObject( REFCLSID rclsid, REFIID riid, void** ppv ){ HRESULT hr; MathClassFactory *pCF; pCF = 0; // необходимо убедиться, что CLSID соответствует нашему компоненту if (rclsid! = CLSID_Math) return (E_FAIL ); pCF = new MathClassFactory; if ( pCF == 0 ) return( E_OUTOFMEMORY ); hr = pCF-> QueryInterface ( riid, ppv ); // Проверка возвращаемого значения QueryInterface if ( FAILED( hr ) ) { delete pCF; pCF = 0; } return hr; }
Пользователю компонента Math потребуется указатель на интерфейс IMath. Вначале пользователь создаст экземпляр компонента, а потом запросит этот интерфейс. Средства СОМ предоставляют базовые функции API, позволяющие пользователю создавать экземпляры СОМ-компонентов. В следующей программе показано, каким образом клиент может получить доступ к компоненту Math. //// Client.срр//#include " client.h" #include < initguid.h> #include " imath.h" int main( int argc, char *argv[] ){ CoInitialize( NULL ); HRESULT hr = CoGetClassObject( CLSID_Math, CLSCTX_INPROC, NULL, IID_IClassFactory, (void**) & pCF ); // Используя интерфейс фабрики классов, создаем экземпляр // компонента Math IUnknown* pUnk; hr = pCF-> CreateInstance( NULL, IID_IUnknown, (void**) & pUnk ); // Освободить интерфейс фабрики классов pCF-> Release(); IMath* pMath = NULL; hr = pUnk-> QueryInterface( IID_IMath, (LPVOID*)& pMath ); pUnk-> Release(); long result; result = pMath-> Multiply( 100, 8 ); cout < < " 100 * 8 is " < < result < < endl; result = pMath-> Subtract( 1000, 333 ); cout < < " 1000 - 333 is " < < result < < endl; pMath-> Release (); CoUninitialize(); return 0; }
Прежде чем использовать какую-либо функцию СОМ API, клиент должен инициализировать сервисы СОМ с помощью функции CoInitialize, Как только это будет сделано, клиент должен применить функцию CoGetClassObject для получения требуемого интерфейса фабрики классов компонента (заданного идентификатором CLSID компонента, который будет рассмотрен чуть ниже). Тотчас после получения клиентом фабрики классов он вызывает метод CreateInstance() для создания фактического экземпляра класса Math и освобождает интерфейс фабрики классов. Метод CreateInstance() возвращает указатель на заданный интерфейс. В нашем примере требуется интерфейс IUnknown, и сразу же после его получения мы применяем метод QueryInterface для получения указателя на интерфейс IMath. Необходимый интерфейс IMath можно было легко получить и с помощью вызова CreateInstance, но мы хотели показать вам пример использования методов QueryInterface() и Release(). Указатель на интерфейс IMath используется для выполнения некоторых основных вычислений. После их окончания мы вызываем метод Release () и удаляем экземпляр компонента. Может возникнуть вопрос: " Откуда СОМ известно место хранения компонента (DLL)? " Ответим: " Из реестра". В СОМ для хранения информации о компонентах широко используется реестр Windows.
От теории к практике
Популярное:
|
Последнее изменение этой страницы: 2016-04-11; Просмотров: 1724; Нарушение авторского права страницы