程序師世界是廣大編程愛好者互助、分享、學習的平台,程序師世界有你更精彩!
首頁
編程語言
C語言|JAVA編程
Python編程
網頁編程
ASP編程|PHP編程
JSP編程
數據庫知識
MYSQL數據庫|SqlServer數據庫
Oracle數據庫|DB2數據庫
 程式師世界 >> 編程語言 >> 更多編程語言 >> Delphi >> 失敗的大牛事件委托,與我的委托,失敗大牛事件委托

失敗的大牛事件委托,與我的委托,失敗大牛事件委托

編輯:Delphi

失敗的大牛事件委托,與我的委托,失敗大牛事件委托


看了網上大牛的DELPHI事件委托,實際用起來是有BUG的。代碼如下:

unit faDelegate;

interface

uses
Generics.collections, TypInfo, ObjAuto, SysUtils;
type
Event = class
private
FMethods : TList<TMethod>;
FInternalDispatcher: TMethod;
//悲催的是泛型類的方法不能內嵌匯編,只能通過一個非泛型的父類來實現
procedure InternalInvoke(Params: PParameters; StackSize: Integer);
public
constructor Create;
destructor Destroy; override;
end;

Event<T> = class(Event)
private
FObj:TObject;
FProName:string;

FEntry : T;
function ConvertToMethod(var Value):TMethod;
procedure SetEntry(var AEntry);
public
constructor Create(Obj:TObject;ProName:String );
destructor Destroy; override;
procedure Add(AMethod : T);
procedure Remove(AMethod : T);
function IndexOf(AMethod: T): Integer;

// property Invok : T read FEntry;
end;

implementation

{ Event<T> }

procedure Event<T>.Add(AMethod: T);
var
m : TMethod;
begin
m := ConvertToMethod(AMethod);
if ((m.Code<>nil) and (FMethods.IndexOf(m) < 0)) then
FMethods.Add(m);
end;

function Event<T>.ConvertToMethod(var Value): TMethod;
begin
Result := TMethod(Value);
end;

constructor Event<T>.Create(Obj:TObject;ProName:String );
var
MethInfo: PTypeInfo;
TypeData: PTypeData;
m:TMethod;
p:Pointer;
begin
MethInfo := TypeInfo(T);
if MethInfo^.Kind <> tkMethod then //檢測T的類型
raise Exception.Create('T only is Method(Member function)!');

TypeData := GetTypeData(MethInfo);

Inherited Create();
FInternalDispatcher := CreateMethodPointer(InternalInvoke, TypeData); //把InternalInvoke的函數地址轉為TMethod
SetEntry(FEntry); //FEntry是入口地址,設為FInternalDispatcher

FObj:=Obj;
FProName:=ProName;

m:=GetMethodProp(FObj,FProName);
p:=@m;
Add(T(p^)); //先添加對象原有的方法
SetMethodProp(FObj,FProName,FInternalDispatcher); //設定對象的入口
end;

destructor Event<T>.Destroy;
begin
ReleaseMethodPointer(FInternalDispatcher); //和CreateMethodPointer是一對的,正好相反

inherited Destroy;
end;

function Event<T>.IndexOf(AMethod: T): Integer;
begin
Result := FMethods.IndexOf(ConvertToMethod(AMethod));
end;

procedure Event<T>.Remove(AMethod: T);
begin
FMethods.Remove(ConvertToMethod(AMethod));
end;

procedure Event<T>.SetEntry(var AEntry);
begin
TMethod(AEntry) := FInternalDispatcher;
end;

{ Event }

constructor Event.Create;
begin
FMethods := TList<TMethod>.Create;
end;

destructor Event.Destroy;
begin
FMethods.Free;
inherited Destroy;
end;

