Форум программистов, компьютерный форум, киберфорум
C++ Builder
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.66/270: Рейтинг темы: голосов - 270, средняя оценка - 4.66
Супер-модератор
Эксперт Pascal/DelphiАвтор FAQ
 Аватар для volvo
33379 / 21503 / 8236
Регистрация: 22.10.2011
Сообщений: 36,899
Записей в блоге: 11

Классы-перехватчики (interceptor classes)

05.02.2015, 15:06. Показов 62795. Ответов 0
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Иногда возникает необходимость изменить поведение определенного VCL-контрола, или добавить к нему функционал. Можно конечно, создать новый компонент, являющийся наследником от необходимого, добавить его на палитру и использовать. Но это не всегда оправдано. Иногда нужно изменить функционал совсем немного, и не во всех проектах, и даже не на всех формах проекта, а только в одном месте. В подобных случаях на помощь приходят классы-перехватчики (interceptor classes).

Идея очень проста: наследуемся от нужного класса, добавляем функционал или переопределяем методы, изменяя существующий функционал, а потом подсовываем компилятору свой, измененный класс вместо стандартного. Дла этого подобная подмена должна осуществляться перед описанием класса формы.


Поставим задачу: при нажатии любой кнопки на форме выполнить кроме действия, предусмотренного обработчиком события OnClick, еще какое-либо действие. В Дельфи это делается так:
Delphi
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
type
  TButton = class({Vcl.}StdCtrls.TButton) // Vcl. добавляется в новых версиях Дельфи
  public
    procedure Click; override; // Для примера - изменяем функционал метода Click
  end;
  
  TForm1 = class(TForm)
    // ...
    Button1: TButton; // У всех кнопок, лежащих на форме уже будет изменен функционал Click
    // ...
  end; { TForm1 }
  
implementation
 
{ TButton }
 
procedure TButton.Click;
begin
  inherited;
  // дополнительные действия
end;
 
// ...
Казалось бы, дословный перевод на Билдер должен выглядеть следующим образом:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
    class TButton : public Stdctrls::TButton
    {
    public:
        /*
        __fastcall virtual TButton(Classes::TComponent* AOwner)
            : Stdctrls::TButton(AOwner) {}
        */
        DYNAMIC void __fastcall Click(void)
        {
            Stdctrls::TButton::Click();
            // Дополнительные действия
        }
    };
 
// -----
class TForm1 : public TForm
{
__published: // IDE-managed Components
 
    TButton *Button1;
    // ...
, но в таком виде перехват работать не будет, компилятор сигнализирует о неоднозначности: E2015 Ambiguity between 'TButton' and 'Vcl::Stdctrls::TButton', ведь теперь у нас есть два класса с одним и тем же именем TButton. Один - в VCL, со стандартным функционалом, а второй - только что созданный, с функционалом изменённым (одноименный со стандартным, чтобы дальше в классе формы не вносить никаких правок, а использовать привычное имя компонента).

Чтобы проект успешно скомпилировался, нужно эту неоднозначность устранить, для чего поместим наш класс-перехватчик в другое пространство имен, и подменим обращения к TButton на обращения с учетом namespace-а:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
namespace My_Button
{
    class TButton : public Stdctrls::TButton
    {
    public:
        /*
        __fastcall virtual TButton(Classes::TComponent* AOwner)
            : Stdctrls::TButton(AOwner) {}
        */
 
