Как на одно событие можно исполнить несколько процеду

Общие вопросы программирования, алгоритмы и т.п.

Модератор: Модераторы

Как на одно событие можно исполнить несколько процеду

Сообщение carrots » 24.07.2009 00:33:22

В чем состоит не решенная задача:
Во время работы программы присвоить параметрам нужного объекта по несколько функций или процедур. Сделать так чтоб на одно событие исполнялось несколько процедур.
Разнообразие методов и процедур может быть неограниченно, потому вручную создать процедуры для каждого типа событий не выйдет.


Как примерно можно это реализовать:

Получаем с помощью GetPropList неопределенное количество параметров типа tkMethod (функций и процедур — OnClick, OnActivate, OnMouseDown, OnMouseMove, OnCloseQuery и другие).

С помощью SetMethodProp присваиваем событию некую процедуру которая будет исполнять нужные нам процедуры.

Такой метод работает но не передает параметры процедур. :-(


Мож кто встречался? Как можно решить такую задачу? :roll:
Аватара пользователя
carrots
постоялец
 
Сообщения: 138
Зарегистрирован: 28.03.2008 02:13:02

Re: Как на одно событие можно исполнить несколько проце

Сообщение wavebvg » 24.07.2009 00:49:30

Организовать хранение всех процедур в списке и предусмотреть запуск всего, добавленного в список по событию...
wavebvg
постоялец
 
Сообщения: 354
Зарегистрирован: 28.02.2008 04:57:35

Re: Как на одно событие можно исполнить несколько проце

Сообщение carrots » 24.07.2009 01:33:16

Это не проблема...
Проблема в том что нужно вызывать эти функции из абсолютно разных собитий которые на момент написания программы вообще не существовали или не были прописаны.

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

Когда мы создаем процедуру которая на определенное событие вызывает процедуры мы не знаем какие параметры данное событие передает, так как каждый тип события передает в процедуру свои параметры (
TnotifyEvent - Sender: Tobject,
ThelpEvent - Command: Word; Data: Longint; var CallHelp: Boolean,
TgetStrProc - const S: string)
Таких типов может быть не ограниченное количество, потому нельзя создать процедуру для каждого типа которая вызовет другие процедури и передаст им параметры.
А если создать процедуру к примеру типа TnotifyEvent и присвоить ее на TmouseMoveEvent — она передаст только первый параметр (Sender: Tobject) так как у нас нет возможности узнать остальные параметры переданные нашей процедуре.
Аватара пользователя
carrots
постоялец
 
Сообщения: 138
Зарегистрирован: 28.03.2008 02:13:02

Re: Как на одно событие можно исполнить несколько проце

Сообщение Inferno » 24.07.2009 06:17:00

по мне дык здесь без прокладки не обойтись между вызванной и вызываемыми процедурами... вызванная должна знать что за тип: procedure of object... но думаю можно узнать по RTTI сколько параметров нужно передавать вызвав GetTypeData(PPropInfo^.PPropInfo) каторая вернет структуру
Код: Выделить всё
ParamCount : Byte;
               ParamList : array[0..1023] of Char
             {in reality ParamList is a array[1..ParamCount] of:
                  record
                    Flags : TParamFlags;
                    ParamName : ShortString;
                    TypeName : ShortString;
                  end;
              followed by
                  ResultType : ShortString}     

Но как их дальше передавать для всех универсально? не представляю......как это сделать без asm???

сам не пробовал.... получиться отпишись мне тоже интересно, ибо в проекте что то похожее есть....
Аватара пользователя
Inferno
новенький
 
Сообщения: 78
Зарегистрирован: 20.03.2009 14:40:20
Откуда: Тюмень

Re: Как на одно событие можно исполнить несколько проце

Сообщение carrots » 24.07.2009 08:24:01

Пока ниче не нашел... :(
Временно создал процедуры для основних видов событий, пока на них работает...

Все таки вопрос остается:
Как узнать какие параметры и какие значения параметров были переданы в процедуру, и как их передать в следующую неизвестного типа процедуру?
Аватара пользователя
carrots
постоялец
 
Сообщения: 138
Зарегистрирован: 28.03.2008 02:13:02

Re: Как на одно событие можно исполнить несколько проце

Сообщение Inferno » 24.07.2009 09:46:34

Как узнать какие параметры и какие значения параметров были переданы в процедуру

выше постом по-моему я ответил "Как узнать?", просто нужно попробовать...
как передать пробовать на ассемблеровскими вставками push TMethod(medot).Data, параметров в нужном порядке, потом call твоего метода TMethod(medot).Code ... задача не тривиальная
Аватара пользователя
Inferno
новенький
 
Сообщения: 78
Зарегистрирован: 20.03.2009 14:40:20
Откуда: Тюмень

Re: Как на одно событие можно исполнить несколько проце

Сообщение carrots » 24.07.2009 10:22:35

Может я что-то не понял, но по моему вы описали как получить количество и тип параметров которые должны были быть переданы в процедуру согласно типу события(это не проблема, я пользуюсь этими методами), но нужно узнать какие параметры были переданы и их значения.
Аватара пользователя
carrots
постоялец
 
Сообщения: 138
Зарегистрирован: 28.03.2008 02:13:02

Re: Как на одно событие можно исполнить несколько проце

Сообщение Inferno » 24.07.2009 12:04:02

пардон ,... :? да скорее всего я суть маленько не понял.....
Аватара пользователя
Inferno
новенький
 
Сообщения: 78
Зарегистрирован: 20.03.2009 14:40:20
Откуда: Тюмень

Re: Как на одно событие можно исполнить несколько проце

Сообщение MageSlayer » 26.07.2009 19:24:39

Думается, что несколько решений найти можно.
Правда есть ощущение, что произведение количества правок исходников компонент на количество хаков будет для этих решений величиной постоянной :)

Придется написать функцию-диспетчер, которая будет заниматься тем, что вызывать обработчики из списка.
Переменный список параметров событий можно хранить в типе array of const. Для заталкивания этих параметров в стек тоже придется написать функцию :)