procedure Event.InternalInvoke(Params: PParameters; StackSize: Integer);
var
LMethod: TMethod;
begin
for LMethod in FMethods do
begin
//如果用到了棧(也就是Register約定參數大於2或者stdcall,cdecl約定)就把棧內所有數據都拷貝參數棧裡面
if StackSize > 0 then
asm
MOV ECX,StackSize //Move的第三個參數,同時為下一步Sub ESP做准備
SUB ESP,ECX //把棧頂 - StackSize(棧是負向的)
MOV EDX,ESP //Move的第二個參數
MOV EAX,Params
LEA EAX,[EAX].TParameters.Stack[8] //Move的第一個參數
CALL System.Move
end;
//Register協議填寫三個寄存器,EAX肯定是Self,如果是其他協議寄存器被填寫也沒啥影響
asm
MOV EAX,Params //把Params讀到EAX
MOV EDX,[EAX].TParameters.Registers.DWORD[0] //EDX
MOV ECX,[EAX].TParameters.Registers.DWORD[4] //EAX

MOV EAX,LMethod.Data//把Method.Data給到EAX,如果是Register約定就是Self.否則也沒影響
CALL LMethod.Code//調用Method.Data
end;
end;
end;

 

 

BUG體驗在對TDBGridEh中的列的事件OnupdateData做委托時,對Value參數賦值會有錯誤!暈,不知道怎麼辦好!所以只好用自己的方法解決!

我的事件委托:

Delegate<T>=class
private
i:integer;
FEntrance:TMethod;
protected
Delegates:array of TMethod;
procedure AddMethod(m:TMethod);
function GetRunEof():Boolean;
function GetRun():T;
public
constructor Create(C: TObject;ProName:string);virtual;
destructor Destroy; override;
procedure Add(Delegate:T);

end;

DeNotify=class(Delegate<TNotifyEvent>)
published
procedure DoRun(Sender:TObject);
end;

 

implementation

 


procedure Delegate<T>.Add(Delegate: T);
var m:TMethod;
p:Pointer;
begin
p:=@Delegate;
m:=Tmethod(p^);
AddMethod(Tmethod(p^));
end;

procedure Delegate<T>.AddMethod(m: TMethod);
begin
if ((m.Code=nil) or (m.Data=nil)) then exit;
if (m.Code<>FEntrance.Code) then begin
SetLength(Delegates,High(Delegates)+2);
Delegates[High(Delegates)]:=m;
end;
end;

constructor Delegate<T>.Create(C: TObject; ProName: string);
begin
FEntrance.Data:=Self;
FEntrance.Code:=MethodAddress('DoRun');

AddMethod(GetMethodProp(c,ProName));
SetMethodProp(c,ProName,FEntrance);
i:=0;

// if Assigned(lstDelegates)=false then begin
// lstDelegates:=TList.Create;
lstDelegates.Add(Self);
// end;
end;


destructor Delegate<T>.Destroy;
begin
Dec(iTotal);
// if lstDelegates.Count=0 then
// lstDelegates.Free
// else
lstDelegates.Delete(lstDelegates.IndexOf(self));

inherited;
end;

 

function Delegate<T>.GetRun: T;
var m:TMethod;
p:Pointer;
begin
m:=Delegates[i-1];
p:=@m;
Result:=T(p^);
end;

function Delegate<T>.GetRunEof: Boolean;
begin
Result:=not (i<=High(delegates));
if Result=false then
Inc(i)
else
i:=0;
end;


procedure DeNotify.DoRun(Sender: TObject);
begin
while not GetRunEof() do
GetRun()(Sender);
end;

這個方法有很大的缺點,就是一種事件類型要派生一個類!但實在,沒有什麼問題。

看來事物都有兩面性,濃縮很大的代碼,做起來很有技巧,很高難度,而且會比較容易出錯。

如果濃縮不大的代碼,所需要的技巧不多,容易理解,但是冗余又比較多。不爽。

不過,無論如何,正確是第一的。技巧再高,不正確也沒有用。第一種方法好象很強大,但有BUG了,都不知道如何改,因為太高級了。。。。

 

  1. 上一頁:
  2. 下一頁:
Copyright © 程式師世界 All Rights Reserved