-
Pavia © (11.06.16 09:36) [0]Как подменить событие OnClick разом у всех стандартных компонентов? Само-собой подразумевается, что компоненты переписывать нельзя, а вот править VCL можно.
-
K-1000 © (11.06.16 09:40) [1]В цикле присвоить другое событие.
Можно и через инспектор. -
K-1000 © (11.06.16 09:41) [2]
> Само-собой подразумевается, что компоненты переписывать
> нельзя, а вот править VCL можно.
Как это?
Если компоненты это и есть VCL? :) -
Игорь Шевченко © (11.06.16 10:03) [3]Не трожь генофонд, зараза!
-
Юрий Зотов © (11.06.16 10:11) [4]Application.OnMessage.
А переписывать глубины - не стоит. -
> Pavia © (11.06.16 09:36)
> а вот править VCL можно.
[3] +100500
See also: http://rsdn.ru/forum/delphi/480838.1
--
Regards, LVT. -
Юрий Зотов © (12.06.16 08:52) [6]> Application.OnMessage.
Еще лучше - хук на мышь (с учетом того, что форма может быть модальной).
В любом варианте, главное - глубины не трогать. Там и без нас баги найдутся. -
> Юрий Зотов © (12.06.16 08:52) [6]
> Еще лучше - хук на мышь
Клик может быть и клавой.
Тогда уж WH_GETMESSAGE or WH_CALLWNDPROC*
--
Regards, LVT. -
> Leonid Troyanovsky © (11.06.16 10:39) [5]
Вот процедура, заполняющая список контролов с назначенным OnClick,
лежащих на паренте.
uses
typinfo;
procedure EnumControls(AControl: TControl; List: TStrings);
var
i: Longint;
pi: PPropInfo;
onc: TNotifyEvent;
begin
pi := GetPropInfo(AControl, 'OnClick');
if Assigned(pi) then
begin
onc := TNotifyEvent(GetMethodProp(AControl, pi));
if Assigned(onc) then
List.Add(AControl.Name);
end;
if AControl is TWincontrol then
for i := 0 to TWinControl(AControl).ControlCount -1 do
EnumControls(TWinControl(AControl).Controls[i], List);
end;
Например, на форме:
procedure TForm1.Button1Click(Sender: TObject);
begin
EnumControls(Self, ListBox1.Items);
end;
--
Regards, LVT. -
> Leonid Troyanovsky © (12.06.16 10:50) [8]
И вот процедура внедрения в OnClick.
uses
typinfo;
type
PMethod = ^TMethod;
procedure CommonClick (old: PMethod; Sender: TObject);
begin
OutputDebugString('Come on!');
TNotifyEvent(old^)(Sender);
end;
procedure SetControlsCommonClick(AControl: TControl);
var
i: Longint;
pi: PPropInfo;
onc: TMethod;
old: PMethod;
begin
pi := GetPropInfo(AControl, 'OnClick');
if Assigned(pi) then
begin
onc := GetMethodProp(AControl, pi);
if (onc.Data <> nil) or (onc.Code <> nil) then
begin
New(old);
old^ := onc;
onc.Data := old;
onc.Code := @CommonClick;
SetMethodProp(AControl, pi, onc);
end;
end;
if AControl is TWincontrol then
for i := 0 to TWinControl(AControl).ControlCount -1 do
SetControlsCommonClick(TWinControl(AControl).Controls[i]);
end;
--
Regards, LVT. -
Rouse_ © (14.06.16 11:15) [10]Проще тогда динамический Click у TControl перехватить на себя
-
> Rouse_ © (14.06.16 11:15) [10]
> Проще тогда динамический Click у TControl перехватить на
Попробовал.
Взял описание DMT
http://www.transl-gunsmoker.ru/2011/07/hack-9-dynamic-method-table-structure.html
Нахожу индекс Click на своем наследнике
type
TMyControl = class(TControl)
public
procedure Click; override;
end;
procedure TMyControl.Click;
begin
inherited;
end;
путем
function FindDynamicMethod(AClass: TClass; DMTIndex: TDMTIndex): Pointer;
// Pascal-вариант более быстрой BASM-версии подпрограммы System.GetDynaMethod
var
Dmt: PDmt;
DmtMethods: PDmtMethods;
i: integer;
begin
while Assigned(AClass) do
begin
Dmt := GetDmt(AClass);
if Assigned(Dmt) then
for i := 0 to Dmt.Count-1 do
if DMTIndex = Dmt.Indicies[i] then
begin
DmtMethods := @Dmt.Indicies[Dmt.Count];
Result := DmtMethods[i];
Exit;
end;
// Не в этом классе - поднимаемся по иерархии
AClass := AClass.ClassParent;
end;
Result := nil;
end;
На моей D6 получается -21.
Пробую на кошках:
var
oldproc: TClickproc;
procedure NewClick(ASelf: TObject; Sender: TObject);
begin
OutputDebugString('ooch');
oldproc(ASelf, Sender);
end;
procedure TForm1.Button2Click(Sender: TObject);
begin
oldproc := FindDynamicMethod(TControl, -21);
newclick(Self, Button3);
end;
Наконец, делаю из FindDynamicMethod процедуру модификации:
procedure ReplaceDynamicMethod(AClass: TClass; DMTIndex: TDMTIndex; newmethod: Pointer);
var
Dmt: PDmt;
DmtMethods: PDmtMethods;
i: integer;
protect: Dword;
begin
while Assigned(AClass) do
begin
Dmt := GetDmt(AClass);
if Assigned(Dmt) then
for i := 0 to Dmt.Count-1 do
if DMTIndex = Dmt.Indicies[i] then
begin
DmtMethods := @Dmt.Indicies[Dmt.Count];
Win32Check(VirtualProtect(@DmtMethods[i], 4, PAGE_READWRITE, protect));
DmtMethods[i]:= newmethod;
VirtualProtect(@DmtMethods[i], 4, protect, protect);
Exit;
end;
// Не в этом классе - поднимаемся по иерархии
AClass := AClass.ClassParent;
end;
end;
Наконец, делаю ReplaceDynamicMethod(TControl, -21, @newClick).
Но, в результате получаю, что на кнопках (TButton, TRadioButton, TCheckBox) оно не срабатывает.
Видимо, не все так просто. По крайней мере для меня :)
Не знаю, что у меня не так, но уже ясно, что сделать подобное,
скажем, из длл вряд ли получится.
--
Regards, LVT. -
> Leonid Troyanovsky © (15.06.16 10:31) [11]
> путем function FindDynamicMethod(AClass: TClass; DMTIndex:
Путем procedure DumpDynamicMethods, sorry.
--
Regards, LVT. -
Rouse_ © (15.06.16 12:43) [13]
> Leonid Troyanovsky © (15.06.16 10:42) [12]
Да к чему такие сложности - грязный хак и все дела :)type
TForm7 = class(TForm)
Button1: TButton;
Button2: TButton;
procedure FormCreate(Sender: TObject);
end;
var
Form7: TForm7;
implementation
uses
SpliceHelper;
{$R *.dfm}
procedure ClickHandler(Self, Sender: TObject);
begin
ShowMessage((Self as TComponent).Name + ' clicked');
end;
procedure TForm7.FormCreate(Sender: TObject);
function GetControlClickAddr: Pointer;
asm
{$IFDEF WIN32}
lea eax, TControl.Click
{$ELSE}
lea rax, TControl.Click
{$ENDIF}
end;
var
HotPathSpliceRec: THotPachSpliceData;
OldProtect: DWORD;
TrampolineSplice: TNearJmpSpliceRec;
TrampolineAddr, ClickHandlerAddr: Pointer;
begin
ClickHandlerAddr := @ClickHandler;
HotPathSpliceRec.FuncAddr := GetControlClickAddr;
Move(HotPathSpliceRec.FuncAddr^, HotPathSpliceRec.LockJmp, LockJmpOpcodeSize);
HotPathSpliceRec.SpliceRec.JmpOpcode := JMP_OPKODE;
HotPathSpliceRec.SpliceRec.Offset :=
PAnsiChar(ClickHandlerAddr) - PAnsiChar(HotPathSpliceRec.FuncAddr);
SpliceNearJmp(PAnsiChar(HotPathSpliceRec.FuncAddr) - NearJmpSpliceRecSize,
HotPathSpliceRec.SpliceRec);
SpliceLockJmp(HotPathSpliceRec.FuncAddr, LOCK_JMP_OPKODE);
end;
SpliceHelper.pas лежит тут:
http://rouse.drkb.ru/blog/intercept2.zip -
Rouse_ © (15.06.16 13:18) [14]Даже вот так, чтоб все по феншую было :)
type
TForm7 = class(TForm)
Button1: TButton;
Button2: TButton;
ActionList1: TActionList;
Action1: TAction;
procedure FormCreate(Sender: TObject);
procedure Button1Click(Sender: TObject);
procedure Action1Execute(Sender: TObject);
end;
var
Form7: TForm7;
implementation
uses
SpliceHelper;
{$R *.dfm}
type
TControlFriendly = class(TControl);
procedure ClickHandler(Self: TObject);
var
AControl: TControlFriendly;
begin
AControl := TControlFriendly(Self);
ShowMessage(AControl.Name + ' clicked');
if Assigned(AControl.OnClick) and (AControl.Action <> nil) and not
DelegatesEqual(@AControl.OnClick, @AControl.Action.OnExecute) then
AControl.OnClick(Self)
else if not (csDesigning in AControl.ComponentState) and (AControl.ActionLink <> nil) then
AControl.ActionLink.Execute(TComponent(Self))
else if Assigned(AControl.OnClick) then
AControl.OnClick(Self);
end;
procedure TForm7.Action1Execute(Sender: TObject);
begin
ShowMessage((Sender as TComponent).Name + ' clicked 2');
end;
procedure TForm7.Button1Click(Sender: TObject);
begin
ShowMessage((Sender as TComponent).Name + ' clicked 2');
end;
procedure TForm7.FormCreate(Sender: TObject);
function GetControlClickAddr: Pointer;
asm
{$IFDEF WIN32}
lea eax, TControl.Click
{$ELSE}
lea rax, TControl.Click
{$ENDIF}
end;
var
ClickHandlerAddr: Pointer;
HotPathSpliceRec: THotPachSpliceData;
begin
ClickHandlerAddr := @ClickHandler;
HotPathSpliceRec.FuncAddr := GetControlClickAddr;
Move(HotPathSpliceRec.FuncAddr^, HotPathSpliceRec.LockJmp, LockJmpOpcodeSize);
HotPathSpliceRec.SpliceRec.JmpOpcode := JMP_OPKODE;
HotPathSpliceRec.SpliceRec.Offset :=
PAnsiChar(ClickHandlerAddr) - PAnsiChar(HotPathSpliceRec.FuncAddr);
SpliceNearJmp(PAnsiChar(HotPathSpliceRec.FuncAddr) - NearJmpSpliceRecSize,
HotPathSpliceRec.SpliceRec);
SpliceLockJmp(HotPathSpliceRec.FuncAddr, LOCK_JMP_OPKODE);
end; -
Юрий Зотов © (15.06.16 13:56) [15]
type
TMyLabel = class(TLabel)
protected
procedure Click; override;
end;
procedure TMyLabel.Click;
begin
ShowMessage('WOW!');
end;
Розыч, какое сообщение (твое или мое) я увижу при клике по этой метке? -
Rouse_ © (15.06.16 14:00) [16]Мдя... твое :)
-
Юрий Зотов © (15.06.16 14:11) [17]> Rouse_ © (15.06.16 14:00) [16]
В том и фокус, что вызывается не тот клик, который ты хакнул. Значит, надо пройтись по всем использованным в проекте контролам и применить твой хак к каждому.
Сделать, наверное, можно, но геморроя будет побольше. -
Rouse_ © (15.06.16 14:25) [18]Угу, перебор придется делать для таких случаев + контролировать создание новых нестандартных контролов (с перекрытым Click)
-
Юрий Зотов © (15.06.16 14:29) [19]> Rouse_ © (15.06.16 14:25) [18]
> контролировать создание контролов с перекрытым Click
Не обязательно. Если клик не перекрыт, то в предке он просто хакнется еще раз. Не страшно.