Первое (условно чистое решение)
Для компонент, на события которых нужно вешать такие списки обработчиков, переопределяются все нужные методы, типа DoClick, DoShow и т.д. Попутно, видимо, придется навставлять virtual или dynamic для тех методов, где они статические.
Дальше, вместо просто вызова обработчиков в этих переопределенных функциях надо вызвать вашу функцию диспетчер с идентификатором типа события.

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

Удачи.
MageSlayer
постоялец
 
Сообщения: 216
Зарегистрирован: 07.09.2006 12:30:44

Re: Как на одно событие можно исполнить несколько проце

Сообщение carrots » 27.07.2009 01:07:21

Спасибо MageSlayer,
но если я тебя правельно понял - в этих решениях есть ряд недопустимых недостатков.
В первом:
1.Этот метод будет работать только со специальными компонентами — это не допустимо.
2.Будет очень сложно объяснить разработчику плагина как именно он должен переписать свои компоненты.
3.Это повлечет за собой много дополнительной работы.

Относительно второго варианта:
В том то и стоит вопрос что мы не знаем как “выкусывать список параметров из стека и регистров” и как их передать в нужную нам функцию.

Выше я описывал более проще и гибче первого варианта:

Создаем процедуры для каждого типа событий которые будут вызывать соответствующие обработчики из массива
Код: Выделить всё
procedure TCEvent.ExecuteNotifyEvent(Sender: TObject);
var
  i : integer;
  proc: TNotifyEvent;
begin
  for i := 0 to Length(fMethods)-1 do
  begin
    proc:= TNotifyEvent(fMethods[i]);
    proc(Sender);

  end;
end;

procedure TCEvent.ExecuteMouseEvent(Sender: TObject; Button: TMouseButton;
  Shift: TShiftState; X, Y: Integer);
var
  i : integer;
  proc: TMouseEvent;
begin
  for i := 0 to Length(fMethods)-1 do
  begin
    proc:= TMouseEvent(fMethods[i]);
    proc(Sender, Button, Shift, X, Y);

  end;
end;

procedure TCEvent.ExecuteMouseMoveEvent(Sender: TObject; Shift: TShiftState; X,
  Y: Integer);
var
  i : integer;
  proc: TMouseMoveEvent;
begin
  for i := 0 to Length(fMethods)-1 do
  begin
    proc:= TMouseMoveEvent(fMethods[i]);
    proc(Sender, Shift, X, Y);

  end;
end;


Присваиваем эти процедуры параметрам
Код: Выделить всё
procedure TCEvent.SetLink(AObject: tobject; APropName: string);
var
  proc: TNotifyEvent;
  procMME: TMouseMoveEvent;
  procME: TMouseEvent;
