Фабрика классов Логика функционирования
!gLockCount && IgObjCount ? S_OK : S_FALSE;
}
В конструктор класса coSay добавьте код, увеличивающий счетчик числа пользователей объектом Со Say:
gObjCount++;
а в деструктор — уменьшающий:
gObjCount--;
Важным шагом, о котором, тем не менее, легко забыть, является своевременная коррекция файла MyCom.def. Вставьте в конец этого файла строку
DllCanUnloadNow PRIVATE
которая добавляет в список экспортируемых функций еще один элемент. В файл MyCom. h добавьте декларацию нового класса CoSayFactory, реализующего интерфейс iclassFactory. Отметьте, что он произведен от интерфейса iClassFactory, который, как и положено, имеет родителя I unknown. Вы помните, что на плечи класса ложится бремя реализации всех методов своих предков. По той же причине мы вновь заводим счетчик числа пользователей классом (m_ref):
//====== Фабрика классов СОМ DLL-сервера
class CoSayFactory : public IClassFactory
{
public:
CoSayFactory() ;
virtual ~CoSayFactory() ;
// lUnknown
HRESULT _stdcall Querylnterface(REFIID riid,
void** ppv);
UbONG _stdcall AddRefO; ULONG _stdcall Release();
// IClassFactory
HRESULT _stdcall Createlnstance(LPUNKNOWN pUnk,
REFIID riid, void** ppv);
HRESULT _stdcall LockServer(BOOL bLock); private:
ULONG m_ref; };
Реализацию тел заявленных методов вставьте в файл МуСоm.срр. Здесь мы вынуждены повторяться,
вновь прокручивая логику управления временем жизни объектов СОМ:
//========== Фабрика классов
CoSayFactory::CoSayFactory()
{
m_ref = 0; gObjCount++;
}
CoSayFactory::-CoSayFactory()
{
gObjCount--;
}
//====== Методы lUnknown
HRESULT _stdcall CoSayFactory
::QueryInterface(REFIID riid, void** ppv)
{
*ppv = 0;
//=== На сей раз обойдемся без шаблона static_cast<>
if (riid == IID_IUnknown)
*ppv = (lUnknown*)this;
else if (riid == IID_IClassFactory)
*ppv = (IClassFactory*)this;
else
return E_NOINTERFACE;
AddRef();
return S_OK;
}
ULONG _stdcall CoSayFactory:rAddRef()
{
return ++m_ref;
}
ULONG _stdcall CoSayFactory::Release()
{
if (--m_ref==0)
delete this;
return m_ref;
//====== Методы интерфейса IClassFactory
HRESULT _ stdcall CoSayFactory: :CreateInstance
(LPUNKNOWN pUnk, REFIID riid, void** ppv)
{
// Этот параметр управляет аггрегированием
// объектов СОМ, которое мы не поддерживаем
if (pUnk)
return CLASS_E_NOAGGREGATION;
//== Создание нового объекта и запрос его интерфейса
CoSay *pSay = new CoSay;
HRESULT hr = pSay->Query!nterface (riid, ppv) ;
if (FAILED (hr))
delete pSay; return hr;
//=== Управление счетчиком фиксаций сервера в памяти
HRESULT _stdcall CoSayFactory::LockServer(BOOL bLock)
{
if (bLock) // Если TRUE, то увеличиваем счетчик
++gLockCount;
else // Иначе — уменьшаем
--gLockCount;
return S_OK;
}
Мы должны также изменить алгоритм функции DllGetciassOb j ect, которая теперь создает объект фабрики классов и запрашивает один из двух возможных интерфейсов (lUnknown,
IClassFactory):
STDAPI DllGetClassObject (REFCLSID rclsid, REFIID riid, LPVOID* ppv)
{
if (rclsid != CLSID_CoSay)
return CLASS_E_CLASSNOTAVAILABLE;
CoSayFactory *pCF = new CoSayFactory;
HRESULT hr = pCF->Query!nterface(riid, ppv);
if (FAILED(hr))
delete pCF;
return hr;
}
На этом модификация сервера завершается. Дайте команду Build > Rebuild и устраните ошибки, если они имеются. Затем вновь откройте проект клиентского приложения SayClient и внесите изменения в функцию main, которая теперь должна работать с объектами СОМ более изощренным способом. Она должна сначала загрузить СОМ-сервер и запросить адрес его фабрики классов, затем создать с ее помощью объект CoSay, попросив у него адрес интерфейса isay, и лишь после этого можно начать управление объектом. Последовательность освобождения объектов тоже должна быть тщательно выверена. Ниже приведена новая версия файла SayClient.cpp:
#include "interfaces.h"
void main()
{
(reinitialize (0) ;
IClassFactory *pCF;
// Мы зарегистрировали только один класс CoSay,
// поэтому ищем DLL с его помощью, но при этом
// создается не объект CoSay, а объект 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;
}
// Далее мы с помощью фабрики классов
// создаем объект CoSay и просим его
// дать нам адрес интерфеса ISay
ISay *pSay;
hr = pCF->Create!nstance(0,IID_ISay, (void**)&pSay) ;
if (FAILED(hr))
{
MessageBox(0,"Could not create CoSay and get ISay!
", "Createlnstance", MB_OK);
CoUninitialize ();
return;
}
// Уменьшаем счетчик числа пользователей
// фабрикой классов pCF->Release();
//====== Управляем объектом
pSay->Say();
BSTR word = SysAllocString(L"Yes, My Lord");
pSay->SetWord(word);
SysFreeString(word); pSay->Say();
//====== Уменьшаем число его пользователей
pSay->Release();
SCoUninitialize () ;
}
Запустите приложение (Ctrl+F5) и проверьте его работу. Алгоритм проверки остается тем же, что и ранее, но здесь мы должны по логике разработчиков СОМ, радоваться тому, что выполняем большее число правил и стандартов, а также имеем возможность одновременно создавать несколько СОМ-объектов.
На мой взгляд, не может быть ничего лучшего, чем получить код хорошо продуманного класса C++, который дает вам новую, хорошо документированную функциональность. При этом вы получаете полную свободу в том, как ее использовать, и имеете возможность развивать ее по вашему усмотрению. Использование методов класса предполагает выполнение оговоренных заранее правил игры, так же как и при использовании методов интерфейсов. Но эти правила значительно более естественные, чем правила СОМ. Вы, возможно, возразите, что для внедрения в проект нового класса, сам проект надо строить заново. Двоичный объект СОМ в этом смысле внедрить проще. Но здесь надо учитывать тот факт, что для реализации всех выгод СОМ вам придется разработать универсальный контейнер объектов, который будет способен внедрять СОМ-объекты будущих поколений и управлять ими. Это невозможно сделать, не трогая кода вашего приложения. Разработчик более или менее серьезного проекта постоянно корректирует его, изменяя код того или иного модуля. Он просто обречен на это. На мой взгляд, при реализации новых идей проще использовать исходные коды классов, чем двоичные объекты. Без сомнения, за хорошие коды надо платить, также как и за хорошие СОМ-объекты.