        DYNAMIC void __fastcall Click(void)
        {
            Enabled = false;
            Stdctrls::TButton::Click();
            Enabled = true;
        }
    };
};
#define TButton My_Button::TButton
 
// -----
class TForm1 : public TForm
// ...
. Вот теперь для всех кнопок (и уже находившихся на форме Form1, и добавляемых на нее в Design-тайме, и добавляемых в рантайме) будет работать новый метод Click(), который задизейблит кнопку перед началом работы обработчика события OnClick, и восстановит кнопку после того, как этот обработчик завершится. То есть, нажать на любую кнопку во время работы ее OnClick станет просто невозможно.


Очень часто класс-перехватчик используется для изменения спецификатора доступа. Набивший оскомину пример: удаление строки/столбца из TStringGrid. В классе TCustomGrid есть методы DeleteRow/DeleteCol, но они защищенные (protected), следовательно, нельзя просто так взять и использовать их для StringGrid-а. На форуме встречались самые неожиданные решения, от правки исходников VCL (физический перенос заголовков методов в секцию public) до написания своего класса - потомка TStringGrid с "заглушками", вызывающими соответствующие методы TCustomGrid, и вызов этих методов через приведение типа указателя на контрол к указателю на потомка. Но проще всего это сделать через класс-перехватчик:
C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
namespace StringGrid_Delete
{
    class TStringGrid : public Grids::TStringGrid
    {
    public: // открываем в перехватчике оба метода
        using Grids::TStringGrid::DeleteRow;
        using Grids::TStringGrid::DeleteColumn;
    };
};
// и подсовываем перехватчик вместо стандартного компонента
#define TStringGrid StringGrid_Delete::TStringGrid
 
// -----
class TForm1 : public TForm
, теперь для всех гридов на форме есть возможность удалять строк/столбцы одним вызовом метода.


Еще один случай - это реакция контрола на сообщение Windows. Можно, конечно, воспользоваться старым добрым способом, подменить оконную функцию через SetWindowLongPtr + GWLP_WNDPROC, но проще (мне, по крайней мере) снова использовать interceptor:

Те, кто знает, как перехватываются сообщения в VCL - могут сюда не заходить :)

Для того, чтобы перехватить сообщение Windows, в VCL используются три макроса:
C++
1
2
3
4
BEGIN_MESSAGE_MAP
MESSAGE_HANDLER(перехватываемое_сообщение_Windows, тип_Message, название_процедуры_обработчика)
    // ... MESSAGE_HANDLER может использоваться многократно
END_MESSAGE_MAP(класс_родитель)
и методы-обработчики (по одному для каждого перехватываемого сообщения)
C++
1
MESSAGE void __fastcall название_процедуры_обработчика(тип_Message &message);
Теперь, в случае когда компонент получает одно из перехватываемых_сообщений_Windows, перечисленных в таблице сообщений (message map), вызывается соответствующий полученному сообщению метод название_процедуры_обработчика

Подробнее можно прочитать здесь: Таблицы откликов


C++
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
namespace ListBox_DragDrop
{
    class TListBox : public Stdctrls::TListBox
    {
    protected:
        MESSAGE void __fastcall WMDropFiles(TWMDropFiles &message)
        {
            wchar_t chName[MAX_PATH];
            HDROP hdropHandle=(HDROP)message.Drop;
            DragQueryFileW(hdropHandle,0,chName,MAX_PATH);
            Items->Add(chName);
        }
    public:
        __fastcall virtual TListBox(Classes::TComponent* AOwner)
            : Stdctrls::TListBox(AOwner) {}
            
        // перехватываем сообщение WM_DROPFILES, приходящее в ListBox 
        BEGIN_MESSAGE_MAP
          MESSAGE_HANDLER(WM_DROPFILES, TWMDropFiles, WMDropFiles);
        END_MESSAGE_MAP(Stdctrls::TListBox);
    };
}
#define TListBox ListBox_DragDrop::TListBox
 
// -----
class TForm1 : public TForm
, осталось разрешить для ListBox-а перетаскивание файлов вызовом DragAcceptFiles, и больше уже ничего делать не нужно. А теперь представьте, сколько понадобилось бы написать (при использовании SetWindowLongPtr), если на форме находится, скажем, 5 ListBox-ов...


Ну, и немного о том, как определить, в какой области видимости должны располагаться те или иные методы, и какие методы вообще можно перегружать. Поиск в гугле по названию класса (на сайте онлайн-документации Embarcadero)
Code
1
tbutton site:docwiki.embarcadero.com
выводит на нужную страничку:

Выбираем в верхней строке Methods (или Properties), и получаем список всех методов/свойств класса с указанием области видимости:

. Чтобы увидеть, как именно должен описываться заголовок - переходим к описанию нужного метода:

, копируем оттуда заголовок, и помещаем его в нужную секцию класса-перехватчика...

Не по теме:

Пример приведен для онлайн-документации, потому что
1) далеко не все устанавливают себе файл справки
2) в XE и выше очень неудобная справка, гораздо удобнее пользоваться docwiki.embarcadero.com

