如何在 Delphi 中动态地将代码注入事件处理程序? [英] How can I dynamically inject code into event handlers in 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.
例如,对于数据模块中的所有数据集,我需要在 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;
在 MyAfterOpen
和 MyBeforeOpen
中,您可以在调用原始事件处理程序之前、之后或前后引入您的代码.
Inside MyAfterOpen
and MyBeforeOpen
you can bring in your code before, after or around the call to the original event handler.
使用 OwnsObjects := true
收集 TObjectList
中的包装对象,当您清除或释放对象列表时,所有内容都将恢复为原始状态.
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屋!