相關資訊
本類常用軟件
-
福建農村信用社手機銀行客戶端下載下載量:584204
-
Windows優化大師下載量:416898
-
90美女秀(視頻聊天軟件)下載量:366961
-
廣西農村信用社手機銀行客戶端下載下載量:365699
-
快播手機版下載量:325855
一個靈巧的Delphi多播實現方案.必須是支持泛型的Delphi版本.也就是Delphi2009以后.強烈建議用DelphiXE.
用法就是例如寫一個Class指定一個Event,觸發的時候會通知多個Method.和.NET的多播事件機制是一樣的.
用法例如:
type
TFakeButton = class(TButton)
private
FMultiCast_OnClik : TMulticastEvent<TNotifyEvent>;
public
constructor Create(AOwnder : TComponent);override;
destructor Destroy; override;
procedure Click; override;
property MultiCast_OnClik : TMulticastEvent<TNotifyEvent> read FMultiCast_OnClik;
end;
{ TTest }
procedure TFakeButton.Click;
begin
inherited;
//這樣調用可以通知多個事件
FMultiCast_OnClik.Invok(Self);
end;
constructor TFakeButton.Create(AOwnder : TComponent);
begin
inherited Create(AOwnder);
FMultiCast_OnClik := TMulticastEvent<TNotifyEvent>.Create;
end;
destructor TFakeButton.Destroy;
begin
FMultiCast_OnClik.Free;
inherited Destroy;
end;
//
procedure TForm2.Button1Click(Sender: TObject);
var
Test : TFakeButton;
begin
Test := TFakeButton.Create(Self);
Test.MultiCast_OnClik.Add(TestA);
Test.MultiCast_OnClik.Add(TestB);
Test.SetBounds(0,0,100,100);
test.Caption := '試試多播';
Test.Parent := Self;
end;
procedure TForm2.TestA(Sender: TObject);
begin
ShowMessage(Caption);
end;
procedure TForm2.TestB(Sender: TObject);
begin
ShowMessage(FormatDateTime('yyyy-mm-dd hh:nn:ss',now));
end;
在按鈕上點一下,直接會觸發TestA,和TestB.
這個做法主要是省了寫一個事件容器,然后循環調用的麻煩.
下面是方案的代碼:
{
一個多播方法的實現.
和一位同事(一位Delphi牛人)一起討論了一下Delphi下多播事件的實現.
他提供了一個易博龍技術牛人的多播事件方案.這個方案非常牛,但是依賴Delphi的
編譯器特性太多,只能用在開啟優化的代碼.而DelphiXE默認Debug是關閉優化的.
重寫了一個TMulticastEvent.這個不依賴Delphi的編譯器產生的代碼特性.
其中InternalInvoke基本上是那位易博龍大牛的代碼.加了詳細的注釋
wr960204. 2011.5.28
}
unit MultiCastEventUtils;
interface
uses
Generics.collections, TypInfo, ObjAuto, SysUtils;
type
//
TMulticastEvent = class
private
FMethods : TList<TMethod>;
FInternalDispatcher: TMethod;
//悲催的是泛型類的方法不能內嵌匯編,只能通過一個非泛型的父類來實現
procedure InternalInvoke(Params: PParameters; StackSize: Integer);
public
constructor Create;
destructor Destroy; override;
end;
TMulticastEvent<T > = class(TMulticastEvent)
private
FEntry : T;
function ConvertToMethod(var Value):TMethod;
procedure SetEntry(var AEntry);
public
constructor Create;
destructor Destroy; override;
procedure Add(AMethod : T);
procedure Remove(AMethod : T);
function IndexOf(AMethod: T): Integer;
property Invok : T read FEntry;
end;
implementation
{ TMulticastEvent<T> }
procedure TMulticastEvent<T>.Add(AMethod: T);
var
m : TMethod;
begin
m := ConvertToMethod(AMethod);
if FMethods.IndexOf(m) < 0 then
FMethods.Add(m);
end;
function TMulticastEvent<T>.ConvertToMethod(var Value): TMethod;
begin
Result := TMethod(Value);
end;
constructor TMulticastEvent<T>.Create();
var
MethInfo: PTypeInfo;
TypeData: PTypeData;
begin
MethInfo := TypeInfo(T);
if MethInfo^.Kind <> tkMethod then
begin
raise Exception.Create('T only is Method(Member function)!');
end;
TypeData := GetTypeData(MethInfo);
Inherited;
FInternalDispatcher := CreateMethodPointer(InternalInvoke, TypeData);
SetEntry(FEntry);
end;
destructor TMulticastEvent<T>.Destroy;
begin
ReleaseMethodPointer(FInternalDispatcher);
inherited Destroy;
end;
function TMulticastEvent<T>.IndexOf(AMethod: T): Integer;
begin
Result := FMethods.IndexOf(ConvertToMethod(AMethod));
end;
procedure TMulticastEvent<T>.Remove(AMethod: T);
begin
FMethods.Remove(ConvertToMethod(AMethod));
end;
procedure TMulticastEvent<T>.SetEntry(var AEntry);
begin
TMethod(AEntry) := FInternalDispatcher;
end;
{ TMulticastEvent }
constructor TMulticastEvent.Create;
begin
FMethods := TList<TMethod>.Create;
end;
destructor TMulticastEvent.Destroy;
begin
FMethods.Free;
inherited Destroy;
end;
procedure TMulticastEvent.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;
end.