Nashev
@Nashev

Независимо прицепить несколько обработчиков WindowProc произвольному наследнику TWinControl?

Есть как минимум три компонента, которые, получив ссылку на TWinControl, хотят начать корректировать своё положение при изменении положения того контрола, и ещё как-то реагировать на приходящие ему события.


Все три, независимо друг от друга, могут добавляться и удаляться в произвольном порядке. И в рантайме, и в дизайн-тайме.


Все три, в принципе, у меня в исходниках — но подстава: относятся к разным пакетам компонентов и соответственно я не могу их исправить так, чтобы они пользовались каким-то моим общим для них вспомогательным классом — менеджером очереди подменных WindowProc. Кроме того, возможно ещё кто-то тоже пользуется таким-же методом, но я его ещё не обнаружил.


Сейчас они все трое для паразитирования пользуются одним и тем же очень популярным подходом, думая что каждый из них уникален:

procedure T***.PatchControl;
begin
  FOldWndProc := FLinkedControl.WindowProc;
  FLinkedControl.WindowProc := MyWndProc;
end;

procedure T***.UnpatchControl;
begin
  FLinkedControl.WindowProc := FOldWndProc;
end;



Естественно, у каждого свой аналог первого кода вызывается при привязке, второго — при отвязке. Столь же естественно, что когда привязываются несколько таких паразитов а отвязываться они начинают не в обратном порядке, а в любом другом, то отвязывающиеся позднее привязывают обратно те обработчики от уже отвязавшихся, ссылки на которые которые они запомнили когда привязывались после них.


Чтобы убедиться в этом, я повтыкал проверку такого вот вида:

procedure T***.UnpatchControl;
var
  m: TWndMethod;
begin
  m := MyWndProc;
  Assert((TMethod(FLinkedControl.WindowProc).Code = TMethod(m).Code), 'unlink only own handler to prevent errors');

  FLinkedControl.WindowProc := FOldWndProc;
end;



Естественно, ассерты стали сыпаться, естественно даже в IDE с его дизайнером форм. А до того — время от времени сыпались не ассерты, а сама IDE.


Вопрос — чё с этим делать-то?? Голову уже сломал…


P.S. Возможно, как-то иначе перехатывать только WM_WindowPosChanged меня тоже бы устроило…
  • Вопрос задан
  • 3138 просмотров
Решения вопроса 1
debose
@debose
Я бы сделал так:
1) В первую очередь проверил, нельзя ли тоже самое реализовать не перекрывая WndProc, например с помощью TAppEvents.OnMessage.
2) Если нет, то:
Создал отдельный пакет. В нём сделал бы отдельный компонент, позволяющий организовать цепочку вызовов WinProc. Ну собственно, у вас в коде такой класс уже есть. T***(.UnpatchControl_. Если таких классов много, то имеет смысл сделать у них общего наследника. Или изменить класс T*** так, чтобы PatchControl делегировал вызов экземпляру TMyWndProcQueeHandler (прокси).

Класс бы выглядел примерно так:
type
  TMyWndProcQueeHandler = class(TComponent)
  private
    FWndProcsList: array of TWndMethod; // или TList<TWndMethod>
  public
     // здесь присваиваем контролу MyWndProc в качестве WndProc
     // а оригинальный добавлем в FWndProcsList
     procedure PatchControl;

     // здесь соответственно восстанавливаем оригинальный WndProc
     procedure UnpatchControl;

     // здесь вызываем WndProc-ы в нужном порядке. (начиная с последнего (кастомного) до 0-го (оригинального))
     procedure MyWndProc(var Message: TMessage);

     // тут просто добавляем aWndProc в список
     procedure AddWndProc(const aWndProc: TWndProc);
     
     // ну и конечно надо ещё сделать конструктор/деструктор. Опционально переопределить Notification, чтобы получать уведомления от освобожденных связанных компонентов.
    // И добавить property LinkedControl с setter-ом.
end;


Для каждого WinControl-a в котором нужно что-то переопределять, создавать по экземпляру TMyWndProcQueeHandler.
Ответ написан
Пригласить эксперта
Ответы на вопрос 1
@ComodoHacker
А если сделать прокси-WinControl и подсовывать им? А в нем все разрулить.
Ответ написан
Комментировать
Ваш ответ на вопрос

Войдите, чтобы написать ответ

Войти через центр авторизации
Похожие вопросы