Delphi:记录用户触发的 UI 事件



我被要求在我们的程序中放置一些东西,以便记录表单上发生的事情......

记录这样的东西...单击了这个或那个按钮、上下文菜单、在组件上触发的事件等等。

这主要是因为我们处理的是遗留代码,不幸的是,我们正在尽可能调整/审查(Delphi XE10(,我们希望从用户的角度跟踪正在发生的一切(或大部分(,以便 - 能够了解用户在发生神秘事情时所做的事情 - 当我们不知道事情是如何发生的时,能够在必要时审查代码 - 最终速度问题

我不是在谈论异常或数据记录...这些都得到了妥善处理。

这只是关于用户界面。

你知道是否有任何库可以做类似的事情吗? 如果没有,您将如何尝试实现这一目标?

代码示例被愉快地接受(即使是简单的,例如一个或多个带有几个按钮的表单等(

谢谢大家!!

作为轻量级解决方案,您可以使用 OutputDebugString(PChar('My logtext(((或 TFile.AppendAllText('log.txt','My logtext'(。

如果你想要使用特殊的查看器、线程安全等进行花哨的日志记录,你可以包括一些特殊的日志记录框架。在这种情况下,最好在线搜索并自己比较功能,而不是在这里征求意见。

您可以开发一个非可视组件并将其放在每个窗体上,但这必须是一个非常复杂的组件,它要么使用 RTTI 要么查看 Windows 消息,以尝试侦听窗体中其他可视组件发生的情况。

我看到两种更简单的方法可以至少完成您所要求的部分内容......

1( 拦截对事件处理程序的调用

开发一个组件(比我上面暗示的组件简单一些(,该组件挂接到您感兴趣的控件类的事件,记录调用,然后将它们转发到原始事件处理程序过程(如果有(。

假设您对TWinControl.OnEnterOnExit感兴趣,TButton.OnClickTEdit.OnChange...

TEventLogger = Class(TComponent)
Private
Procedure Hookup(Const EventName: String; Const Component: TComponent; Const LoggingHandler: TNotifyEvent; Var Handler: TNotifyEvent);
Public
Procedure Setup(Const Container: TComponent);
Procedure HandleChange(Sender: TObject);
Procedure HandleClick(Sender: TObject);
Procedure HandleEnter(Sender: TObject);
Procedure HandleExit(Sender: TObject);
End;
Procedure TEventLoggerHookup(Const EventName: String; Const Component: TComponent; Const LoggingHandler: TNotifyEvent; Var Handler: TNotifyEvent);
Begin
if Assigned(Handler) Then
Handlers.AddObject(Component.Name + ';' + EventName, Handler);
Handler := LoggingHandler;
End;
Procedure TEventLogger.Setup(Const Container: TComponent);
Var
i: Integer;
c: TComponent;
Begin
For i:=0 to Container.ComponentCount - 1 Do Begin
c := Container.Components[i];
if c Is TWinControl Then Begin
Hookup('Enter',HandleEnter,TWinControl(c).OnEnter;
Hookup('Exit',HandleExit,TWinControl(c).OnExit;
End;
If c Is TButton Then 
Hookup('Click',HandleClick,TButton(c).OnClick;
If c Is TEdit Then 
Hookup('Change',HandleChange,TEdit(c).OnChange;
End;
End;
Procedure TEventLogger.Procedure HandleChange(Sender: TObject);
Var
s: String;
i: Integer;
e: TNotifyEvent;
Begin
With Sender As TComponent Do Begin
s := Name + ';Change';
Log(s);
i:= Handlers.IndexOf(s);
If i <> -1 Then Begin
e := Handlers.Objects[i];
e(Sender);
End;
End;
End;

这没有经过测试,但我认为你明白了重点。要记住的事项:

  • 处理程序是一个简单的TStringList...但是你可以用通用TDictionary<Key,Value>做得更好.当然,您应该创建它并适当地销毁它。
  • 所有列出的事件都TNotifyEvents。但是,您可以轻松地重载 Hookup 过程来处理其他类型的 ov 事件。
  • 当已创建组件并且属性从 dfm 流式传入时,必须执行安装程序。这可能需要您向每个窗体添加一些代码。
  • 如果您的应用程序在运行时以动态方式创建控件,或者如果在运行时弄乱事件处理程序,则此技术可能会(并且将会(适得其反。

2( 使用继承

为要记录的每个使用的可视组件派生一个新的可视组件类。您需要为每个方法定义一个 Log 方法并覆盖您感兴趣的特定"事件"方法。例如:

TLogButton = class(TButton)
private
procedure Log(Const Event: String);
protected
procedure Click; override;
end;
TLogButton.Click;
begin
Log('Click');
Inherited;
end;

然后,您可以实现 Log 方法,添加所需的所有信息(窗体和组件类和名称,或其他(。如果你想做一些复杂的事情,那么你最好将日志操作委托给一个专门的类。

这也没有经过测试。要记住的事项:

  • 要设置它,您只需使用大量的搜索和替换...自 修改TButtonTLogButton...在 pas 和 dfm 文件中(我希望 您正在使用文本DFM,如果没有,最好将它们转换为文本(。
  • 您必须将新单元添加到所有 包含您感兴趣的控件的窗体
  • 你应该 在自定义库中注册新类,以便拥有它们 在 IDE 中可用,否则将无法打开窗体 在设计时。
  • 你必须为每节课都这样做 有兴趣...如果应用程序使用许多 不同但相似的控件类。
  • 你必须知道 要记录的类的内部...并非所有方法都是 可覆盖...许多第三方库没有附带源代码... 这可能是地狱。

结论

整体。。。我可能会选择后一种解决方案。在单个控件类上实现基本上更容易,并且您可能已经可以通过一堆类获得一些非常有用的用户活动跟踪。前一种解决方案稍微复杂一些,并且可能会扩展到涵盖某些情况......但是很难彻底测试,如果你不太了解你的代码库,风险太大。

最新更新