48
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
05.02.2015, 15:06
Ответы с готовыми решениями:

Перехватчики
Собственно, Была проблема, не заходило на некоторые сайты, исправил. Затем решил AVZ утилтой прогнать и обнаружил что есть перехватчики....

Перехватчики №2
Собственно, Была проблема, не заходило на некоторые сайты, исправил. Затем решил AVZ утилтой прогнать и обнаружил что есть перехватчики....

Хуки (глобальные перехватчики событий от клавиатуры)
Знаю, что подобные темы имеются в просторах интернета, но все равно у меня не получается реализовать... Собственно нужно, чтобы при...

0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
05.02.2015, 15:06
Помогаю со студенческими работами здесь

Inner classes
Зашорился в задачу: необходимо распечатать номер, которому звонишь по индексу. public class MyPhone { public MyPhone() { ...

Classes
Здравствуйте. Подскажите пожалуйста. У меня есть структура проекта. Из всей структуры я обращаю внимание на 1 папку classes. Собственно...

Classes must not be nested
написал только: class sokd {} и выдает ошибку 1131, если пробую создавать класс в пакете, выдает такую же ошибку(1037: Packages cannot...

Visual C/C++ Classes
Уважаемые программисты! :gsmile: Помогите, пожалуйста, разобраться с программой. Цель такова:___________________________________ ...

Compare classes
как сравнить через полиморфизм результаты методов классов которые реализуют один и тот же интерфейс?


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
1
Закрытая тема Создать тему
Новые блоги и статьи
SDL3 для Web (WebAssembly): Работа со звуком через SDL3_mixer
8Observer8 08.02.2026
Содержание блога Пошагово создадим проект для загрузки звукового файла и воспроизведения звука с помощью библиотеки SDL3_mixer. Звук будет воспроизводиться по клику мышки по холсту на Desktop и по. . .
SDL3 для Web (WebAssembly): Основы отладки веб-приложений на SDL3 по USB и Wi-Fi, запущенных в браузере мобильных устройств
8Observer8 07.02.2026
Содержание блога Браузер Chrome имеет средства для отладки мобильных веб-приложений по USB. В этой пошаговой инструкции ограничимся работой с консолью. Вывод в консоль - это часть процесса. . .
SDL3 для Web (WebAssembly): Обработчик клика мыши в браузере ПК и касания экрана в браузере на мобильном устройстве
8Observer8 02.02.2026
Содержание блога Для начала пошагово создадим рабочий пример для подготовки к экспериментам в браузере ПК и в браузере мобильного устройства. Потом напишем обработчик клика мыши и обработчик. . .
Философия технологии
iceja 01.02.2026
На мой взгляд у человека в технических проектах остается роль генерального директора. Все остальное нейронки делают уже лучше человека. Они не могут нести предпринимательские риски, не могут. . .
SDL3 для Web (WebAssembly): Вывод текста со шрифтом TTF с помощью SDL3_ttf
8Observer8 01.02.2026
Содержание блога В этой пошаговой инструкции создадим с нуля веб-приложение, которое выводит текст в окне браузера. Запустим на Android на локальном сервере. Загрузим Release на бесплатный. . .
SDL3 для Web (WebAssembly): Сборка C/C++ проекта из консоли
8Observer8 30.01.2026
Содержание блога Если вы откроете примеры для начинающих на официальном репозитории SDL3 в папке: examples, то вы увидите, что все примеры используют следующие четыре обязательные функции, а. . .
SDL3 для Web (WebAssembly): Установка Emscripten SDK (emsdk) и CMake для сборки C и C++ приложений в Wasm
8Observer8 30.01.2026
Содержание блога Для того чтобы скачать Emscripten SDK (emsdk) необходимо сначало скачать и уставить Git: Install for Windows. Следуйте стандартной процедуре установки Git через установщик. . . .
SDL3 для Android: Подключение Box2D v3, физика и отрисовка коллайдеров
8Observer8 29.01.2026
Содержание блога Box2D - это библиотека для 2D физики для анимаций и игр. С её помощью можно определять были ли коллизии между конкретными объектами. Версия v3 была полностью переписана на Си, в. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru