Использование макросов COM
Разработчики 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», и повторите процедуру построения. После этого сервер готов к использованию.