Интернет-журнал "Домашняя лаборатория", 2007 №6
Шрифт:
? Параллельная рассылка уведомлений
По умолчанию подсистема событий рассылает уведомления всем подписчикам последовательно, в некотором заранее не определенном порядке. При этом возникает следующая проблема. Вызов подписчика сводится к вызову некоторого реализованного им метода событийного интерфейса. Какова реализация этого метода, как долго этот метод будет выполняться — подсистеме событий неизвестно. Но передача уведомления следующему подписчику в данном случае будет выполнена только после завершения передачи уведомления предыдущему подписчику. Это означает, что подсистема событий не может гарантировать временной интервал, в рамках которого все подписчики
Потребовав параллельную рассылку уведомлений для данного класса событий, администратор просит подсистему событий по возможности рассылать уведомления параллельно, из разных потоков.
Более кардинальное решение проблемы связано с использованием фильтра на стороне издателя, что будет рассмотрено позднее.
? Идентификатор издателя
Событийному классу можно приписать уникальный идентификатор издателя. Это позволяет подписываться на события, распространение которых инициируется конкретным издателем.
Имеется еще один важный параметр класса событий, задание которого возможно только программным путем (используя административные компоненты для работы с СОМ+ каталогом) после регистрации событийного класса. Это свойство событийного класса MuitiInterfacePubiisherFilterCLSID. Данное свойство задает CLSID специального класса — фильтра, который на стороне издателя может регулировать рассылку уведомлений подписчикам. Данный фильтр будет обсуждаться позднее.
• Активация экземпляра событийного класса и инициализация рассылки уведомлений Активация экземпляра событийного класса выполняется издателем, желающим инициировать рассылку уведомлений о наступлении некоторого события. Издатель формирует экземпляр событийного класса, вызывая, например, функцию CoCreateInstance. Далее, получив указатель на событийный интерфейс данного событийного класса, он вызывает некоторый метод этого интерфейса, инициируя тем самым рассылку уведомлений всем подписчикам. Особенностью процесса активации экземпляра событийного класса является то, что подсистема событий автоматически синтезирует реализацию событийного интерфейса, обеспечивая активацию подписчиков и вызов соответствующего реализованного каждым подписчиком метода событийного интерфейса. Заметим, что в процесс рассылки уведомлений могут вмешаться два фильтра — фильтр со стороны издателя и фильтр со стороны подписчика.
Если не использовалась технология асинхронных компонент, и издатель дождался конца процесса рассылки уведомлений, то он может проанализировать код возврата типа hresult, который может принимать одно из следующих значений:
? S_SUCCESS — уведомления были доставлены всем подписчикам,
? EVENT_S_SOME_SUBSCRIBERS_FAILD — код успешного в целом завершения рассылки, однако доставка уведомлений некоторым подписчикам завершилась неудачей. Заметим, что на этом этапе невозможно определить подписчиков, не получивших уведомлений,
? EVENT_E_ALL_SUBSCRIBERS_FAILD — неудача, никто из подписчиков не получил уведомления.
? EVENT_S_NOSUBSCRIBERS — операция завершилась успехом, но подписчики на данное событие отсутствуют.
Издатель, подписчик и подписка
Исходя из вышеизложенного, издателем может выступать любой компонент (программа), имеющий право активировать экземпляр событийного класса и вызвать некоторый его метод. Никакой специальной регистрации в подсистеме событий не требуется. Для использования универсального маршалинга (маршалинга библиотеки типов) необходимо зарегистрировать на машине издателя библиотеку типов событийного класса.
Подписчиком является обычный СОМ+ компонент, реализующий событийный интерфейс. Особенностью является его регистрация не только на той машине, где он должен быть активирован в соответствии с логикой приложения (например, на машине пользователя, заинтересованного в получении информации о наступлении события), но и на той же машине, на которой зарегистрирован событийный класс. Последнее необходимо для оформления подписки.
По поводу подписки следует заметить, что существует три ее типа:
• Постоянная
Постоянная подписка переживает перезагрузку компьютера. При использовании постоянной подписки при вызове некоторого метода событийного интерфейса издателем формируется новый экземпляр каждого подписчика, который обрабатывает переданный ему вызов и уничтожается после возврата из вызванного метода. Это обеспечивается тем, что подсистема событий имеет информацию о CLSID каждого подписчика и машине, на которой его следует активировать.
• Временная
Эта подписка сохраняется только на время жизни объекта — подписчика. В отличие от предыдущего случая, экземпляр класса подписчика активируется не подсистемой событий, а некоторым приложением. После этого выполняется подписка, в результате которой подсистема событий получает указатель на событийный интерфейс объекта-подписчика. После получения и обработки уведомления о событии объект-подписчик не уничтожается и может принимать новые уведомления.
• PerUser
Это вариант постоянной подписки, который можно использовать только в том случае, когда издатель и подписчик работают на одном компьютере. В записи о подписке этого типа хранится SID некоторого пользователя, в процессе которого был создан объект-подписчик. Подписка включается подсистемой событий только при входе пользователя с данным SID в систему и отключается при его выходе из системы.
Как и в случае регистрации событийного класса, зарегистрировать подписку может только администратор, используя программирование и/или Component Services. Каждая подписка на некоторое событие является объектом, хранимым в подсистеме событий. Подписаться можно на событийный интерфейс в целом (на все его методы) или на некоторый метод событийного интерфейса. Если необходимо подписаться на несколько методов событийного интерфейса (но не на все), нужно отдельно оформить подписку для каждого метода. Заметим, что подписчик должен реализовать все методы событийного интерфейса.
Начнем с программной регистрации подписки:
• Прежде всего формируется экземпляр реализованного в системе класса с идентификатором CLSID_CEventSubscription (объект подписки) и запрашивается указатель на интерфейс IEventSubscription этого класса.
• Интерфейс IEventSubscription используется для задания свойств объекта подписки:
? GUID регистрируемой подписки задается вызовом метода IEventSubscription:: put_SubscriptioniD , где параметром является BSTR строка, задающая GUID подписки. Этот идентификатор должен быть уникален в данном сервисе событий,