• 一个灵巧的Delphi多播实事件现方案


    一个灵巧的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.

    http://blog.csdn.net/wr960204/article/details/6452158

  • 相关阅读:
    201202
    Android牟利之道(一)界面嵌入有米广告
    SoketException log
    ERROR: 9patch image about.9.png malformed.
    Conversion to Dalvik format failed with error 1
    absolute绝对定位(相对于整个html流)以及不为人知的(fixed)绝对定位(fixed相对于浏览器窗口=不动的div)
    about getElementsByTagName()的那点事
    js 不用onload的loding
    absolute fixed效果 复制网页打开就是代码 http://www.cnblogs.com/0banana0/archive/2011/05/25/2056643.html
    关于datetime的那点事
  • 原文地址:https://www.cnblogs.com/findumars/p/4463528.html
Copyright © 2020-2023  润新知