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

查看:389
本文介绍了如何在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.

例如,对于所有数据集在Datamodule中,我需要在 BeforeOpen AfterOpen 事件之间运行代码以捕获开始时间,记录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;

Inside MyAfterOpen MyBeforeOpen 您可以在对原始事件处理程序的调用之前,之后或周围引入代码。

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

收集包含对象的 TObjectList with OwnsObjects:= true ,当您清除或释放对象列表时,所有内容都将恢复为原始文件。

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天全站免登陆