如何将代码动态注入 Delphi 中的事件处理程序? [英] How can I dynamically inject code into event handlers in Delphi?

查看:22
本文介绍了如何将代码动态注入 Delphi 中的事件处理程序?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

对于调试/性能测试,我想在运行时将日志代码动态添加到给定类型组件的所有事件处理程序中.

For debugging / performance tests I would like to dynamically add logging code to all event handlers of components of a given type at run time.

例如,对于数据模块中的所有数据集,我需要在 BeforeOpenAfterOpen 事件中运行代码来捕获开始时间,并记录经过的时间在 AfterOpen 中.

For example, for all Datasets in a Datamodule, I need to run code in the BeforeOpen and AfterOpen events to capture the start time, and to log the elapsed time in AfterOpen.

我更愿意动态执行此操作(无组件子类化),以便我可以仅在需要时以最少的努力将其添加到所有现有数据模块和表单中.

I would prefer to do this dynamically (no component subclassing), so that I can add this to all existing datamodules and forms with minimal effort only when needed.

迭代所有组件并按其类型过滤很容易,但是对于已经分配了事件处理程序的组件,我需要一种方法来存储现有的事件处理程序,并分配一个新的修改后的事件处理程序它首先进行日志记录,然后调用已经存在的原始代码.

Iterating all components and filtering by their type is easy, but for the components which already have event handlers assigned, I need a way to store the existing event handlers, and assign a new modified event handler which first does the logging and then will invoke the original code which was already present.

所以这段代码

procedure TMyDatamodule.OnBeforeOpen(Sender: TDataset);
begin
  SomeProc;
end;

在运行时会变成

procedure TMyDatamodule.OnBeforeOpen(Sender: TDataset);
begin
  StoreStartTime(Sender); // injected code

  SomeProc;
end;

是否有可以应用的设计模式,或者是否有一些示例代码可以展示如何在 Delphi 中实现这一点?

Is there a design pattern which can be applied, or even some example code which shows how to implement this in Delphi?

推荐答案

您可以使用以下方案重新连接数据集:

You can use the following scheme to rewire the datasets:

type
  TDataSetEventWrapper = class
  private
    FDataSet: TDataSet;
    FOrgAfterOpen: TDataSetNotifyEvent;
    FOrgBeforeOpen: TDataSetNotifyEvent;
    procedure MyAfterOpen(DataSet: TDataSet);
    procedure MyBeforeOpen(DataSet: TDataSet);
  protected
    property DataSet: TDataSet read FDataSet;
  public
    constructor Create(ADataSet: TDataSet);
    destructor Destroy; override;
  end;

constructor TDataSetEventWrapper.Create(ADataSet: TDataSet);
begin
  Assert(ADataSet <> nil);
  inherited Create;
  FDataSet := ADataSet;
  FOrgAfterOpen := FDataSet.AfterOpen;
  FOrgBeforeOpen := FDataSet.BeforeOpen;
  FDataSet.AfterOpen := MyAfterOpen;
  FDataSet.BeforeOpen := MyBeforeOpen;
end;

destructor TDataSetEventWrapper.Destroy;
begin
  FDataSet.AfterOpen := FOrgAfterOpen;
  FDataSet.BeforeOpen := FOrgBeforeOpen;
  inherited;
end;

procedure TDataSetEventWrapper.MyBeforeOpen(DataSet: TDataSet);
begin
  if Assigned(FOrgBeforeOpen) then
    FOrgBeforeOpen(DataSet);
end;

procedure TDataSetEventWrapper.MyAfterOpen(DataSet: TDataSet);
begin
  if Assigned(FOrgAfterOpen) then
    FOrgAfterOpen(DataSet);
end;

MyAfterOpenMyBeforeOpen 中,您可以在调用原始事件处理程序之前、之后或前后引入您的代码.

Inside MyAfterOpen and MyBeforeOpen you can bring in your code before, after or around the call to the original event handler.

使用 OwnsObjects := trueTObjectList 中收集包装器对象,当您清除或释放对象列表时,一切都将恢复到原始状态.

Collect the wrapper objects in a TObjectList with OwnsObjects := true and everything will revert to the original when you clear or free the objectlist.

注意:要使此代码工作,必须在创建包装器时连接事件,并且禁止手动重新分配这些事件.

Caution: For this code to work the events have to be wired already when you create the wrappers and manually reassigning those events is forbidden.

这篇关于如何将代码动态注入 Delphi 中的事件处理程序?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