Скачать презентацию
Идет загрузка презентации. Пожалуйста, подождите
Презентация была опубликована 11 лет назад пользователемzis61.org.ua
2 Загруженная в оперативную память программа (ЕХЕ-файл) становится процессом. Процесс - это выполняемая программа, процесс имеет свою память, описатели файлов и системные ресурсы. Если дважды запустить одну и ту же программу, то получится два разных процесса. Хранение компонентов
3 В адресном пространстве каждого процесса содержится такая информация: Oбраз ЕХЕ-файла программы; Все несистемные Dll, загруженные программой пользователя; Глобальные данные программы; Стек программы; Динамически выделяемая память, в том числе heap область Windows и библиотеки С++ периода выполнения; Файлы, спроецированные в память; Блоки памяти, совместно используемые несколькими процессами; Память, локальная для данного выполняемого потока; Таблицы виртуальной памяти; Ядро и Dll-компоненты Windows
4 После реализации компонента на любом языке программирования он должен выполняться как процесс в определенной операционной системе. СОМ – компоненты хранятся либо в исполняемых файлах ЕХЕ, либо в файлах Dll динамически загружаемых библиотек
5 Компоненты, которые хранятся в исполняемых файлах, называются локальными серверами. Клиенты (объекты, которые используют функции других объектов) и локальные серверы находятся в различных адресных пространствах в оперативной памяти. Библиотеки СОМ берут на себя заботу о передаче данных между ними. Пример локального сервера - Microsoft Word. Он предоставляет множество СОМ-компонентов, к которым имеют доступ другие приложения.
6 СОМ-серверы, которые хранятся в dll-файлах называются внутризадачными серверами. Внутризадачный сервер и клиент, который его вызвал, находятся в одном адресном пространстве. Транспортировка данных (упаковка, передача данных за пределы процесса, распаковка данных по достижении ими места назначения) в случае внутризадачных серверов не нужна. Вызов метода внутризадачного сервера сводится к прямому вызову функции.
7 При создании компонента необходимо выполнить действия: создание фабрики класса, получить указатель IClassFactory, потом создать компонент. Фабрика классов необходима для каждого компонента, а хранилище компонента должно обеспечить для средств СОМ способ доступа к этой фабрике. В зависимости от варианта хранения используется одна из двух основных технологий доступа. DLL-файлы должны предоставлять в общее пользование две функции: DllGetClassObject() и DllCanUnloadNow(), а исполняемые файлы должны регистрировать свои фабрики классов с помощью COM - функции CoRegisterClassObject. Реализация фабрики классов
8 Основные участники процесса создания компонента: клиент, библиотека СОМ, динамически подключаемые библиотеки Dll, фабрика классов, компонент. Клиент - это приложение или компонент приложения, которые используют другие компоненты и подсоединяются к компонентам посредством интерфейсов, инициирует запрос обращением к функции CoGetClassObject(). После создания фабрики класса клиент использует интерфейс IClassFactory для создания компонента. Библиотека СОМ - это спецификация, определяющая правила создания компонентов и библиотек компонентов, а также набор функций API (application programming interface), предоставляющих сервисы управления компонентами для клиентов и других компонентов, реализует CoGetClassObject().
9 Динамически подключаемые библиотеки DLL - это объектные модули, которые загружаются и подключаются к приложению на этапе выполнения приложения. Dll-файл состоит из глобальных данных, откомпилированных функций, ресурсов. Dll-файлы загружаются в адресное пространство приложения- клиента. Компиляция и тестирование Dll-файла осуществляется отдельно от приложения-клиента, что увеличивает модульность программы. Dll-файлы компилируются так, чтобы их можно было загружать по определенному базовому адресу и проецировать на тот же виртуальный адрес в процессе. DLL содержат функцию DllGetClassObject(), которая вызывается функцией СОМ CoGetClassObject(). Задача DllGetClassObject() создать запрошенную фабрику класса. Фабрика классов - это класс, поддерживающий интерфейс фабрики классов IClassFactory и используемый для создания множества объектов. Сервер предусматривает фабрику классов, инкапсулируя тем самым этап создания объекта. Конкретная фабрика классов создает компоненты, соответствующие только одному конкретному CLSID. Клиент использует интерфейсы фабрики классов для управления процессом создания компонентов. Стандартный интерфейс создания компонентов IClassFactory. Компоненты порождаются именно при помощи этого интерфейса. Компонент - приложение, которое поставляется пользователю как двоичный код, скомпилированный, скомпонованный и готовый к применению. Компоненты подключаются к другим компонентам на этапе выполнения, формируя приложение.
10 Для реализации фабрики классов необходимо определить интерфейс фабрики классов, производный от СОМ-интерфейса IClassFactory и переопределить методы CreateInstance() и LockServer().
11 class MathClassFactory : public IClassFactory //интерфейс фабрики классов компонента, производный от { //стандартного интерфейса фабрики классов protected: long m_lRef; //счетчик обращений к экземпляру ClassFactory public: MathClassFactory(); //конструктор интерфейса ~MathClassFactory(); //деструктор // объявление базового интерфейса IUnknown HRESULT __stdcall QueryInterface(const IID& iid, void** ppv ); ULONG __stdcall AddRef(); ULONG __stdcall Release(); // объявление базового интерфейса IClassFactory HRESULT __stdcall CreateInstance(IUnknown* pUnknownOuter, const IID& iid, void** ppv); HRESULT __stdcall LockServer(BOOL block); //блокировка программы сервера в памяти };
12 // реализация конструктора фабрики классов для компонента Math MathClassFactory::MathClassFactory() { m_lRef = 0; } // реализация деструктора фабрики классов для компонента Math MathClassFactory::~MathClassFactory() { }
13 //переопределение функций QueryInterface(), AddRef(),Release(), //наследуемых от интерфейса IUnknown, // определение указателя на требуемый интерфейс IMath HRESULT __stdcall MathClassFactory::QueryInterface(const IID& iid, void** ppv) { *ppv = 0; if ( iid == IID_IUnknown || iid == IID_IClassFactory ) *ppv = this; else { *ppv=NULL; return E_NOINTERFACE; } AddRef(); return S_OK; }
14 //увеличение счетчика ссылок ULONG __stdcall MathClassFactory::AddRef() { return InterlockedIncrement( &m_lRef ); } ULONG __stdcall MathClassFactory::Release() //уменьшение счетчика ссылок { if ( InterlockedDecrement( &m_lRef ) == 0 ) { delete this; return 0; } return m_lRef; }
15 // метод создания экземпляра компонента HRESULT __stdcall MathClassFactory::CreateInstance (IUnknown* pUnknownOuter, const IID& iid, void** ppv ) { // параметры метода: указатель на интерфейс IUnknown // ссылка на идентификатор возвращаемого интерфейса, указатель на запрашиваемый интерфейс Math* pMath; //указатель на объект Math HRESULT hr; //указатель на запрашиваемый интерфейс. (hr содержит значение, возвращаемое функцией QueryInterface()) *ppv = 0; // инициализация указателя на запрашиваемый интерфейс pMath = new Math; // создание нового объекта Math if ( pMath == 0 ) //если указатель не определен, return ( E_OUTOFMEMORY ); //то экземпляр объекта не //будет создан из-за нехватки памяти hr = pMath->QueryInterface( iid, ppv ); если память выделена, то получить у объект указатель на запрашиваемый интерфейс if ( FAILED( hr ) ) // указатель не определен, удалить объект Math delete pMath; return hr; // иначе возврат значения указателя на запрашиваемый интерфейс }
16 // реализация метода блокировки фабрики классов в памяти HRESULT __stdcall MathClassFactory::LockServer( BOOL block ) { // параметр - FALSE - для освобождения фабрики классов и выгрузки ее из памяти // TRUE - для сохранения фабрики классов в памяти. if ( block ) InterlockedIncrement( &g_lLocks ); else InterlockedDecrement( &g_lLocks ); return S_OK; }
17 Основные операции, которые необходимо выполнить для создания компонентов, следующие: Создание самой фабрики классов Получение указателя на интерфейс IClassFactory Создание компонента Технология создания компонентов с помощью фабрики классов
18 Последовательность вызовов функций СОМ: Клиент вызывает CoCreateInstance(), которая реализована в библиотеке СОМ. CoCreateInstance() вызывает CoGetClassObject() CoGetClassObject() отыскивает компонент в реестре. Если компонент найден, то CoGetClassObject() загружает Dll, являющуюся сервером компонента CoGetClassObject() вызывает DllGetClassObject() для создания фабрики класса (основная операция - new) DllGetClassObject() запрашивает у фабрики класса интерфейс IСlassFactory, который возвращается функции CoCreateInstance() CoCreateInstance() вызывает функцию CreateInstance () этого интерфейса CreateInstance() запрашивает у компонента интерфейс компонента CoCreateInstance() освобождает фабрику класса и возвращает указатель на интерфейс компонента клиенту Клиент использует данный указатель для вызова методов компонента
19 КлиентБиблиотека СОМ DLLФабрика классов Компонент DllGetClass Object CoCreateInstr ance CoGetClass Object new CA IClassFactory::CreateInstance () IClassFactory::Release() pIX -> Fx() new CFactory Рис. 15. Последовательность выполнения кодов клиента и компонента
20 Серверы внутри процесса (Dll-серверы) зависят от наличия следующих экспортируемых функций: DllCanUnloadNow() DllRegisterServer() DllUnregisterServer() DllGetClassObject() Для создания сервера Dll функция CoCreateInstance() вызывает CoGetClassObject(), которая вызывает DllGetClassObject(). Последняя возвращает указатель на интерфейс IClassFactory, который используется для создания компонента. Организация сервера в формате Dll
21 Псевдокод, описывающий процесс взаимодействия клиента, СОМ и Dll-сервера. IClassFactory* pClassFactory ; IUnknown* pUnknown; CoInitialize(NULL); //инициализация СОМ //CОМ использует реестр, чтобы отыскать //идентификатор класса по параметру имя сервера CLSIDFromProgID(имя сервера, &clsid); //СОМ ищет сервер в памяти по идентификатору класса CoGetClassObject(clsid, IID_IClassFactory, (void**) & pClassFactory);
22 if (DLLсервера еще не загружена) { СОМ считывает имя DLL- сервера из реестра. Загружает DLL-сервера в память процесса } if (сервер только что загружен) { создаются глобальные объекты – фабрики классов Вызывается InitInstance DLL } //СОМ вызывает из DLL глобальную экспортируемую функцию DllGetClassObject() // со значением CLSID, переданным ранее в CoGetClassObject. //DllGetClassObject() возвращает указатель на интерфейс IClassFactory* //COM возвращает клиенту указатель IClassFactory*
23 pClassFactory ->CreateInstance (IID_IUnknown, (void**) &pUnknown); //Вызывается функция фабрики классов CreateInstance() через таблицу виртуальных функций сервера. //Создается объект класса имя сервера. Возвращается указатель на запрошенный интерфейс pClassFactory ->Release(); pUnknown->Release(); // Вызывается Release() для объекта класса имя сервера через таблицу виртуальных функций if (счетчик ссылок==0) { объект самоуничтожается } CoFreeUnusedLibraries(); // СОМ вызывает из DLL экспортируемую функцию DllCanUnloadNow() //Функция DllCanUnloadNow() проверяет if (все созданные DLL объекты уничтожены) {return TRUE; } CoUninitialize(); //СОМ освобождает DLL и ресурсы, если DllCanUnloadNow() возвратила TRUE. //Kлиент завершается. Windows выгружает DLL, если DLL не используется другими процессами
24 Организация сервера в формате ЕХЕ Компонент передает клиенту интерфейс. Интерфейс – это, по существу, массив указателей функций. Клиент должен иметь доступ к памяти, занимаемой интерфейсом. Так как компонент и клиент находятся в разных адресных пространствах, то у клиента нет доступа к памяти процесса компонента. Если клиент не имеет доступ к памяти, связанной с интерфейсом, то он не сможет вызвать функции этого интерфейса. Чтобы с интерфейсом можно было работать через границу процесса, необходимо: Процесс должен иметь возможность вызывать функцию в другом процессе Процесс должен иметь возможность передавать другому процессу данные Клиент не должен беспокоиться о том, является ли компонент сервером внутри процесса или вне процесса.
25 При запуске ЕХЕ обязан зарегистрировать все поддерживаемые им фабрики классов. Когда клиент вызывает CoGetClassObject() СОМ сначала просматривает внутреннюю таблицу фабрики класса, ища заданный клиентом CLSID. Если фабрика класса в таблице отсутствует, то СОМ обращается к реестру и запускает соответствующий модуль ЕХЕ. Задача модуля ЕХЕ зарегистрировать свои фабрики классов, чтобы их могла найти СОМ. Так как ЕХЕ не могут экспортировать функцию DllGetClassObject(), то для замены функции DllGetClassObject(), используемой для Dll- сервера, используется функция CoRegisterClassObject(), которая регистрирует фабрику классов, получая указатель на ее интерфейс. Зарегистрированная фабрика классов становится доступной для СОМ
26 Когда работа сервера завершается, фабрики класса следует удалить из внутренней таблицы СОМ. Это выполняется при помощи функции CoRevokeClassObject(). Серверы внутри процесса экспортируют функцию DllCanUnloadNow(). Библиотека СОМ вызывает ее, чтобы определить, можно ли выгрузить сервер из памяти. Всякий раз при создании нового компонента счетчик компонентов увеличивается. Однако его значение не увеличивается при создании новой фабрики классов. Следовательно, сервер допускает завершение своей работы при наличии активной фабрики классов. Локальный сервер создает свои фабрики классов и удаляет их. Поэтому клиент должен использовать функцию IClassFactory::LockServer(), если он хочет гарантировать, что сервер присутствует в памяти, пока клиент пытается создать свои компоненты.
27 DLL не управляет своим жизненным циклом, ЕХЕ загружает и выгружает DLL. ЕХЕ управляет временем своего существования и выгружаются сами, когда счетчик блокировок в функции LockServer() станет равным нулю. Локальный сервер должен оставаться в памяти, пока с ним работает пользователь. Это значит, что сервер должен быть загружен не библиотекой СОМ, а пользователем. Когда пользователь завершает работу с сервером, с ним по-прежнему могут работать клиенты. Следовательно, при завершении программы пользователя сервер должен убрать с экрана пользовательский интерфейс, но не завершаться, пока не закончит обслуживание всех клиентов. Таким образом, сервер не должен посылать себе сообщение WM_QUIT при обработке сообщения об окончании работы пользователя и закрытия окна WM_DESTROY, если только функция CanUnloadNow не вернула значение S_OK
28 псевдокод, описывающий взаимодействие клиента, СОМ и EXE-сервера. CLSID clsid; IClassFactory* pClassFactory ; IUnknown* pUnknown; //инициализация СОМ CoInitialize(NULL); //CОМ использует реестр, чтобы отыскать идентификатор класса по параметру имя сервера CLSIDFromProgID(имя сервера, &clsid); //СОМ ищет сервер в памяти по идентификатору класса CoGetClassObject(clsid, IID_IClassFactory,(void**) & pClassFactory);
29 If ( ЕХЕ-сервер еще не загружен или нужен его новый экземпляр) { СОМ считывает имя ЕХЕ - сервера из реестра. Загружает ЕХЕ-сервер в память процесса } if (сервер только что загружен) { создаются глобальные объекты - фабрики классов Вызывается InitInstance (для MFC) CoInitialize(NULL); for (для каждого объекта-фабрики) { CoRegisterClassObject(..) //Возвращается IClassFactory* в СОМ }
30 // СОМ возвращает клиенту указатель на запрашиваемый интерфейс (указатель, передаваемый клиенту, не //совпадает с указателем на интерфейс сервера) pClassFactory ->CreateInstance (IID_IUnknown, (void**) &pUnk); //Вызывается функция фабрики классов CreateInstance(). Создается объект класса имя сервера //Возвращается указатель на запрошенный интерфейс pClassFactory ->Release(); pUnknown->Release(); //Вызывается Release() для объекта класса имя сервера if (счетчик ссылок==0) { объект самоуничтожается } if (все созданные объекты освобождены) { корректное завершение сервера } CoUninitialize(); //СОМ вызывает Release для всех объектов, не освобожденных //клиентом. Сервер завершается, если освобождаются все объекты //СОМ освобождает ресурсы.Клиент завершается
31 У компонента всегда имеется отдельная фабрика классов. Фабрика класса компонента обычно реализует интерфейс IClassFactory. С помощью этого интерфейса клиент может создать компонент. Для создания фабрики классов используется функция СОМ DllGetClassObject() и возвращается интерфейс IClassFactory, который передается функции СОМ CoCreateInstance(). Функция IClassFactory::CreateInstance() запрашивает у компонента его интерфейс. Получив интерфейс компонента, CoCreateInstance() освобождает фабрику классов и возвращает указатель на интерфейс компонента клиенту. Клиент использует данный указатель для вызова методов компонента. Выводы
Еще похожие презентации в нашем архиве:
© 2024 MyShared Inc.
All rights reserved.