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