Модель программирования Component Object Model. Разработка COM-сервера, страница 8

¨  Могут быть реализованы в виде отдельных серверов с собственным CLSID или могут быть частью самого COM-сервера. Функция DllGetClassObject, реализованная в DLL-файле, где находится сервер, должна знать, как создавать фабрику классов. Библиотека OLE при этом не используется.

COM интерфейс IClassFactory имеет всего два метода: CreateInstance и LockServer. Первый необходим для того, чтобы динамически создавать произвольное количество объектов тех классов (CLSID), которые живут в доме DLL COM-сервера, а второй — для того, чтобы запретить или разрешить системе выгружать сервер из памяти. Это позволяет пользователю гибко управлять необходимыми ресурсами. Если COM-объект пока не нужен клиентскому приложению, но вскоре может понадобиться, то вызвав метод  LockServer с параметром TRUE, клиент может запретить выгрузку из памяти DLL-сервера, несмотря на то, что счетчик числа пользователей ее объектами равен нулю. Если в течение какого-то времени не предвидится использование COM-объектов, то клиент может вызвать метод LockServer с параметром FALSE, разрешив тем самым выгрузку DLL-сервера из памяти.

Для реализации этой функциональности вновь откройте проект COM-сервера MyCom и в файл MyCom.cpp добавьте две глобальные переменные:

ULONG gLockCount;      // Счетчик числа блокировок DLL

ULONG gObjCount; // Счетчик числа пользователей COM-объектами

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

STDAPI DllCanUnloadNow()

{

//=== Если счетчики нулевые, то позволяем системе выгрузку DLL-сервера

return !gLockCount && !gObjCount ? S_OK : S_FALSE;

}

В конструктор класса CoSay добавьте код, увеличивающий счетчик числа пользователей объектом CoSay:

gObjCount++;

а в деструктор — уменьшающий:

gObjCount--;

Важным шагом, о котором, тем не менее, легко забыть, является своевременная коррекция файла MyCom.def. Вставьте в конец этого файла строку:

DllCanUnloadNow  PRIVATE

Она добавляет в список экспортируемых функций еще один элемент. В файл MyCom.h добавьте декларацию нового класса CoSayFactory, реализующего интерфейс IClassFactory. Отметьте, что он произведен от интерфейса IClassFactory, который (как и положено) имеет родителя IUnknown. Вы помните, что на плечи класса (то есть, наши плечи) ложится бремя реализации всех методов своих предков. По той же причине мы вновь заводим счетчик числа пользователей классом (m_ref).

class CoSayFactory : public IClassFactory        // Фабрика классов COM DLL-сервера

{

private:

ULONG m_ref; // Счетчик пользователей фабрикой классов

public:

CoSayFactory();

virtual ~CoSayFactory();

// Реализация обещаний IUnknown

HRESULT __stdcall QueryInterface (REFIID riid, void** ppv);

ULONG __stdcall AddRef();

ULONG __stdcall Release();

// Реализация обещаний IClassFactory

HRESULT __stdcall CreateInstance (LPUNKNOWN p, REFIID r, void** pp);

HRESULT __stdcall LockServer (BOOL bLock);

};

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

CoSayFactory::CoSayFactory() { m_ref = 0; gObjCount++; }

CoSayFactory::~CoSayFactory() { gObjCount--; }

HRESULT __stdcall CoSayFactory::QueryInterface (REFIID riid, void** ppv)

{

*ppv = 0;

if (riid == IID_IUnknown) // На сей раз обойдемся без шаблона static_cast<>

*ppv = (IUnknown*)this;

else if (riid == IID_IClassFactory)

*ppv = (IClassFactory*)this;

else

return E_NOINTERFACE;

AddRef();

return S_OK;

}

ULONG __stdcall CoSayFactory::AddRef() { return ++m_ref; }

ULONG __stdcall CoSayFactory::Release()

{

if (--m_ref==0)

delete this;

return m_ref;

}

//==== Методы интерфейса IClassFactory

HRESULT __stdcall CoSayFactory::CreateInstance(LPUNKNOWN p,REFIID r,void** pp)

{

if (p) // Этот параметр управляет аггрегированием объектов COM,которое мы не поддерживаем

return CLASS_E_NOAGGREGATION;

CoSay *pSay = new CoSay; // Создание нового объекта и запрос его интерфейса