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

HRESULT hr = pSay->QueryInterface (r, pp);

if (FAILED(hr))

delete pSay;

return hr;

}

//===== Управление счетчиком фиксаций сервера в памяти

HRESULT __stdcall CoSayFactory::LockServer(BOOL bLock)

{

if (bLock) // Если TRUE, то увеличиваем счетчик

++gLockCount;

else     // Иначе — уменьшаем

--gLockCount;

return S_OK;

}

Мы должны также изменить алгоритм функции DllGetClassObject, которая теперь создает объект фабрики классов и запрашивает один из двух возможных интерфейсов (IUnknown, IClassFactory).

STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, LPVOID* ppv)

{

if (rclsid != CLSID_CoSay)

return CLASS_E_CLASSNOTAVAILABLE;

CoSayFactory *pCF = new CoSayFactory;

HRESULT hr = pCF->QueryInterface(riid, ppv);

if (FAILED(hr))

delete pCF;

return hr;

}

На этом модификация сервера завершается. Дайте команду Build4Rebuild и устраните ошибки, если они имеются. Затем вновь откройте проект клиентского приложения SayClient и внесите изменения в функцию main, которая теперь должна работать с объектами COM более изощренным способом. Она должна сначала загрузить COM-сервер и запросить адрес его фабрики классов, затем создать с ее помощью объект CoSay, попросив у него адрес интерфейса ISay, и лишь после этого можно начать управление объектом. Последовательность освобождения объектов тоже должна быть тщательно выверена. Ниже приведена новая версия файла SayClient.cpp.

#include "guids.h"

void main()

{

CoInitialize(0);

IClassFactory *pCF;

// Мы зарегистрировали только один класс CoSay, поэтому ищем DLL с его помощью, но при этом создается объект

// CoSayFactory (см. код DllGetClassObject). Здесь же мы просим дать адрес интерфейса IClassFactory

HRESULT hr = CoGetClassObject (CLSID_CoSay,

CLSCTX_INPROC_SERVER, 0, IID_IClassFactory, (void**)&pCF);

if (FAILED(hr))

{

MessageBox (0,"Could not Get Class Factory!", "CoGetClassObject", MB_OK);

CoUninitialize();

return;

}

ISay *pSay;    // Здесь мы создаем объект CoSay и просим его дать адрес интерфеса ISay

hr = pCF->CreateInstance (0, IID_ISay, (void**)&pSay);

if (FAILED(hr))

{

MessageBox (0, "Could not create CoSay", "CreateInstance", MB_OK);

CoUninitialize();

return;

}

pCF->Release(); // Уменьшаем счетчик числа пользователей фабрикой классов

pSay->Say(); // Управляем объектом

BSTR word = SysAllocString (L"Yes, My Lord");

pSay->SetWord (word);

SysFreeString (word);

pSay->Say();

pSay->Release();  // Уменьшаем число его пользователей

CoUninitialize();

}

Запустите и проверьте. Алгоритм проверки остается тем же, что и ранее, но здесь мы должны (по логике разработчиков COM) радоваться тому, что выполняем большее число правил и стандартов, а также имеем возможность одновременно создавать несколько COM-объектов.

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

Вы, возможно, возразите, что для внедрения в проект нового класса, сам проект надо строить заново. Двоичный объект COM в этом смысле внедрить проще. Но здесь надо учитывать тот факт, что для реализации всех выгод COM вам придется разработать универсальный контейнер объектов, который будет способен внедрять COM-объекты будущих поколений и управлять ими. Это невозможно сделать, не трогая кода вашего приложения. Разработчик более или менее серьезного проекта постоянно корректирует его, изменяя код того или иного модуля. Он просто обречен на это. На мой взгляд, при реализации новых идей проще использовать исходные коды классов, чем двоичные объекты. Без сомнения, за хорошие коды надо платить, также как и за хорошие COM-объекты.

Независимость от языка