Чтение онлайн

на главную - закладки

Жанры

Программирование на Visual C++. Архив рассылки

Jenter Алекс

Шрифт:

Путь среди функций этого COM сервера имеется функция MessageArchiving, которая архивирует почтовые сообщения программы Outlook фирмы Майкрософт. Архивы могут быть настолько огромными, что этот процесс подвесит ваше клиентское приложение на несколько часов (без какой-либо возможности взаимодействия с пользователем), пока функция архивирования наконец не выполнится полностью COM-сервером. Как можно бороться с этим? Первое, что приходит на ум, – это создание отдельного потока, в котором и вызовется эта "долгая" функция. Но не все тут так просто: Давайте вспомним, что для того, чтобы использовать модель COM в вашем приложении, вы должны проинициализировать её с помощью функции CoInitialize или CoInitializeEx. Думаю, первая функция нам не подходит, т. к. наверняка вы будете использовать

не только MessageArchiving, но и другие методы. Если учесть, что специально для неё (MessageArchiving) мы создали отдельный поток, то логично предположить, что работа с остальными функциями сервера PostAgent будет осуществляться, как минимум, ещё из основного потока. Поэтому, чтобы из всех потоков можно было работать с одним и тем же COM-сервером, нам необходима многопоточная инициализация модели COM (MTA), с помощью CoInitializeEx. Доказал? Будем надеяться, что да.

Что дальше? У нас все работает – потоки отлично исполняются, сообщения архивируются, даже налажен четкий механизм критических секций в коде: Но вот ваш шеф сказал вам, что ваше приложение ДОЛЖНО поддерживать механизм OLE-drag-drop. Вы сталкивались с этим? Нет? Значит, ваше счастье. Почему я так говорю? А потому, что для того, чтобы работать с drag-drop, нужно инициализировать однопоточную модель COM (STA), и все тут! Дело в том, что пока нет известного мне механизма OLE-drag-drop, реализованного через MTA. Когда разрабатывалась и создавалась технология ОЛЕ, в ней вообще не было понятия MTA.

Ну и что теперь будем делать дальше? Можно попытаться объяснить шефу, что это "невозможно". Но если он не поймет этого? Тогда придется инициализировать COM для каждого потока отдельно, в каждом из них вызывать COM-сервер, вводить глобальные переменные для того, чтобы обмениваться информацией между разными функциями одного и того же COM-сервера. А можно инициализировать STA один раз, но при этом применять процедуры маршалинга и самому следить за синхронизацией потоков и вызовами COM. В общем, кошмар. Допускаю, что это несколько утрированный пример, и, возможно, его можно как-то решить способами, которых я не знаю. Однако я сомневаюсь, что эти решения дадутся без боли в голове и будут претендовать на изящность. Ну а если, ко всему прочему, я добавлю ещё и неаккуратность самих разработчиков сервера PostAgent, то вполне может произойти ситуация, когда PostAgent просто повиснет, зациклится: да что угодно, но управления вам так и не вернет. Почему вы должны страдать от этого?

Вполне логично спросить, наконец, чего ради я начал тут столь бурные излияния на искусственно придуманный пример, и какое отношение ко всему этому имеет Connection point. А вот давайте все же немного дофантазируем, раз уж вы смогли дочитать этот текст до настоящего момента и понять, о чем идет речь.

А если с самого начала разработчики PostAgent подошли бы немного с другой точки зрения к реализации своих интерфейсов? Что я имею ввиду? А вот что. Все вызовы COM являются по своей природе синхронными вызовами. Это означает, что COM-сервер не отдаст вам управление до тех пор, пока не выполнится функция, которую вы вызвали. Но с появлением Connection point получила право на жизнь и асинхронная модель функций в COM. То есть "солидные" приложения и их разработчики (не в пример разработчикам PostAgent) реализуют в своих серверах 2 интерфейса для одного и того же набора функций. Один из них является синхронным, а другой – асинхронным. Про некоторые прелести синхронных функций я вам только что рассказал. А как работают асинхронные функции?

Примерно так. Вы вызываете асинхронную функцию, реализация которой заключается в том, чтобы сохранить переданные ей параметры в некой глобальной структуре данных, затем запустить внутри COM-сервера поток, который будет выполнять действия по обработке переданных данных и вернуть управление клиенту. Вот и все! И никакой головной боли, что я описывал выше. Вы вызвали MessageArchiving, она запомнила переданные вами данные, вернула вам управление, а сама заставила PostAgent выполнять задачу в фоновом для вас режиме. Однако если идти дальше, то вам может понадобиться и механизм, с помощью которого вы смогли бы не только наблюдать за

тем, что делает сейчас работающий COM-сервер, но так же и знать когда он закончит свою работу. Ведь так?! Понимаете, куда я клоню? Правильно! Сигнализировать о том, что работа функции, наконец, закончена, может специально предназначенный для этого Connection point. Кроме того, с его же помощью можно и прервать работу функции, если сделать соответствующий функционал в сервере. Но об этом позже. Таким образом, умелое сочетание синхронных и асинхронных функций даст возможность реализовать максимально эффективное взаимодействие через COM.

Думаю к настоящему моменту уже должно быть понятно, зачем нужны Connection points , а также хотя бы примерно, что это такое. Теперь перейдем к более детальному рассмотрению.

