Алекс Jenter - Программирование на Visual C++. Архив рассылки
- Название:Программирование на Visual C++. Архив рассылки
- Автор:
- Жанр:
- Издательство:неизвестно
- Год:неизвестен
- ISBN:нет данных
- Рейтинг:
- Избранное:Добавить в избранное
-
Отзывы:
-
Ваша оценка:
Алекс Jenter - Программирование на Visual C++. Архив рассылки краткое содержание
РАССЫЛКА ЯВЛЯЕТСЯ ЧАСТЬЮ ПРОЕКТА RSDN, НА САЙТЕ КОТОРОГО ВСЕГДА МОЖНО НАЙТИ ВСЮ НЕОБХОДИМУЮ РАЗРАБОТЧИКУ ИНФОРМАЦИЮ, СТАТЬИ, ФОРУМЫ, РЕСУРСЫ, ПОЛНЫЙ АРХИВ ПРЕДЫДУЩИХ ВЫПУСКОВ РАССЫЛКИ И МНОГОЕ ДРУГОЕ.
Программирование на Visual C++. Архив рассылки - читать онлайн бесплатно полную версию (весь текст целиком)
Интервал:
Закладка:
importlib("stdole32.tlb");
importlib("stdole2.tlb");
[ uuid(F7222740-2296-11d5-964D-00001CDC1022) ]
dispinterface IFireClassEvents {
properties:
methods:
[id()] boolean MyEvent();
} // primary dispatch interface for cmyinterface
[ uuid(D46238C5-2277-11D5-964D-00001CDC1022) ]
dispinterface IMyInterface {
properties:
Как видите, мы объявили метод MyEvent в интерфейсе IFireClassEvents, который и будет являться нашим событием, которое мы будем «посылать» клиентскому приложению. Я оставил этот метод без параметров, а тип возвращаемого результата сделал булевским. Будем считать это маленьким капризом. Вы вольны описать этот метод, как вам будет угодно.
Далее продолжаем редактировать ODL-файл. В описании нашего кокласса MyInterface нужно добавить одну строку:
[ uuid(D46238C6-2277-11D5-964D-00001CDC1022) ]
coclass MyInterface {
[default] dispinterface IMyInterface;
[default,source] interface IFireClassEvents;
};
Замечаем отличия? Конечно! Появилось новое служебное слово source. Что оно означает? В MSDN написано, что «атрибут [source] указывает, что член кокласса, свойство или метод — это источник событий. Для членов кокласса этот атрибут означает, что этот член вызовется раньше, чем выполнится». А также есть такая фраза: «В свойстве или методе этот атрибут указывает, что член возвращает объект или VARIANT, который является источником событий. Объект реализует интерфейс IConnectionPointContainer» . Думаю, с этим все ясно. Сохраните сделанные изменения и откомпилируйте ODL-файл. Если вы все сделали правильно, то ошибок не будет.
Для каждой точки соединения, реализованной в нашем классе CMyInterface, мы должны объявить connection point part, которая реализует точку соединения. Если вы реализуете одну или более точек соединения вы также должны объявить простую карту соединений в вашем классе. Карта соединений – это таблица точек соединения, поддерживаемых ActiveX-контролом. Наш пример демонстрирует простую карту соединений и одну точку соединения. Для этого откройте файл объявления класса MyInterface.h и сразу после макроса DECLARE_INTERFACE_MAP() добавьте нижеприведенный код:
DECLARE_CONNECTION_MAP()
BEGIN_CONNECTION_PART(CMyInterface,ObjCP)
CONNECTION_IID(IID_IFireClassEvents
END_CONNECTION_PART(ObjCP)
Таким образом мы объявляем карту соединений. BEGIN_CONNECTION_PART и END_CONNECTION_PART — это макросы, объявленные во встроенном классе и наследуемые от CConnectionPoint, который реализует эту точку соединения. Если вы хотите переопределить какую-либо функцию класса CConnectionPoint или добавить функцию-член вашего родителя, тогда объявите её между двумя этими макросами. К примеру, макрос CONNECTION_IID, расположенный между двумя этими макросами, переопределяет функцию CConnectionPoint::GetIID.
Я буду немного непоследователен, однако попрошу вас сразу, раз уж вы редактируете данный заголовочный файл, подняться к объявлению класса CMyInterface и перед его объявлением вставить следующие строки:
extern const IID IID_IFireClassEvents;
interface IFireClassEvents : public IDispatch {};
Первая строка объявляет константу IID_IFireClassEvents, которую мы использовали в макросе CONNECTION_IID(IID_IFireClassEvents). Ведь все должно работать без ошибок. Однако перед описанием этой константы стоит служебное слово extern. Это значит, что она уже где-то объявлена. Поэтому я и сказал, что слегка непоследователен. Здесь все правильно. Сейчас мы закончим с заголовочным файлом и объявим этот идентификатор интерфейса в файле реализации класса CMyInterface.
Вторая строка есть объявление интерфейса. Теперь сохраните файл MyInterface.h и закройте его.
Следующим этапом добавим в файл MyInterface.cpp то, о чем собственно говорили только что выше. Найдите в коде декларацию идентификатора IID_IMyInterface и сразу после него вставьте следующие строки (только не забывайте вставлять правильные числовые значения, если вы используете uuid, отличный от того, что создала мне утилита guidgen.exe):
#pragma warning( disable : 4211)
static const IID IID_IFireClassEvents = //(F7222740-2296-11d5-964D-00001CDC1022)
{0xf7222740, 0x2296, 0x11d5, {0x96, 0x4d, 0x0, 0x0, 0x1c, 0xdc, 0x10, 0x22}};
#pragma warning(default : 4211)
Обратите внимание на две директивы препроцессора, которые окаймляют данное объявление. Первая из них гасит ненужное предупреждение компилятора, а вторая снова его разрешает. Теперь добавьте в карту интерфейсов интерфейс IConnectionPointContainer (MyInterface.cpp):
BEGIN_INTERFACE_MAP(CMyInterface, CCmdTarget)
INTERFACE_PART(CMyInterface, IID_IMyInterface, Dispatch)
INTERFACE_PART(CMyInterface, IID_IConnectionPointContainer, ConnPtContainer)
END_INTERFACE_MAP()
И наконец, сразу за картой интерфейсов вставьте карту соединений:
BEGIN_CONNECTION_MAP(CMyInterface, CCmdTarget)
CONNECTION_PART(CMyInterface, IID_IFireClassEvents, ObjCP)
END_CONNECTION_MAP()
Затем добавьте в конструктор нашего класса вызов функции EnableConnections. Эта функция является недокументированной функцией класса CCmdTarget, однако если вы не вызовете её из конструктора класса, работающего с Connection point и являющегося потомком CCmdTarget, то у вас ничего не получится.
CMyInterface::CMyInterface() {
EnableAutomation();
EnableConnections();
// To keep the application running as long as an OLE automation
// object is active, the constructor calls AfxOleLockApp.
AfxOleLockApp();
}
Сохраните редактируемый файл и постройте наш проект. Если вы делали все правильно, то не должно быть ни ошибок, ни предупреждений. На данный момент у нас готов основной каркас. Займемся реализацией непосредственно самих функций. Однако сначала я хотел бы обратить ваше внимание на следующий факт. Я думаю, ни для кого не секрет, что COM-сервер может иметь сразу нескольких клиентов. Может случиться так, что этот сервер должен посылать событие клиентам в ответ на какое-либо действие пользователя. Например, сервер имеет графический интерфейс, и когда пользователь выбирает меню Файл, сервер должен уведомить ВСЕХ ЗАИНТЕРЕСОВАННЫХ клиентов об этом. Естественно возникает 2 вопроса:
1. Каким образом сервер сможет послать событие сразу всем клиентам.
2. Как определить, какой клиент заинтересован в получении событий, а какой нет?
Начнем со второго вопроса. Для этих целей интерфейс IConnectionPoint предусматривает специальные методы подписки на события Advise и Unadvise. С помощью первого из них клиент сообщает серверу, что он желает получать уведомления относительно какого-либо события, а вторая функция, наоборот, сообщает серверу, что данный клиент больше в таких сообщениях не нуждается. Соответственно, сервер должен иметь некий список, в который он заносит клиентов, подписанных на те или иные сообщения. И из которого он потом будет удалять отписавшихся клиентов. Таким образом, когда серверу нужно будет послать некое событие клиентам, то он в цикле пробежится по этому списку и отправит сообщения всем заинтересованным клиентам. Это и есть ответ на первый вопрос. Теперь приведем процедуру, которая реализует все выше сказанное. Вставьте её код в файл MyInterface.cpp а также не забудьте объявить её в MyInterfcae.h.
Читать дальшеИнтервал:
Закладка: