}
STDMETHODIMP_(ULONG) CoCarClassFactory::Release () { if(--m_refCount == 0)
{
delete this; return 0;
}
return m_refCount;
}
//Фабрики класса не реализуют интерфейсы кокласса, который они создают!
STDMETHODIMP CoCarClassFactory::QueryInterface(REFIID riid, void** ppv)
{
//Какой интерфейс желаете?
if(riid == IID_IUnknown)
{
*ppv = (lUnknown*)this;
}
else if(riid == IID_IClassFactory)
{
*ppv = (IClassFactory*)this;
}
else
{
*ppv = NULL; return E_NOINTERFACE;
}
((IUnknown*)(*ppv))->AddRef(); return S_OK;
}
Теперь реализуем IClassFactory:: CreateInstance(). В этом методе создадим объект СоСаr и запросим нужный интерфейс. Вернем HRESULT, который клиент сможет проанализировать. Если в методе CreateInstance() что-то не сработает, выделенную под кокласс память надо освободить:
// Создаем СоСаr!
39
STDMETHODIMP CoCarClassFactory:: CreateInstance(LPUNKNOWN pUnkOuter, REFIID riid, void** ppv)
{
// В этом объекте класса агрегация не поддерживается if(pUnkOuter != NULL)
return CLASS_E_NOAGGREGATION; СоСаr* pCarObj = NULL;
HRESULT hr;
//Создаем автомобиль pCarObj = new CoCar;
//Запрашиваем интерфейс
hr = pCarObj -> QueryInterface(riid, ppv);
// Проблема? Освобождаем занятую память if (FAILED(hr))
delete pCarObj;
return hr;
}
Реализуем IClassFactory: :LockServer()
для работы со счетчиком блокировок. В конструкторе и деструкторе объекта класса добавьте соответствующий код для этого счетчика. Для обращения к глобальным переменным, определенным в CarInProcServer.cpp, используйте ключевое, слово extern в файле СРР вашего объекта класса:
//Завершаем построение фабрики класса extern ULONG g_lockCount;
extern ULONG g_objCount;
//Конструктор
CoCarClassFactory::CoCarClassFactory()
{
m_refCount = 0; g_objCount++;
}
//Деструктор
CoCarClassFactory::~CoCarClassFactory() { g_objCount--; }
40
// Блокировки сервера
STDMETHODIMP CoCarClassFactory::LockServer(BOOL fLock)
{
if (fLock) |
++g_lockCount; |
else |
--g_lockCount; |
return S_OK;
}
Кроме того, убедитесь, что вы вставили нужные файлы в исходный код фабрики класса (существует обращение к CоСаr в CreateInstance(), поэтому требуется заголовочный файл CоСаr). Далее разработаем минимальный полный набор экспортируемых функций сервера.
3. Реализация хранилища компонентов DLL
Реализуйте в файле CarlnProcServer.cpp метод
DllGetClassObject():
//Этот экспортируемый метод делает фабрики класса видимыми для
//менеджера управления сервисом (Service Control Manager)
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv) { /*
1.Проверьте, совпадают ли CLSID и СоСаr, если нет — верните код ошибки
2.Создайте объект CoCarClassFactory
3.Запросите параметр REFIID у нового объекта класса
4.Установите HRESULT для клиента
*/
}
Последняя экспортируемая функция DLL — это
DllCanUnloadNow():
// Метод определяет, можно ли выгрузить DLL из памяти
STDAPI DllCanUnloadNow()
{
//Если g_objCount и g_lockCount оба равны нулю, возвращаем S_OK,
//в противном случае — S_FALSE
}
41
Ниже приводится возможный код для каждой из экспортируемых функций:
// Экспортируемые функции DLL
STDAPI DllCanUnloadNow ();
{
if (g_lockCount == 0 && g_objCount == 0)
return S_OK; |
// Выгрузи меня |
else |
|
return S_FALSE; |
// Оставь в памяти |
}
STDAPI DllGetClassObject(REFCLSID rclsid, REFIID riid, void** ppv)
{
HRESULT hr;
CoCarClassFactory *pCFact = NULL;
//Знаем, как в этом сервере создавать машины if(rclsid != CLSID_CoCar)
return CLASS_E_CLASSNOTAVAILABLE;
//Запрашивается CoCarClassFactory
pCFact = new CoCarClassFactory;
// Получаем интерфейс из CoCarClassFactory hr = pCFact -> QueryInterface(riid, ppv) ; if(FAILED(hr))
delete pCFact; return hr;
}
Создайте и вставьте в проект файл carinprocserver.def, чтобы сделать эти две функции видимыми окружающему миру. Имя в операторе library должно совпадать с именем проекта, который вы компонуете:
LIBRARY "CARINPROCSERVER" EXPORTS
DllGetClassObject @l PRIVATE DllCanUnloadNow @2 PRIVATE
42
4. Внесение информации в реестр
Чтобы завершить наш DLL-сервер, нужно создать еще REG-файл для внесения минимальной и полной информации о сервере в реестр системы. Создайте ProgID для вашего кокласса с подключом CLSID. Введите его прямо под HKCR. Затем введите CLSID для вашего кокласса с подключом InprocServer32, указывающим на физический путь к DLL.
Вот пример файла carinprocserver.reg:
REGEDIT
HKEY_CLASSES_ROOT\CarInProcServer.CoCar\CLSID = {7AD2D539-EE35-lld2-B8DE- 0020781238D4}
HKEY_CLASSES_ROOT\CLSID\{7AD2D539-EE35-lld2-B8DE-0020781238D4} = CarlnProcServer.CoCar
HKEY_CLASSES_ROOT\CLSID\{7AD2D539-EE35-lld2-B8DE-0020781238D4} \InprocServer32 = E:\ATL\Labs\Chapter03\CarInProcServer \Debug\CarInProcServer.dll
После того как сформируете REG-файл, просто щелкните по нему дважды в Проводнике Windows, чтобы записать информацию в реестр. Используйте regedit.exe для проверки правильности занесения информации.
Итак, создан СОМ-сервер на обычном C++ и без всяких библиотек. Позже построим клиент СОМ на C++, обращающийся к этому серверу.
Контрольные вопросы
1.Для чего служит фабрика класса?
2.Какая информация записывается в реестр системы о распределённых COM-программах?
43