...
остальные типы
...
begin

  LinkedObj := AObject;
  LinkedProp:=APropName;
  if GetPropInfo(AObject,APropName)^.PropType^.Name = 'TNotifyEvent' then
  begin
    proc:= @ExecuteNotifyEvent;
    SetMethodProp(AObject,APropName,TMethod(proc));
    exit;
  end;
  if GetPropInfo(AObject,APropName)^.PropType^.Name = 'TMouseMoveEvent' then
  begin
    procMME:= @ExecuteMouseMoveEvent;
    SetMethodProp(AObject,APropName,TMethod(procMME));
    exit;
  end;
  if GetPropInfo(AObject,APropName)^.PropType^.Name = 'TMouseEvent' then
  begin
    procME:= @ExecuteMouseEvent;
    SetMethodProp(AObject,APropName,TMethod(procME));
    exit;
  end;
...
остальные типы
...

end;   

Добавляем обработчики в массив
Код: Выделить всё
procedure TCEvent.AddMethod(AMethod: TProcedureOfObject);
begin
  SetLength(fMethods,Length(fMethods)+1);
  fMethods[Length(fMethods)-1] := AMethod;


end;

Если к нужному событию не присвоена процедура диспетчер - мы ее просто присваиваем
Так что можно обрабатывать абсолютно все объекты и все события, но если кто-то создаст свой тип события - на него нужно будет создавать еще функцию диспетчер, что добавляет хлопот.
Потому стоит вопрос о создании универсальной функции диспетчере которая будет работать со всеми типами событий.
Аватара пользователя
carrots
постоялец
 
Сообщения: 138
Зарегистрирован: 28.03.2008 02:13:02

Re: Как на одно событие можно исполнить несколько проце

Сообщение MageSlayer » 27.07.2009 20:20:05

carrots писал(а):Спасибо MageSlayer,
но если я тебя правельно понял - в этих решениях есть ряд недопустимых недостатков.
В первом:
1.Этот метод будет работать только со специальными компонентами — это не допустимо.


Точно.

carrots писал(а):2.Будет очень сложно объяснить разработчику плагина как именно он должен переписать свои компоненты.

Хм. Странно. Компоненты в плагинах. Я почему-то подумал, что ситуация как наоборот, дать возможность делать подписку на события хоста.

carrots писал(а):3.Это повлечет за собой много дополнительной работы.

При большом количестве компонент для адаптации - да.

carrots писал(а):Относительно второго варианта:
В том то и стоит вопрос что мы не знаем как “выкусывать список параметров из стека и регистров” и как их передать в нужную нам функцию.


В случае "публикации" хостовых событий, условно говоря, хэшем типа события выступает адрес процедуры, которая которая дергает это событие. Клиент так или иначе стандартным способом подписаться на событие уже не может.

carrots писал(а):Выше я описывал более проще и гибче первого варианта:

Скорее всего - да.

carrots писал(а):Если к нужному событию не присвоена процедура диспетчер - мы ее просто присваиваем
Так что можно обрабатывать абсолютно все объекты и все события, но если кто-то создаст свой тип события - на него нужно будет создавать еще функцию диспетчер, что добавляет хлопот.
Потому стоит вопрос о создании универсальной функции диспетчере которая будет работать со всеми типами событий.


Собственно по ходу обдумывания второго варианта на ум пришел и третий.

Тоже хак, но еще более простой и надежный чем третий.
У нас проблема - как выкусить параметры. Так? Ок. Только на самом деле нам это не нужно, нам просто нужно "передать эти параметры" другим процедурам. Итак, сам хак - вместо того, чтобы пытаться узнать сколько параметров у процедуры-обработчика, то есть сколько параметров передал нам неизвестный компонент, можно просто скопировать все регистры и стек с запасом. Например, все регистры (в случае fastcall) и кусок стека, скажем байт 200. В таком случае, нам не нужно думать о том, что за параметры у события, какой их тип и т.д. И параметр настройки у алгоритма всего один - какой размер стека считать достаточным.
Разумеется, что придется вручную балансировать стек при выходе из процедуры-обработчика, но это легко - просто отправлять стек на то, место которое было перед копированием параметров. Ну и возможно придется сохранить все измененные регистры до вызова и восстановить после. Тоже ничего военного.

P.S. Третий вариант для лентяев типа меня :)
MageSlayer
постоялец
 
Сообщения: 216
Зарегистрирован: 07.09.2006 12:30:44


Вернуться в Общее

Кто сейчас на конференции

Сейчас этот форум просматривают: нет зарегистрированных пользователей и гости: 27

Рейтинг@Mail.ru