Connection point подразумевает под собой соединение. Соединение состоит из 2 частей: из объекта, вызывающего интерфейс и называемого источником, и из объекта, содержащего реализацию этого интерфейса и называемого приемником. Устанавливая точку соединения, источник позволяет приемнику присоединиться к себе. С помощью механизма точки соединения (интерфейс IConnectionPoint) указатель на интерфейс приемника передается объекту источника. Этот указатель обеспечивает источнику доступ к функциям-членам реализации приемника. К примеру, для возбуждения события, реализованного приемником, источник может вызвать соответствующий метод из реализации приемника.

Часть 2. Создание COM-сервера

Программа-пример Server

Итак, давайте создадим внутризадачный COM-сервер (DLL), который будет предоставлять клиенту одну-единственную функцию, результатом работы которой будет возбуждение события. Для этого запустите Visual Studio и выберите меню File->New. Затем в появившемся диалоге выберите вкладку Projects и в поле имени проекта введите ту информацию, что указана на рисунке 2, а также в качестве используемого мастера выберите MFC AppWizard (dll).

Рисунок 2

Нажмите ОК и вы увидите следующий диалог (рисунок 3). Поставьте установки, как показано на рисунке, и не забудьте отметить птичкой опцию Automation. Это важно, т.к. лишь в этом случае мастер добавит к нашему проекту код, необходимый для COM-сервера.

Рисунок 3

Нажмите Finish. Если вы сделали все правильно, то в окне ClassView вы должны увидеть ту же картину, что и на рисунке 4.

Рисунок 4

Итак, что же нам создал мастер? Он добавил минимальный джентльменский набор, который обеспечит существование скромному внутризадачному (DLL) COM-серверу. Это функции DllCanUnloadNow, DllGetClassObject и DllRegisterServer. Среди этих функций также должна находиться функция DllUnregisterServer, которая, на мой взгляд, также важна, как и DllRegisterServer, однако разработчики в Майкрософт почему-то так не считают. О том, как добавить в COM-сервер механизм unregistered, я расскажу в другой статье. Кроме того, в функцию InitInstance мастер добавил следующий код регистрации фабрики классов, за что ему и спасибо.

BOOL CPointServerApp::InitInstance {

 // Register all OLE server (factories) as running. This enables the

 // OLE libraries to create objects from other applications.

 COleObjectFactory::RegisterAll;

 return TRUE;

}

Пожалуй, все? Нет, не все. Если вы посмотрите в файлы проекта, созданные мастером, то увидите среди них замечательный файл описания интерфейсов PointServer.odl, содержимое которого имеет вид:

Поделиться:
Популярные книги

Война

Валериев Игорь
7. Ермак
Фантастика:
боевая фантастика
альтернативная история
5.25
рейтинг книги
Война

Экономка тайного советника

Семина Дия
Фантастика:
фэнтези
5.00
рейтинг книги
Экономка тайного советника

Чужбина

Седой Василий
2. Дворянская кровь
Фантастика:
попаданцы
альтернативная история
5.00
рейтинг книги
Чужбина

Мятежник

Прокофьев Роман Юрьевич
4. Стеллар
Фантастика:
боевая фантастика
7.39
рейтинг книги
Мятежник

Бестужев. Служба Государевой Безопасности. Книга вторая

Измайлов Сергей
2. Граф Бестужев
Фантастика:
фэнтези
попаданцы
аниме
5.00
рейтинг книги
Бестужев. Служба Государевой Безопасности. Книга вторая

Метатель. Книга 3

Тарасов Ник
3. Метатель
Фантастика:
попаданцы
альтернативная история
рпг
фэнтези
фантастика: прочее
постапокалипсис
5.00
рейтинг книги
Метатель. Книга 3

Студент из прошлого тысячелетия

Еслер Андрей
2. Соприкосновение миров
Фантастика:
героическая фантастика
попаданцы
аниме
5.00
рейтинг книги
Студент из прошлого тысячелетия

Ты не мой BOY

Рам Янка
5. Самбисты
Любовные романы:
современные любовные романы
5.00
рейтинг книги
Ты не мой BOY

Идеальный мир для Лекаря 2

Сапфир Олег
2. Лекарь
Фантастика:
юмористическая фантастика
попаданцы
аниме
5.00
рейтинг книги
Идеальный мир для Лекаря 2

Лекарь для захватчика

Романова Елена
Фантастика:
попаданцы
историческое фэнтези
фэнтези
5.00
рейтинг книги
Лекарь для захватчика

Отверженный VI: Эльфийский Петербург

Опсокополос Алексис
6. Отверженный
Фантастика:
городское фэнтези
альтернативная история
аниме
5.00
рейтинг книги
Отверженный VI: Эльфийский Петербург

Он тебя не любит(?)

Тоцка Тала
Любовные романы:
современные любовные романы
7.46
рейтинг книги
Он тебя не любит(?)

Убивать чтобы жить 3

Бор Жорж
3. УЧЖ
Фантастика:
героическая фантастика
боевая фантастика
рпг
5.00
рейтинг книги
Убивать чтобы жить 3

Темный Лекарь 11

Токсик Саша
11. Темный Лекарь
Фантастика:
попаданцы
аниме
фэнтези
5.00
рейтинг книги
Темный Лекарь 11