如何在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.
例如,对于所有数据集在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屋!