Обработка событий COM в Delphi: идентификация элементов через HTMLElementEvents2
Вопрос обработки событий в среде COM часто встает перед разработчиками, использующими Delphi. Одной из задач является идентификация объекта, который вызвал определенное событие. Рассмотрим на примере интерфейса HTMLElementEvents2 из MSHTML, как можно идентифицировать элемент, который вызвал обработчик события.
Проблема
Когда мы работаем с событиями COM, особенно с интерфейсом HTMLElementEvents2, нам может потребоваться определить, какой именно HTML-элемент вызвал обработчик события. В случае использования элементов, которые производят наследование от TInterfacedObject и реализуют интерфейс HTMLElementEvents2, в методах событий можно идентифицировать конкретные элементы и получать доступ к их свойствам.
Существует два основных подхода к реализации обработчиков событий COM:
Определение отдельных методов для каждого события, что может быть громоздким и требовать много кода.
Использование класса TEventDispatch из модуля OleCtrls.Pas, который позволяет привязать обработчик к одному событию и делает реализацию более компактной.
Альтернативный ответ
Используя второй подход, важно знать, как внутри реализации метода Invoke идентифицировать вызвавший объект. Однако, как было замечено в вопросе, попытки сделать это через аргументы метода Invoke не приводят к успеху, так как в них отсутствуют какие-либо данные о вызвавшем объекте.
function TEventObject.Invoke(DispID: Integer; const IID: TGUID;
LocaleID: Integer; Flags: Word; var Params; VarResult, ExcepInfo, ArgErr: Pointer): HResult;
var
vPDispParams: PDispParams;
begin
vPDispParams := PDispParams(@Params);
// Проверка не приносит результатов, так как не содержит pEvtObj
end;
Решение проблемы
С помощью комментариев от @IgorTandetnik, удалось найти решение. Ошибка была в том, что привязка обработчика к элементу происходила не через интерфейс ConnectionPoint, а напрямую к свойству onclick. Исправление заключалось в использовании ConnectionPoint для привязки обработчика к интерфейсу событий:
ITE := IDispatch(V) as IHtmlInputTextElement;
Assert(ITE <> Nil);
CPC := ITE as IConnectionPointContainer;
Assert(CPC <> Nil);
OleCheck(CPC.FindConnectionPoint(HTMLInputTextElementEvents2, CP));
OleCheck((CP as IConnectionPoint).Advise(DocEvent, Cookie));
После этого, в методе Invoke можно было идентифицировать объект, вызвавший событие, следующим образом:
function TEventObject.Invoke(...): HResult;
begin
// ...
V := OleVariant(vPDispParams^.rgvArg^[0]);
IDisp := IDispatch(V);
if Supports(IDisp, IHTMLEventObj, IEvtObj) then begin
IHE := IEvtObj.srcElement;
IHE.QueryInterface(IHtmlInputTextElement, ITE);
end;
end;
Заключение
Таким образом, для идентификации объекта, вызвавшего событие COM, необходимо корректно использовать ConnectionPoint для привязки обработчика к интерфейсу событий. В противном случае, информация о вызвавшем объекте может быть недоступна.
Описание контекста: Вопрос касается обработки событий COM в Delphi, с акцентом на идентификацию HTML-элементов через интерфейс HTMLElementEvents2.
Комментарии и вопросы
Получайте свежие новости и обновления по Object Pascal, Delphi и Lazarus прямо в свой смартфон. Подпишитесь на наш Telegram-канал delphi_kansoftware и будьте в курсе последних тенденций в разработке под Linux, Windows, Android и iOS
Материалы статей собраны из открытых источников, владелец сайта не претендует на авторство. Там где авторство установить не удалось, материал подаётся без имени автора. В случае если Вы считаете, что Ваши права нарушены, пожалуйста, свяжитесь с владельцем сайта.