Разработчики COM рекомендуют для повышения надежности и переносимости компонентов использовать при их разработке множество макроопределений, которые вы также вынуждены будете использовать при разработке проекта на базе ATL. Например, макрос STDMETHODIMP при раскрытии заменяет спецификаторы HRESULT _stdcall. Для того чтобы приобрести навыки использования макросов СОМ, мы применим их в файлах MyCom.h и MyCom.cpp. Сравнивая старую и новую версии этих файлов, вы без труда поймете смысл макроподстановок. В файл MyCom.h ведите коррекцию кодов так, как показано ниже:
#if !defined(MY_COSAY_HEADER)
#define MY_COSAY_HEADER
#pragma once
#include "MyComTLib_h.h" class CoSay : public ISay
//====== Класс, реализующий интерфейсы ISay, lUnknown
public: CoSay (') ;
virtual -CoSay();
// lUnknown
STDMETHODIMP QuerylnterfacefREFIID riid, void** ppv);
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// ISay
STDMETHODIMP Say();
STDMETHODIMP SetWord (BSTR word);
private:
//====== Счетчик числа пользователей классом
ULONG m_ref;
//====== Текст, выводимый в окно
BSTR m_word;
};
//====== Фабрика классов СОМ DLL-сервера
class CoSayFactory : public IClassFactory
{
public:
CoSayFactory();
virtual ~CoSayFactory();
// lUnknown
STDMETHODIMP QueryInterface(REFIID riid, void** ppv) ;
STDMETHODIMP_(ULONG) AddRef();
STDMETHODIMP_(ULONG) Release();
// IClassFactory
STDMETHODIMP Createlnstance(LPUNKNOWN pUnk,
REFIID riid, void** ppv);
STDMETHODIMP LockServer(BOOL bLock);
private:
ULONG m_ref; };
#endif
Теперь перейдите к файлу MyCom.cpp и произведите замены в соответствии с текстом, приведенным ниже:
#include "MyComTLib_i.c"
#include "MyCom.h"
//====== Произвольный ограничитель длины строк
#define MAX_LENGTH 128
//====== Счетчик числа блокировок DLL
ULONG gLockCount;
//====== Счетчик числа пользователей СОМ-объектами
ULONG gObjCount;
CoSay::CoSay()
{
//=== Обнуляем счетчик числа пользователей класса,
//=== так как интерфейс пока не используется
m_ref = 0;
//=== Динамически создаем строку текста по умолчанию
m_word = SysAllocString(L"This is MyComTLib speaking");
gObjCount++;
}
CoSay::-CoSay()
{
//====== при завершении работы освобождаем память
if (m_word)
SysFreeString(m_word);
gObjCount—;
}
//====== Реализация методов lUnknown
STDMETHODIMP CoSay::QueryInterface(REFIID riid, void** ppv)
{
// Стандартная логика работы с клиентом
// Поддерживаем только два интерфейса
//====== Реализация lUnknown *ppv = 0;
if (riid==IID_IUnknown)
*ppv = static_cast<IUnknown*>(this);
else if (riid==IID_ISay)
*ppv = static_cast<ISay*>(this);
else
return E_NOINTERFACE;
//====== Добавляем единицу к счетчику
//====== пользователей нашим объектом
AddRef () ;
return S_OK;
}
STDMETHODIMP_(ULONG) CoSay::AddRef()
{
return ++m_ref;
}
STDMETHODIMP_(ULONG) CoSay: :Release ()
{
if (--m_ref==0)
delete this;
return m_ref;
}
//====== Реализация ISay
STDMETHODIMP CoSay::Say()
{
//=== Преобразование типов (из BSTR в char*),
//=== которое необходимо для использования
MessageBox char buff[MAX_LENGTH];
WideCharToMultiByte(CP_ACP, 0, m_word, -1,
buff, MAX_LENGTH, 0, 0);
MessageBox (0, buff, "Interface ISay:", MB_OK);
return S_OK;
}
STDMETHODIMP CoSay::SetWord(BSTR word)
{
//====== Повторное зыделение памяти
SysReAllocString(&m_word, word);
return S_OK;
}
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;
}
STDAPI DllCanUnloadNow()
{
//====== Если счетчики нулевые, то мы позволяем
//====== системе выгрузку DLL-сервера
return IgLockCount && IgObjCount ? S_OK : S_FALSE;
}
//====== Фабрика классов
CoSayFactory::CoSayFactory()
{
m_ref = 0;
gObjCount++;
}
CoSayFactory::-CoSayFactory()
gObjCount--;
}
//====== Методы lUnknown
STDMETHODIMP CoSayFactory
::QueryInterface(REFIID riid, void** ppv)
{
*ppv = 0;
//=== Обходимся без шаблона static casto
if (riid == IID_IUnknown)
*ppv = (lUnknown*)this;
else if (riid == IID_IClassFactory)
*ppv = (IClassFactory*)this;
else
return E_NOINTERFACE;
AddRef () ;
return S_OK;
}
STDMETHODIMP_(ULONG) CoSayFactory::AddRef()
{
return ++m_ref;
}
STDMETHODIMP_(ULONG) CoSayFactory::Release()
{
if (--m_ref==0)
delete this;
return m_ref;
}
//====== Методы IClassFactory
STDMETHODIMP 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;
}
//=== Управление счетчиком фиксаций сервера в памяти
STDMETHODIMP CoSayFactory::LockServer(BOOL bLock)
{
if (bLock) // Если TRUE, то увеличиваем счетчик
++gLockCount;
else // Иначе — уменьшаем
--gLockCount; return S_OK;
}
Регистрация библиотеки типов
Библиотеку типов также надо регистрировать для того, чтобы клиент мог найти ее с помощью уникального идентификатора. Введите изменения в файл MyCom.reg в соответствии со схемой, приведенной ниже, но используя при этом ваши идеитификаторы, файловые адреса и помня о правилах переноса. Сохраните исправления и зарегистрируйте все перечисленные объекты, дважды щелкнув на файле MyCom.reg в окне Windows File Manager:
REGEDIT HKEY_CLASSES_ROOT\MyComTLib.CoSay\CLSID =
{9B865820-2FFA-lld5-98B4-OOE0293F01B2}
HKEY_CLASSES_ROOT\CLSID\
{9B865820-2FFA-lld5-98B4-OOE0293F01B2}
= MyComTLib.CoSay
HKEY_CLASSES_ROOT\CLSID\
{9B865820-2FFA-lld5-98B4-OOE0293F01B2}
\InprocServer32 =
D:\My Projects\MyComTLib\Debug\MyComTLib.dll'
HKEY_CLASSES_ROOT\CLSID\
{9B865820-2FFA-lld5-98B4-OOE0293F01B2}\TypeLib =
{0934DA90-608D-4107-9ECC-C7E828AD0928}
HKEY_CLASSES_ROOT\TypeLib\
{0934DA90-608D-4107-9ECC-C7E828AD0928}
= MyComTLib
HKEY_CLASSES_ROOT\TypeLib\
{0934DA90-608D-4107-9ECC-C7E828AD0928}
\1.0\0\Win32 =
D:\My Projects\MyComTLib\Debug\MyComTLib.tlb
После этого дайте команду Build > Rebuild Solution. При осуществлении компоновки (Linking) в окне Output должна появиться строка:
Creating library Debug/MyComTLib.lib
and object Debug/MyComTLib.exp
которая свидетельствует о том, что DEF-файл воспринят и участвует в построении проекта. Если вы не видите этой строки, то выполните шаги по настройке проекта, которые описаны выше в разделе «Файл описания DLL», и повторите процедуру построения. После этого сервер готов к использованию.