- Фантастика и фэнтези
- Ироническое фэнтези
- Стимпанк
- Детективная фантастика
- Киберпанк
- Попаданцы
- LitRPG
- Космоопера
- Технофэнтези
- Городская фантастика
- Русское фэнтези
- Юмористическая фантастика
- Космическая фантастика
- Боевое фэнтези
- Эпическая фантастика
- Зарубежная фантастика
- Книги магов
- Мистика
- Разная фантастика
- Разное фэнтези
- Любовное фэнтези
- Романтическая фантастика
- Городское фентези
- Историческая фантастика
- Историческое фэнтези
- Социально-философская фантастика
- Сказочная фантастика
- Иностранное фэнтези
- Романтическое фэнтези
- Ироническая фантастика
- Ужасы и Мистика
- Постапокалипсис
- Героическая фантастика
- Фэнтези
- Научная Фантастика
- Социально-психологическая
- Альтернативная история
- Боевая фантастика
- Разная литература
- Пословицы, поговорки
- Шахматы
- Визуальные искусства
- Фанфик
- Авто и ПДД
- Военное
- Отраслевые издания
- Гиды, путеводители
- Зарубежная образовательная литература
- Военная история
- Цитаты из афоризмов
- Современная литература
- Великолепные истории
- Начинающие авторы
- Современная зарубежная литература
- Боевые искусства
- Изобразительное искусство, фотография
- Кино
- Литература 19 века
- Недвижимость
- Музыка, музыканты
- Готические новеллы
- Истории из жизни
- Культура и искусство
- Спецслужбы
- Зарубежная прикладная литература
- Музыка, танцы
- Подростковая литература
- Прочее
- Военная техника, оружие
- Газеты и журналы
- Периодические издания
- Домоводство, Дом и семья
- Любовные романы
- Экономическая литература
- Научные и научно-популярные книги
- О животных
- Биохимия
- Культурология
- Юриспруденция
- Политика
- Психотерапия
- Языкознание
- Научпоп
- Медицина
- Психология, личное
- Радиотехника
- Филология
- Педагогика
- Образовательная литература
- Деловая литература
- Физика
- Науки: разное
- Химия
- Воспитание детей, педагогика
- Детская психология
- Зарубежная публицистика
- Биология
- Архитектура
- Зарубежная психология
- Науки о космосе
- Социология
- Математика
- География
- Беременность, ожидание детей
- Литературоведение
- Транспорт, военная техника
- Обществознание
- Зоология
- География
- Альтернативная медицина
- Иностранные языки
- Ветеринария
- Рефераты
- Астрология
- Биофизика
- Экология
- История Европы
- Учебники
- Шпаргалки
- Государство и право
- Ботаника
- Религиоведение
- История
- Техническая литература
- Прочая научная литература
- Психология
- Детективы и Триллеры
- Справочная литература
- Проза
- Новелла
- Юмор
- Проза
- Историческая проза
- Русская современная проза
- Советская классическая проза
- Повести
- Очерки
- Эссе
- Рассказы
- Зарубежная современная проза
- Сентиментальная проза
- Зарубежная классика
- Афоризмы
- Эпистолярная проза
- Феерия
- Семейный роман/Семейная сага
- Разное
- Антисоветская литература
- Магический реализм
- Русская классическая проза
- Современная проза
- О войне
- Контркультура
- Классическая проза
- Поэзия, Драматургия
- Детская литература
- Детская фантастика
- Детские остросюжетные
- Детские стихи
- Детские приключения
- Внеклассное чтение
- Учебная литература
- Зарубежные детские книги
- Бизнес
- Загадки
- Книги для подростков
- Школьные учебники
- Детский фольклор
- Книги для дошкольников
- Детские детективы
- Детская познавательная и развивающая литература
- Буквари
- Прочая детская литература
- Сказка
- Детская проза
- Детская образовательная литература
- Юмор
- Документальные книги
- Бизнес
- Внешнеэкономическая деятельность
- Тайм-менеджмент
- Кадровый менеджмент
- О бизнесе популярно
- Управление, подбор персонала
- Бизнес
- Менеджмент и кадры
- Продажи
- Экономика
- Ценные бумаги и инвестиции
- Малый бизнес
- Делопроизводство, офис
- Корпоративная культура, бизнес
- Банковское дело
- Финансы
- Зарубежная деловая литература
- Ораторское искусство / риторика
- Личные финансы
- Интернет-бизнес
- Поиск работы
- Государственное и муниципальное управление
- Бухучет и аудит
- Работа с клиентами
- Менеджмент
- Краткое содержание
- Личная эффективность
- Переговоры
- Приключения
- Старинная литература
- Религия и духовность
- Компьютеры и Интернет
- Блог
Читаем без скачивания Сущность технологии СОМ. Библиотека программиста - Дональд Бокс
Шрифт:
Интервал:
Закладка:
STDMETHODIMP_(DWORD) MyClassObject::AddConnection(DWORD extconn, DWORD) {
DWORD res = 0;
if (extconn & EXTCONN_STRONG) {
LockModule();
// note external reference
// записываем внешнюю ссылку
res = InterlockedIncrement(&m_cExtRef);
}
return res;
}
STDMETHODIMP_(DWORD) MyClassObject::ReleaseConnection(DWORD extconn, DWORD, BOOL bLastReleaseKillsStub) {
DWORD res = 0;
if (extconn & EXTCONN_STRONG) {
UnlockModule();
// note external reference
// записываем внешнюю ссылку
res = InterlockedDecrement(&m_cExtRef);
if (res == 0 & bLastReleaseKillsStub) CoDisconnectObject((IExternalConnection*)this, 0);
}
return res;
}
Отметим, что счетчик блокировок модуля будет ненулевым до тех пор, пока существуют неосвобожденные внешние ссылки на объект класса, в то время как внутренние ссылки, удержанные библиотекой COM, игнорируются.
Хотя технология использования IExternalConnection для объектов класса существовала в COM с самых первых дней, лишь немногие разработчики используют ее на деле. Вместо этого большинство серверов обычно игнорируют неосвобожденные внешние ссылки на объекты класса и завершают серверные процессы преждевременно. Этому положению способствовало присутствие метода LockServer в интерфейсе IClassFactory, который внушает разработчикам мысль, что клиенты будто бы способны в действительности обеспечить выполнение сервера. В то время как большинство разработчиков серверов успешно запирают модуль в методах LockServer, для клиента не существовало надежного способа вызвать данный метод. Рассмотрим следующий клиентский код:
IClassFactory *pcf = 0;
HRESULT hr = CoGetClassObject(CLSID_You, CLSCTX_LOCAL_SERVER, О, IID_IClassFactory, (void**)&pcf);
if (SUCCEEDED(hr)) hr = pcf->LockServer(TRUE);
// keep server running?
// поддерживать выполнение сервера?
В первых версиях COM этот фрагмент кода находился бы в условиях серьезной гонки. Отметим, что существует интервал между вызовами CoGetClassObject и IClassFactory::LockServer. В течение этого периода времени другие клиенты могут уничтожить последний остающийся экземпляр класса. Поскольку неосвобожденная ссылка на объект класса игнорируется наивными реализациями серверов, серверный процесс прекратит работу раньше исходного вызова клиентом метода LockServer . Теоретически это можно было бы преодолеть следующим образом:
IClassFactory *pcf = 0;
HRESULT hr = S_OK;
do {
if (pcf) pcf->Release();
hr = CoGetClassObject(CLSID_You, CLSCTX_LOCAL_SERVER, 0, IID_IClassFactory, (void**)&pcf);
if (FAILED(hr)) break;
hr = pcf->LockServer(TRUE);
// keep server running?
// поддерживать выполнение сервера?
}
while (FAILED(hr));
Отметим, что данный фрагмент кода периодически пытается подсоединиться к объекту класса и заблокировать его, пока вызов LockServer проходит успешно. Если сервер завершит работу преждевременно – между вызовами CoGetClassObject и LockServer, то вызов LockServer возвратит сообщение об ошибке, извещающее об отсоединенном заместителе, что вызовет повтор последовательности. Под Windows NT 3.51 и в более ранних версиях этот нелепый код был единственным надежным способом получения ссылки на объект класса.
Был признан тот факт, что многие реализации серверов не использовали IExternalConnection для должного управления временем жизни сервера, и в версии COM под Windows NT 4.0 введена следующая модернизация для замены этих наивных реализаций. При маршалинге ссылки на объект класса в ответ на вызов CoGetClass0bject SCM вызовет метод объекта класса IClassFactory::LockServer. С тех пор как значительное большинство серверов реализуют IClassFactory в своих объектах класса, эта модернизация исполняемых программ COM исправляет значительное количество дефектов. Однако если объект класса не экспортирует интерфейс IClassFactoryили если сервер должен выполняться и в более ранних версиях COM, чем Windows NT 4.0, то необходимо использовать технологию IExternalConnection.
Следует обсудить еще одну проблему, относящуюся ко времени жизни сервера. Отметим, что когда сервер решает прекратить работу, то он сообщает о том, что главный поток серверного приложения должен начать свою последовательность операций останова (shutdown sequence ) до выхода из процесса. Частью этой последовательности операций останова является вызов CoRevokeClassObject для отмены регистрации его объектов класса. Если, однако, были использованы показанные ранее реализации UnlockModule , то появляются условия серьезной гонки. Возможно, что в промежутке между тем моментом, когда сервер сигнализирует главному потоку посредством вызова SetEvent или PostThreadMessage, и тем моментом, когда сервер аннулирует объекты своего класса, вызывая CoRevokeClassObject, в серверный процесс поступят дополнительные запросы на активацию. Если в этот интервал времени создаются новые объекты, то уже нет способа сообщить главному потоку, что прекращение работы – плохая идея и что у процесса появились новые объекты для обслуживания. Для устранения этих условий гонки в COM предусмотрены две API-функции:
ULONG CoAddRefServerProcess(void);
ULONG CoReleaseServerProcess(void);
Эти две подпрограммы управляют счетчиком блокировок модуля от имени вызывающего объекта. Эти подпрограммы временно блокируют любой доступ к библиотеке COM, чтобы гарантировать, что во время установки счетчика блокировок новые активационные запросы не будут обслуживаться. Кроме того, если функция CoReleaseServerProcess обнаружит, что удаляется последняя блокировка в процессе, то она изнутри пометит все объекты класса в процессе как приостановленные и сообщит SCM, что процесс более не является сервером для его CLSID.
Следующие подпрограммы корректно реализуют время жизни сервера во внепроцессном сервере:
void LockModule(void) {
CoAddRefServerProcess();
// COM maintains lock count
// COM устанавливает счетчик блокировок
}
void UnlockModule(void) {
if (CoReleaseServerProcess() == 0) SetEvent(g_heventShutdown);
}
Отметим, что прекращение работы процесса в должном порядке по-прежнему остается обязанностью вызывающей программы. Однако после принятия решения о прекращении работы ни один новый активационный запрос не будет обслужен этим процессом.
Даже при использовании функций CoAddRefServerProcess / CoReleaseServerProcess все еще остаются возможности для гонки. Возможно, что во время выполнения CoReleaseServerProcess на уровне RPC будет получен входящий запрос на активацию от SCM. Если вызов от SCM диспетчеризован после того, как функция CoReleaseServerProcess снимает свою блокировку библиотеки COM, то активационный запрос отметит, что объект класса уже помечен как приостановленный, и в SCM будет возвращено сообщение об ошибке со специфическим кодом (CO_E_SERVER_STOPPING). Когда SCM обнаруживает этот специфический код, он просто запускает новый экземпляр серверного процесса и повторяет запрос, как только новый серверный процесс зарегистрирует себя. Несмотря на системы защиты, используемые библиотекой COM, остается вероятность того, что поступающий активационный запрос будет выполняться одновременно с заключительным вызовом функции CoReleaseServerProcess. Чтобы избежать этого, сервер может явно возвратить CO_E_SERVER_STOPPING как из IClassFactory::Create Instance, так и из IPersistFile::Load в том случае, если он определит, что по окончании запроса на прекращение работы был сделан еще какой-то запрос. Следующий код демонстрирует этот способ:
STDMETHODIMP MyClassObject::CreateInstance(IUnknown *puo, REFIID riid, void **ppv) {
LockModule();
// ensure we don't shut down while in call
// убеждаемся в том, что не прекращаем работу
// во время вызова HRESULT hr;
*ppv = 0;
// shutdown initiated?
// процесс останова запущен?
DWORD dw = WaitForSingleObject(g_heventShutdown, 0);
if (dw == WAIT_OBJECT_0) hr = CO_E_SERVER_STOPPING;
else {
// normal CreateInstance implementation
// нормальная реализация CreateInstance
}
UnlockModule();
return hr;
}
Во время написания этого текста ни одна из коммерческих библиотек классов COM не реализовывала этот способ.
Идентификаторы приложений
В версии COM под Windows NT 4.0 введено понятие приложений COM (COM applications). Приложения COM идентифицируются с помощью GUID (называемых в этом контексте AppID – идентификаторы приложения) и представляют серверный процесс для одного или более классов. Каждый CLSID может быть связан с ровно одним идентификатором приложений. Эта связь фиксируется в локальном реестре, использующем именованное значение AppID:
[HKCRCLSID{27EE6A4E-DF65-11D0-8C5F-0080C73925BA}] @="Gorilla" AppID="{27EE6A4E-DF65-11D0-8C5F-0080C73925BA}"
Все классы, принадлежащие к одному и тому же приложению COM, будут иметь один и тот же AppID, а также будут использовать одни и те же установки удаленной активации и защиты. Эти установки записаны в локальном реестре под ключом HKEY_CLASSES_ROOTAppID
Подобно CLSID, AppID могут быть зарегистрированы для каждого пользователя под Windows NT версии 5.0 или выше. Поскольку серверы, реализованные до появления Windows NT 4.0, не регистрируют явно свои AppID, инструментальные средства конфигурирования COM (например, DCOMCNFG.EXE, OLEVIEW.EXE) автоматически создадут новый AppID для этих старых серверов. Чтобы синтезировать AppID для старых серверов, эти программы автоматически добавляют именованное значение AppID ко всем CLSID, экспортируемым определенным локальным сервером. При добавлении этих именованных значений DCOMCNFG или OLEVIEW просто использует в качестве AppID первый встреченный CLSID для данного сервера. Те приложения, которые были разработаны после выпуска Windows NT 4.0, могут (и будут) использовать для своих AppID особый GUID.