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