- Фантастика и фэнтези
- Ироническое фэнтези
- Стимпанк
- Детективная фантастика
- Киберпанк
- Попаданцы
- LitRPG
- Космоопера
- Технофэнтези
- Городская фантастика
- Русское фэнтези
- Юмористическая фантастика
- Космическая фантастика
- Боевое фэнтези
- Эпическая фантастика
- Зарубежная фантастика
- Книги магов
- Мистика
- Разная фантастика
- Разное фэнтези
- Любовное фэнтези
- Романтическая фантастика
- Городское фентези
- Историческая фантастика
- Историческое фэнтези
- Социально-философская фантастика
- Сказочная фантастика
- Иностранное фэнтези
- Романтическое фэнтези
- Ироническая фантастика
- Ужасы и Мистика
- Постапокалипсис
- Героическая фантастика
- Фэнтези
- Научная Фантастика
- Социально-психологическая
- Альтернативная история
- Боевая фантастика
- Разная литература
- Пословицы, поговорки
- Шахматы
- Визуальные искусства
- Фанфик
- Авто и ПДД
- Военное
- Отраслевые издания
- Гиды, путеводители
- Зарубежная образовательная литература
- Военная история
- Цитаты из афоризмов
- Современная литература
- Великолепные истории
- Начинающие авторы
- Современная зарубежная литература
- Боевые искусства
- Изобразительное искусство, фотография
- Кино
- Литература 19 века
- Недвижимость
- Музыка, музыканты
- Готические новеллы
- Истории из жизни
- Культура и искусство
- Спецслужбы
- Зарубежная прикладная литература
- Музыка, танцы
- Подростковая литература
- Прочее
- Военная техника, оружие
- Газеты и журналы
- Периодические издания
- Домоводство, Дом и семья
- Любовные романы
- Экономическая литература
- Научные и научно-популярные книги
- О животных
- Биохимия
- Культурология
- Юриспруденция
- Политика
- Психотерапия
- Языкознание
- Научпоп
- Медицина
- Психология, личное
- Радиотехника
- Филология
- Педагогика
- Образовательная литература
- Деловая литература
- Физика
- Науки: разное
- Химия
- Воспитание детей, педагогика
- Детская психология
- Зарубежная публицистика
- Биология
- Архитектура
- Зарубежная психология
- Науки о космосе
- Социология
- Математика
- География
- Беременность, ожидание детей
- Литературоведение
- Транспорт, военная техника
- Обществознание
- Зоология
- География
- Альтернативная медицина
- Иностранные языки
- Ветеринария
- Рефераты
- Астрология
- Биофизика
- Экология
- История Европы
- Учебники
- Шпаргалки
- Государство и право
- Ботаника
- Религиоведение
- История
- Техническая литература
- Прочая научная литература
- Психология
- Детективы и Триллеры
- Справочная литература
- Проза
- Новелла
- Юмор
- Проза
- Историческая проза
- Русская современная проза
- Советская классическая проза
- Повести
- Очерки
- Эссе
- Рассказы
- Зарубежная современная проза
- Сентиментальная проза
- Зарубежная классика
- Афоризмы
- Эпистолярная проза
- Феерия
- Семейный роман/Семейная сага
- Разное
- Антисоветская литература
- Магический реализм
- Русская классическая проза
- Современная проза
- О войне
- Контркультура
- Классическая проза
- Поэзия, Драматургия
- Детская литература
- Детская фантастика
- Детские остросюжетные
- Детские стихи
- Детские приключения
- Внеклассное чтение
- Учебная литература
- Зарубежные детские книги
- Бизнес
- Загадки
- Книги для подростков
- Школьные учебники
- Детский фольклор
- Книги для дошкольников
- Детские детективы
- Детская познавательная и развивающая литература
- Буквари
- Прочая детская литература
- Сказка
- Детская проза
- Детская образовательная литература
- Юмор
- Документальные книги
- Бизнес
- Внешнеэкономическая деятельность
- Тайм-менеджмент
- Кадровый менеджмент
- О бизнесе популярно
- Управление, подбор персонала
- Бизнес
- Менеджмент и кадры
- Продажи
- Экономика
- Ценные бумаги и инвестиции
- Малый бизнес
- Делопроизводство, офис
- Корпоративная культура, бизнес
- Банковское дело
- Финансы
- Зарубежная деловая литература
- Ораторское искусство / риторика
- Личные финансы
- Интернет-бизнес
- Поиск работы
- Государственное и муниципальное управление
- Бухучет и аудит
- Работа с клиентами
- Менеджмент
- Краткое содержание
- Личная эффективность
- Переговоры
- Приключения
- Старинная литература
- Религия и духовность
- Компьютеры и Интернет
- Блог
Читаем без скачивания Программирование на Visual C++. Архив рассылки - Алекс Jenter
Шрифт:
Интервал:
Закладка:
СТАТЬЯ
Подключение к событиям объектной модели DHTML при использовании WebBrowser-control
Автор: Тимофей Чадов
Демонстрационное приложение – event.zip (181 Kb)
ВведениеВ последнее время путешествуя по форумам и группам новостей все чаще можно встретить вопросы, касающиеся ActiveX-элемента WebBrowser. К сожалению зачастую эти вопросы теряются в общем ворохе сообщений, а материалов касающихся использования функциональности Internet Exploler катастрофически мало. В то же время у этих технологий находиться все больше поклонников. Уж слишком заманчиво выглядит возможность написать собственный броузер, приложив минимум усилий.
Например, текущая версия продукта, в разработке которой я принимаю участие, использует эти технологии для отображения информации из документно-ориентированной базы данных. При этом, как показывает практика, интерфейс web-представлений в данном случае, гораздо дружественнее пользователю, чем использование множества диалоговых форм, а чтобы его сменить достаточно подправить пару шаблонов в ресурсах.
Предлагаемая вашему вниманию статья посвящена одному из аспектов написания подобных программ. В ней будет рассмотрено как можно подключиться с событиям объектной модели броузера, а значит позволить вашему приложению использовать те преимущества, которые дает DHTML. Я попытаюсь рассказать вам как непосредственно обрабатывать события, возбуждаемые объектной моделью броузера в процессе работы пользователя со страницей.
Немного теорииКак и большинство ActiveX элементов WebBrowser является источником событий подключаемых через стандартный механизм Connection Point. К числу таких событий относятся OnBeforeNavigate, OnDocumentComplete и т.п. Несомненно они важны для управления приложением в целом, однако их возможностей явно недостаточно, если мы захотим более тесно познакомиться с DOM DHTML, например, узнать о перемещении мыши над элементами страницы или о нажатии клавиши на клавиатуре или вообще быть в курсе всех событий, которые можно использовать в сценариях DHTML.
При использовании объектной модели DHTML из сценариев возможно создавать собственные обработчики событий простым присваиванием наблюдаемого элемента соответствующим свойствам, например:
<SCRIPT LANGUAGE="jscript">
function mousedownhandler() {
// функция обработчик
}
function afterPageLoads() {
someElement.onmousedown = mousedownhandler;
}
</SCRIPT>
Возникает вопрос. А можно ли получить нечто подобное из клиента на C++? Конечно, причем похожим образом. Достаточно создать свою функцию обработки и зарегистрировать ее.
Теперь поподробнее. Для этого нужно проделать не так уж и много. Необходимо реализовать простой Com-объект для каждой функции обработчика. Этот объект должен реализовывать всего два стандартных интерфейса IUnknown и IDispatch. Далее ссылка на этот объект присваивается соответствующему свойству элемента, событие которого мы хотим наблюдать. При возникновении события броузер просто вызовет IDispatch::Invoke нашего объекта со значением DISPID = DISPID_VALUE (=0).
Замечу, что это не единственный способ заставить приложения реагировать на события. Например, можно заставить работать механизм window.external. Тогда соответствующий скрипт будет выглядеть например, так:
function mousedownhandler() {
// функция обработки window.external.onmousedown;
}
Думаю идея понятна. Однако этот способ удобен если мы сами формируем страницу. Сегодня мы пойдем первым путем.
Пишем шаблоный классИтак, настало время применить все вышеизложенное на практике. Конечно, можно руками написать COM-объекты для каждой функции, однако представьте, что число обработчиков переваливает за десяток, а каждый раз нужно реализовывать по сути одно и тоже. Думаю, понятно, к чему я клоню :) Самое время вспомнить о шаблонах C++. Итак, напишем простенький шаблонный класс. Чтобы не зависеть от конкретной библиотеки реализуем пару IUnknown, IDispatch вручную. Реализация IUnknown вполне стандартна, а из IDispatch необходимо реализовать только функцию Invoke.
template <class T>
class CHtmlEventObject : public IDispatch {
typedef void (T::*EVENTFUNCTIONCALLBACK)(DISPID id, VARIANT* pVarResult);
public:
CHtmlEventObject() { m_cRef = 0; }
~CHtmlEventObject() {}
HRESULT __stdcall QueryInterface(REFIID riid, void** ppvObject) {
*ppvObject = NULL;
if (IsEqualGUID(riid, IID_IUnknown)) *ppvObject = reinterpret_cast<void**>(this);
if (IsEqualGUID(riid, IID_IDispatch)) *ppvObject = reinterpret_cast<void**>(this);
if (*ppvObject) {
((IUnknown*)*ppvObject)->AddRef();
return S_OK;
} else return E_NOINTERFACE;
}
DWORD __stdcall AddRef() {
return InterlockedIncrement(&m_cRef);
}
DWORD __stdcall Release() {
if (InterlockedDecrement(&m_cRef) == 0) {
delete this;
return 0;
}
return m_cRef;
}
STDMETHOD(GetTypeInfoCount)(unsigned int FAR* pctinfo) {
return E_NOTIMPL;
}
STDMETHOD(GetTypeInfo)(unsigned int iTInfo, LCID lcid, ITypeInfo FAR* FAR* ppTInfo) {
return E_NOTIMPL;
}
STDMETHOD(GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames,
LCID lcid, DISPID FAR* rgDispId) {
return S_OK;
}
STDMETHOD(Invoke)(DISPID dispIdMember, REFIID riid, LCID lcid,
WORD wFlags, DISPPARAMS* pDispParams, VARIANT* pVarResult,
EXCEPINFO * pExcepInfo, UINT * puArgErr) {
if (DISPID_VALUE == dispIdMember) (m_pT->*m_pFunc)(m_id, pVarResult);
else TRACE(_T("Invoke dispid = %dn"), dispIdMember);
return S_OK;
}
public:
static LPDISPATCH CreateHandler(T* pT,
EVENTFUNCTIONCALLBACK pFunc, DISPID id) {
CHtmlEventObject<T>* pFO = new CHtmlEventObject<T>;
pFO->m_pT = pT;
pFO->m_pFunc = pFunc;
pFO->m_id = id;
return reinterpret_cast<LPDISPATCH>(pFO);
}
protected:
T* m_pT;
EVENTFUNCTIONCALLBACK m_pFunc;
DISPID m_id;
long m_cRef;
};
Как применять этот класс? Проще простого.
Шаг 1. Создаем свою функцию обработчик по прототипу onevent(dispid id, VARIANT* pVarResult). В принципе ее можно разместить где угодно. Я предпочитаю создавать ее в классе представления, наследнике CHtmlView. При этом все обработчики сосредоточены в одном месте и не нужно беспокоится о взаимодействии с классом документа.
Шаг 2. Регистрируем ее в качестве обработчика интересующего нас события. Для этого через вызов CHtmlEventObject::CreateObject создаем экземпляр нашего COM-объекта. Передаем в него адрес функции обработчика и собственный идентификатор события. После этого передаем ссылку на него интересующему нас элементу.
// Создаем объект-обработчик
LPDISPATCH dispFO = CHtmlEventObject<CEventView>::CreateHandler(this, OnKeyDown, 1);
VARIANT vIn;
V_VT(&vIn) = VT_DISPATCH;
V_DISPATCH(&vIn) = dispFO;
// устанавливаем обработчик document.onkeydown
hr = pHtmlDoc->put_onkeydown(vIn);
Здесь есть одна тонкость. Зарегистрировать обработчик можно только тогда, когда документ уже загружен, иначе GetHtmlDocument() вернет NULL. Для этого можно отслеживать событие OnDocumentComplete. Ну вот собственно и все.
Получаем информацию о событииРассмотрим еще раз прототип функции обработчика
OnEvent(DISPID id, VARIANT* pVarResult);
В качестве id передается значение которое мы указали при регистрации обработчика. Это может пригодиться если мы захотим реализовать обработку нескольких событий в одной функции. Для этого нужно зарегистрировать одну и ту же функцию с разными DISPID, тогда по приходу событий мы сможет их различать.
pVarResult нужно, если не требуется обработки по умолчанию. При этом достаточно в pVarResult вернуть VARIANT_FALSE.
Итак, когда вызывается наш обработчик никакой дополнительной информации о событии в функцию не передается. А как же тогда поподробнее узнать, что произошло? Для этого необходимо воспользоваться интерфейсом IHTMLEventObj, доступным через объект window текущего документа. Посредством этого интерфейса можно получить подробную информацию о произошедшем событии, например, элемент, послуживший источником событий, состояние клавиш, местоположение курсора мыши и состояние ее кнопок.
Вот его краткое описание из MSDN:
Методы IHTMLEventObj
get_altKey Состояние клавиши Alt get_button Возвращает информацию о нажатых кнопках мыши get_cancelBubble Возвращает будет ли продолжена обработка события вверх по иерархии обработчиков get_clientX Возвращает горизонтальную позицию курсора мыши относительно клиентской области окна get_clientY Возвращает вертикальную позицию курсора мыши относительно клиентской области окна get_ctrlKey Состояние клавиши Ctrl get_fromElement Возвращает указатель на интерфейс IHTMLElement позволяющий получить доступ к элементу с которого "ушел" курсор мыши при событиях onmouseover или onmouseout. get_keyCode Возвращает код нажатой клавиши get_offsetX Возвращает горизонтальную позицию курсора относительно контейнера элемента get_offsetY Возвращает позицию курсора относительно контейнера элемента get_qualifier Возвращает идентификатор события get_reason Возвращает состояние передачи данных для объекта источника данных get_returnValue Возвращаемое значение события или диалога get_screenX Горизонтальная координата относительно координат экрана get_screenY Вертикальная координата относительно координат экрана get_shiftKey Состояние клавиши Shift get_srcElement Возвращает указатель на интерфейс IHTMLElement послуживший источником событий get_srcFilter Возвращает объект фильтр возбудивший событие onfilterchange get_toElement Возвращает указатель на интерфейс IHTMLElement позволяющий получить доступ к элементу с на который "пришел" курсор мыши при событиях onmouseover или onmouseout get_type Возвращает строковое название события get_x Возвращает горизонтальную позицию мыши относительно родительского объекта в иерархии, позиционированного с помощью атрибутов CSS get_y Возвращает вертикальную позицию мыши относительно родительского объекта в иерархии, позиционированного с помощью атрибутов CSS put_cancelBubble Задать будет ли продолжена обработка события вверх по иерархии обработчиков put_keyCode Задать код нажатой клавиши put_returnValue Задать возвращаемое событием значениеСтоит заметить, что интерфейс IHTMLEventObj доступен только на время обработки конкретного события. При этом не все свойства в контексте определенного события имеют смысл. Например, значения возвращаемые функциями get_fromElement и get_toElement доступны только при обработке событий мыши onmouseover и onmouseout.