Интернет-журнал "Домашняя лаборатория", 2007 №6 - Усманов
Шрифт:
Интервал:
Закладка:
STDMETHODIMP_(ULONG) CoJournal::AddRef()
{
return ++m_refCount;
}
STDMETHODIMP_(ULONG) CoJournal::Release()
{
if (--m_refCount == 0)
{
delete this;
return 0;
}
else
return m_refCount;
}
STDMETHODIMP CoJournal::Querylnterface(REFIID riid, void** pIFace)
{
if (riid == IID_IUnknown)
{
*pIFace = (IUnknown*)this;
}
else if (riid == IID_IJournal)
{
*pIFace = (IJournal*)this;
}
else
{
*pIFace = NULL; return E_ NOINTERFACE;
}
((IUnknown*)(*pIFace)) — > AddRef();
return S_OK;
}
// IPub & IJournal
STDMETHODIMP CoJournal::SetTitle (BSTR bstrTitle)
{
SysReAllocString(&m_bstrTitle, bstrTitle);
return S_OK;
}
STDMETHODIMP CoJournal::SetYear(int nYear)
{
m_nYear = nYear;
return S_OK;
}
STDMETHODIMP CoJournal::SetNumber(int nNumber)
{
m_nNumber = nNumber;
return S_OK;
}
STDMETHODIMP CoJournal::GetInfo(BSTR *pbstrInfo)
{
char* pszTitle = NULL;
char* pszText = NULL;
int nTitleLength;
nTitleLength = SysStringLen(m_bstrTitle);
pszTitle = (char*)malloc(2*nTitleLength);
pszText = (char*)malloc(2*nTitleLength + 50);
wcstombs(pszTitle, m_bstrTitle, 2*nTitleLength);
sprintf(pszText, "JournalnnTitle: %snYear: %dnNumber: %dn", pszTitle, m_nYear, m_nNumber);
*pbstrInfo = SysAllocStringLen(NULL, 2*strlen(pszText));
mbstowcs(*pbstrlnfo, pszText, 2*strlen(pszText));
free(pszTitle);
free(pszText);
return S_OK;
}
Теперь, когда коклассы CoBook и CоJournal реализованы, им следует присвовить GUID и добавить соответствующую информацию в файл iid.h. Вот последняя версия этого файла
// GUID для всех интерфейсов и классов
// {9A5DE 9А0-7225-11d5-9 8С7-000001223694}
DEFINE_GUID(IID_IPub,
0x9a5de9a0, 0x7225, 0x11d5,
0x98, 0xc7, 0x0, 0x0, 0x1, 0x22, 0x36, 0x94);
// {9A5DE 9A1-7225-11d5-98C7-000001223694}
DEFINE_GUID(IID_IBook,
0x9a5de9al, 0x7225, 0x11d5,
0x98, 0xc7, 0x0, 0x0, 0x1, 0x22, 0x36, 0x94);
// {9A5DE 9A2-7 22 5-11d5-9 8C7-0 000 0122 3 69 4}
DEFINE_GUID(IID_IJournal,
0x9a5de9a2, 0x7225, 0x11d5,
0x98, 0xc7, 0x0, 0x0, 0x1, 0x22, 0x36, 0x94);
// {49F00760-7238-11d5-98C7-000001223694}
DEFINE_GUID(CLSID_CoBook,
0x4 9f007 60, 0x7238, 0x11d5,
0x98, 0xc7, 0x0, 0x0, 0x1, 0x22, 0x36, 0x94);
// {49F00761-7238-11d5-98C7-000001223694}
DEFINE_GUID(CLSID_CoJournal,
0x4 9f007 61, 0x7238, 0x11d5,
0x98, 0xc7, 0x0, 0x0, 0x1, 0x22, 0x36, 0x94);
Обратите внимание, что имя идентификатора кокласса стандартно формируется следующим образом — префикс CLSID_, за которым следует имя кокласса. Например, CLSID_CoBook.
Фабрики классов
Теперь возникает важный вопрос — как клиент может создавать СОМ объекты CoBook и CoJournal?
Два важных принципа СОМ:
• Независимость от языка
В соответствии с идеологией модели СОМ, классы СОМ (коклассы) могут реализовываться на различных языках и должны храниться в бинарном виде (dll или ехе файл). Клиент может быть реализован на любом из поддерживающих модель СОМ языке и, следовательно, должен иметь возможность создавать (активизировать) СОМ объект не используя каких-либо специфических для данного языка методов, а с помощью специального стандартного интерфейса.
• Прозрачность местоположения сервера
Имеется три места, где может размещаться СОМ сервер: о В процессе клиента
Этот способ обеспечивает самую быструю связь клиента и сервера (который реализуется в виде dll). Но есть и проблемы. Например, при ошибке в сервере "вылетает" весь процесс (т. е. и клиент), о Вне процесса клиента, но на одной с ним машине
Это так называемая локальная связь (обычно используется ехе-сервер). Она обеспечивает надежность (при ошибках в сервере клиент не гибнет), но необходима организация передачи данных между клиентом и сервером через границы процессов. Это СОМ берет на себя, обеспечивая кодирование, передачу и декодирование данных. При этом создаются прокси объект в процессе клиента и заглушка в процессе сервера. Прокси имитирует для клиента сервер, а заглушка имитирует для сервера клиента. В связи с этим код клиента не зависит от того, где располагается сервер. Все преобразование данных и их пересылка осуществляется парой прокси-заглушка. Коммутация прокси и заглушки основана на протоколе упрощенного удаленного вызова процедур (LRPC–Lightweight Remote Procedure Call).
♦ На удаленной машине
При этом связь наиболее медленная. Архитектура похожа на архитектуру, описанную в предыдущем пункте, только вместо LRPC используется RPC — протокол удаленного вызова процедур.
Заметим, что клиент не обязан знать, где именно расположен используемый сервер. Код клиента не меняется при изменении местоположения сервера. Более того, сервер может располагаться в нескольких местах, и по просьбе клиента система (менеджер управления сервисом), может устанавливать связь клиента с ближайшей к нему копией сервера.
Итак, нужен стандартный интерфейс для активации СОМ объекта. Такой интерфейс в СОМ имеется и называется IClassFactory (определен в <unknwn.h>). Дня каждого кокласса, "живущего" в некотором сервере, в этом же сервере должен "жить" класс, реализующий так называемую фабрику класса (или объект класса). Именно фабрика класса предоставляет клиенту интерфейс IClassFactory и более никаких других интерфейсов (кроме, естественно, IUnknown, от которого IClassFactory порожден как и все остальные СОМ интерфейсы).
Интерфейс IClassFactory имеет два метода:
• Createlnstance
С помощью этого метода клиент, имеющий указатель на фабрику некоторого класса, может создать любое число экземпляров этого класса и получить указатель на желаемый интерфейс этого класса.
• LockServer
Дня активации какого-либо СОМ объекта необходимо размещение сервера, в котором "живет" данный объект, в оперативной памяти. Каждый объект ведет счетчик ссылок на себя и автоматически удаляет себя из памяти, когда ссылок больше нет. Если нет активных объектов, "живущих" в сервере, то сервер также может быть удален из памяти. Но клиент может этому восприпятствовать, заблокировав сервер в памяти для более быстрой активации объектов в будущем. Для этого он и может вызвать метод LockServer.
Далее для определенности будем рассматривать построение dll-сервера (сервера в процессе клиента). Естественно, этот выбор никак не скажется на коде клиента. Построение локального сервера (сервер вне процесса клиента, но на этой же машине) будет рассмотрено позже.
Следующий код содержит определение фабрики класса для кокласса совоок. /
/////////////////////////////////////////////////
// CoBookFactory.h: заголовочный файл для класса CoBookFactory —
// фабрика класса для класса CoBook
//////////////////////////////////////////////////
#ifndef _CoBookFactory_
#define _CoBookFactory_