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

ЖАНРЫ

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

Jenter Алекс

Шрифт:

Это все на сегодня. Пока! 

Алекс Jenter jenter@rsdn.ru Красноярск, 2001. Рассылка является частью проекта RSDN.

Программирование на Visual C++

Выпуск №49 от 8 июля 2001 г.

Добрый день, дорогие подписчики!

СТАТЬЯ 

Добавление технологии Connection point в приложение на базе библиотеки MFC

Автор: Евгений Щербатов

Часть 1. Необходимость существования и принцип работы Connection point

В данной статье я сделал попытку объяснить, что

такое Connection point, её (его – кому как нравится) устройство и принцип работы. Плохо или хорошо у меня это получилось, судить вам.

Структура статьи построена таким образом, чтобы человек, НИЧЕГО не знающий об этой аббревиатуре, не только понял общие принципы работы, но и смог реализовать данную возможность в своих программах с использованием MFC технологии. Я сам в свое время реально столкнулся с проблемой в максимально сжатые сроки изучить и реализовать этого "зверя", ни разу и не слышав о нем раньше. Если вы находитесь в подобной ситуации, то эта статья для вас.

Я не претендую на академическую точность изложения материала, потому как тот кусочек текста, что вы будете читать, есть не детальный перевод материала из MSDN, а моя попытка систематизировать те знания, которыми я обладаю и преподнести их вам в том контексте и порядке, который, как я считаю, сильно помог бы мне в свое время. Считаю нужным заметить, что реализация Connection point на MFC и ATL сильно отличаются, впрочем, равно как и реализация самих COM-серверов. Именно ввиду этого я и выбрал в качестве примеров библиотеку MFC. Дело в том, что про использование Connection point на ATL написано немало статей – я в этом лично убедился. Да и, кроме того, сам мастер в ATL без проблем позволяет добавить и использовать эту технологию. Что же касается MFC, то здесь это сопряжено с некоторыми трудностями. Поэтому, думаю, что те люди, у которых будет необходимость работать именно с MFC, хоть немного, но получат пользу от этого материала. Я постараюсь детально рассказать о тех подводных камнях, с которыми вы можете столкнуться при этом, и помогу вам преодолеть их. Остается добавить, что я буду искренне благодарен любым поправкам и советам в мой адрес.

Итак, начнем! Для начала неплохо бы открыть MSDN и посмотреть словарик на этот счет – Glossary (Platform SDK: COM). Мы можем увидеть следующую вещь:

Connection point object (объект точки связи)

Это COM-объект, который управляется Connectable object и содержит реализацию IConnectionPoint интерфейса. Одна или более точек соединения объектов может быть создана и управляться Connectable object. Каждая точка соединения объекта управляет поступлением событий от специфического интерфейса к другому объекту и пересылкой этих событий к клиенту.

Теперь смотрим, что такое Connectable object:

Connectable object (соединяемый объект)

Это COM-объект, который реализует, как минимум, интерфейс IConnectionPointContainer для управления точкой соединения объектов. Соединяемые объекты поддерживают связь от сервера к клиенту. Соединяемый объект может создавать и управлять одной или более точками соединения подобъектов, которые получают события от интерфейсов реализованных в других объектах и посылают их клиентской стороне.

Ну как, все понятно? Мне не очень: – тогда читаем дальше.

Давайте вспомним знаменитый CALLBACK способ общения интерфейса API программ на языке C. Предположим, что у вас есть некая DLL, которая содержит в себе экспортируемую функцию, предназначенную для архивирования документов. Пусть у неё имеется два параметра. Первый – это путь к папке с документами, которую следует заархивировать, а второй: ну второй – это указатель на функцию. Callback-функцию – функцию обратного вызова.

FolderArchiving( LPCSTR lpszFolderPath, LOGCALLBACKFUNC *pfnLogCallbackFunc)

Где формат функции обратного вызова следующий:

typedef BOOL (LOGCALLBACKFUNC)(LPCSTR lpszDocPath, int nCurrenDoc);

Т.е. в эту функцию будет передаваться номер архивируемого документа и путь к нему.

Как же это все работает? Клиент определяет в своем приложении функцию с любым именем, строго имеющую

те же параметры, что описаны выше – речь идет о функции обратного вызова, – а затем передает указатель на неё в FolderArchiving. После чего DLL начинает свою работу по архивированию сообщений и периодически, перед началом упаковки каждого документа будет вызывать ту функцию, адрес которой передал ей клиент, указывая в её параметрах номер документа и путь к нему. Таким образом, клиентское приложение получает весьма симпатичный механизм наблюдения за процессом архивирования. И при желании может вести log-файл, а также отображать диалог прогресса, если таковой не реализован в DLL. Вот, собственно, что такое CALLBACK, если объяснить на пальцах в двух словах. На рисунке 1 вы видите небольшую диаграмму, схематически поясняющую процесс работы, описанный выше.

Рисунок 1

Подобный механизм "обмена" повсеместно распространен среди функций API операционной системы. Его же часто используют разработчики в своих модулях, как в приведенном выше примере.

Теперь представим ситуацию, что та DLL, о которой шла речь, получила кроме API-интерфейса ещё и COM-интерфейс. А в вашу задачу входит реализация всех API-функций в виде COM-функций. И что же мы будем делать с нашим Callback? Вот тут на помощь и придет технология Connection point! Это фактически тот же механизм обратного вызова, только приспособленный для COM модели, естественно со своими правилами и отличиями.

Модель COM не всегда имела возможность взаимодействия с помощью исходящих интерфейсов. Было время, когда она воспринималась исключительно как модель входящих интерфейсов. В этой связи, чтобы подчеркнуть важность существования Connection points, давайте смоделируем следующую ситуацию.

Пусть у нас имеется приложение PostAgent с COM-интерфейсом. Пусть оно будет ЕХЕ-сервером и может работать как самостоятельное приложение с графическим интерфейсом. В число достоинств этой программы входит работа с архивами почтовых программ. Некоторые особенности работы с почтовыми архивами оказались настолько важны, что разработчики PostAgent вынесли их в отдельные COM функции в каком-либо интерфейсе. Замечательно! Теперь вы сможете пользоваться услугами этого приложения, и ваша программа получит тот функционал, которого, возможно, вам так не хватало все это время.

Путь среди функций этого 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.

Поделиться с друзьями: