Сущность технологии СОМ. Библиотека программиста
Шрифт:
LONG g_cLocks = 0; void LockModule(void)
{ InterlockedIncrement(&g_cLocks); }
void UnlockModule(void)
{ InterlockedDecrement(&g_cLocks); }
При наличии этих подпрограмм реализация DllCanUnloadNow становится чрезвычайно простой:
STDAPI DllCanUnloadNow(void)
{ return g_cLocks == 0 ? S_OK : S_FALSE; }
Oстается только вызывать в подходящее время подпрограммы LockModule и UnlockModule.
Существуют две основные
STDMETHODIMP_(ULONG) Chimp::AddRef(void)
{ if (m_cRef == 0) LockModule; return InterlockedIncrement(&m_cRef); }
и декрементируют счетчик блокировок при заключительном вызове Release:
STDMETHODIMP_(ULONG) Chimp::Release (void)
{ LONG res = InterlockedDecrement(&m_cRef); if (res == 0)
{ delete this; UnlockModule; }
return res; }
Поскольку объекты, не размещенные в динамически распределяемой области памяти (такие, как объекты классов), не содержат счетчика ссылок, при каждом вызове AddRef и Release нужно инкрементировать или декрементировать счетчик блокировок:
STDMETHODIMP_(ULONG) ChimpClass::AddRef(void) {
LockModule;
return 2;
}
STDMETHODIMP_(ULONG) ChimpClass::Release (void) {
UnlockModule;
return 1;
}
Объекты классов, которые реализуют IClassFactory, должны устанавливать свои серверные счетчики блокировок на вызовы IClassFactory::LockServer:
STDMETHODIMP ChimpClass::LockServer(BOOL bLock)
{
if (bLock) LockModule;
else UnlockModule;
return S_OK;
}
Как будет обсуждаться в главе 6, IClassFactory::LockServer создана в первую очередь для внепроцессных серверов, но она достаточно проста и для использования во внутрипроцессных серверах.
Следует заметить, что в протоколе CoFreeUnusedLibraries/DllCanUnloadNow неотъемлемо присутствует состояние гонки (race condition). Возможно, что один поток задач будет выполнять заключительное освобождение последнего экземпляра, экспортированного из DLL, в то время как второй поток будет выполнять подпрограмму CoFreeUnusedLibraries. В СОМ приняты все меры предосторожности, чтобы избежать этой ситуации. В частности, в реализацию СОМ под Windows NT 4.0 Service Pack 2 добавлена специальная возможность для борьбы с состоянием гонки. Версия Service Pack 2 библиотеки СОМ определяет, чтобы к DLL обращались из нескольких потоков, и вместо того, чтобы незамедлительно выгружать DLL изнутри CoFreeUnusedLibraries, СОМ ставит DLL в очередь DLL, подлежащих освобождению. Затем СОМ будет ждать неопределенное время, пока не разрешит этим неиспользуемым серверным DLL освободиться посредством
1 Вероятно, в Windows NT 5.0 будет предусмотрена дополнительная поддержка для подтверждения того, что DLL освобождаются быстро и безошибочно. Подробности можно найти в документации SDK.
Классы и IDL
Как уже отмечалось в начале этой главы, СОМ рассматривает интерфейсы и классы как отдельные сущности. В свете этого классы СОМ (а равно и интерфейсы СОМ) должны быть определены в IDL с целью обеспечить независимое от языка описание конкретных типов данных, которые может экспортировать сервер. IDL-определение класса СОМ содержит список интерфейсов, которые экспортируются элементами класса, исключая катастрофический сбой:
[uuid(753A8A7D-A7FF-11d0-8C30-0080C73925BA)]
coclass Gorilla { interface IApe; interface IWarrior; }
IDL– определения коклассов (coclass) всегда появляются в контексте определения библиотеки (library definition). В IDL определения библиотек используются для группирования набора типов данных (например, интерфейсы, коклассы, определения типов) в логический блок или пространство имен. Все типы данных, появляющиеся в контексте определения библиотеки IDL, будут отмечены в результирующей библиотеке типов. Библиотеки типов используются вместо IDL-файлов такими средами, как Visual Basic и Java.
Как правило, IDL-файл может содержать один библиотечный оператор, и все типы данных, определенные или использованные внутри определения библиотек, появятся в генерируемой библиотеке типа:
// apes.idl // bring in IDL definitions of ape interfaces
// введем IDL-определения интерфейсов обезьян
import «apeitfs.idl»;
[ uuid(753A8A80-A7FF-11d0-8C30-0080C73925BA),
// LIBID – идентификатор библиотеки version(1.0),
// version number of library – номер версии библиотеки
lcid(9),
// locale ID of library (english)
// код локализации библиотеки (english)
helpstring(«Library of the Apes»)
// title of library – заголовок библиотеки
]
library ApeLib { importlib(«stdole32.tlb»);
// bring in std defs. – вносим стандартные опредепения
[uuid(753A8A7D-A7FF-11d0-8C30-0080C73925BA)] coclass Gorilla {
[default] interface IApe;
interface IWarrior; }
[uuid(753A8A7E-A7FF-11d0-8C30-0080C73925BA)] coclass Chimpanzee {
[default] interface IApe;
interface IEgghead; }
[uuid(753A8A7F-A7FF-11d0-8C30-O080C73925BA)] coclass Orangutan {
[default] interface IApe;
interface IKeeperOfTheFaith; } }
Атрибут [default] показывает, какой из интерфейсов наиболее близко представляет внутренний тип класса. В тех языках, которые распознают этот атрибут, [default] позволяет программисту объявлять ссылки объекта, используя только имя кокласса СОМ: