【问题标题】:Is there a way to log every gui event in Delphi?有没有办法在 Delphi 中记录每个 gui 事件?
【发布时间】:2011-01-11 00:10:56
【问题描述】:

Delphi 调试器非常适合调试线性代码,其中一个函数以可预测的线性方式调用其他函数,我们可以逐行逐行执行程序。

我发现调试器在处理事件驱动的 gui 代码时不太有用,其中一行代码可能导致触发新事件,而这又可能触发其他事件。 在这种情况下,“逐步执行代码”方法无法让我看到正在发生的一切。

我通常解决这个问题的方法是 1) 猜测哪些事件可能是问题的一部分,然后 2) 为每个事件添加断点或日志记录。

问题在于这种方法是随意且耗时的。

我可以在调试器中轻弹一个开关来说“记录所有 gui 事件”吗?或者我可以添加一些代码来捕获事件,比如

procedure GuiEventCalled(ev:Event)
begin
    log(ev);
    ev.call();
end

我正在寻找的最终结果是这样的(例如):

FieldA.KeyDown 
FieldA.KeyPress 
FieldA.OnChange 
FieldA.OnExit 
FieldB.OnEnter

这将消除 Delphi gui 调试中的所有猜测。

我使用的是 Delphi 2010

[编辑] 一些答案建议了拦截或记录 Windows 消息的方法。其他人随后指出,并非所有的 Delphi 事件都是 Windows 消息。我认为我要问的是这些类型的“非 Windows 消息”事件; Delphi 代码创建的事件。 [/编辑]

[编辑2] 看完这里的所有信息后,我有了一个想法,使用 RTTI 来动态拦截 TNotifyEvents 并将它们记录到 Debugging 窗口中的 Event Log 中。这包括 OnEnter、OnExit、OnChange、OnClick、OnMouseEnter、OnMouseLeave 事件。经过一番修改后,我让它工作得很好,至少对我来说是这样(它不记录关键事件,但可以添加)。 我已经发布了代码here

使用

  1. 下载 EventInterceptor Unit 并将其添加到您的项目中
  2. 将 EventInterceptor 单元添加到 Uses 子句
  3. 在您要跟踪的每个表单的代码中的某处添加此行。

    AddEventInterceptors(MyForm);

打开调试器窗口,任何被调用的事件都将被记录到事件日志中

[/EDIT2]

【问题讨论】:

标签: delphi


【解决方案1】:

使用我写的“delphieventlogger”单元download here。它只是一种方法调用,非常易于使用。它将所有 TNotifyEvents(例如 OnChange、OnEnter、OnExit)记录到调试器窗口中的 Delphi 事件日志中。

【讨论】:

    【解决方案2】:

    不,没有通用的方法可以做到这一点,因为 Delphi 没有任何可以以某种方式挂钩的“事件类型”。事件处理程序只是一个方法引用,它的调用方式如下:

    if assigned(FEventHandler) then
      FEventHandler(self);
    

    只是一个普通的方法引用调用。如果您想记录所有事件处理程序,您必须自己在每个事件处理程序中插入一些调用。

    【讨论】:

    • 事实证明,我感兴趣的很多事件都属于同一类型(即 TNotifyEvent)。使用 RTTI,我们可以拦截所有此类事件并记录它们。
    【解决方案3】:

    我知道它有点贵,但是您可以使用自动化 QA(现在是 SmartBear)TestRecorder 作为 TestComplete 的扩展(如果您只希望在您的系统上使用它,那么仅 TestComplete 就可以了)。该软件将跟踪您的 GUI 操作并将其存储在类似语言的脚本中。甚至还有一个单元可以链接到您的 exe 中,以便直接在用户系统上进行这些记录。当一些用户无法解释他们做了什么导致错误时,这尤其有用。

    【讨论】:

      【解决方案4】:

      使用 WinSight 实时查看消息流。

      如果您确实希望程序生成日志,请覆盖 WinProc 和/或拦截 Application 中的消息。

      【讨论】:

      • 所有 Delphi 事件都是 Windows 消息吗??我刚刚尝试使用 TApplicationEvents.OnMessage 记录事件,而我似乎得到的只是鼠标移动和键盘事件。我没有看到 OnChange、OnExit 或 OnEnter 事件(据我所知)。我想我希望得到比 Windows 消息更高级的东西。
      • btw WinSight 不附带 Delphi 2010
      • @awmross:Delphi 事件不是 Windows 消息。 Windows 消息被分派到 Delphi 控件上的消息处理例程,如果有一个附加的事件处理程序,其中一些调用事件处理程序。
      • @awmross: TApplication.OnMessage 仅捕获发布到主消息队列的消息。这主要是针对操作系统发出的消息,而不是内部 VCL/RTL 消息,这些消息通常直接发送到 WndProc() 方法。不,并非所有 VCL 事件一开始都是消息驱动的。您正在寻找的东西没有单一的解决方案。您必须在代码中使用TApplication.OnMessageTApplication.HookMainWindow()WndProc() 覆盖、SetWindowsHook() 和选择性断点/挂钩的组合。
      • @Remy Lebau - TeamB 你的评论应该是一个答案。
      【解决方案5】:

      TApplication.OnMessage 事件可用于捕获发布到主消息队列的消息。这主要是针对操作系统发出的消息,而不是内部 VCL/RTL 消息,这些消息通常直接发送到 WndProc() 方法。并非所有 VCL 事件一开始都是消息驱动的。您正在寻找的东西没有单一的解决方案。您必须在代码中使用 TApplication.OnMessageTApplication.HookMainWindow()WndProc() 覆盖、SetWindowsHook() 和选择性断点/挂钩的组合。

      Borland 的 WinSight 工具不再分发,但是有很多第三方工具现成的可以与 WinSight 做同样的事情,例如 Microsoft 的 Spy++、WinSpector 等,用于实时跟踪日志窗口消息。

      【讨论】:

        【解决方案6】:

        作为替代方法,要调试触发的事件,请使用调试器 Step Into (F7) 而不是 Step Over (F8) 命令。

        调试器将在调用期间到达的任何可用代码行处停止。

        【讨论】:

        • 我最初不能使用这种方法,因为我们使用的是相当复杂的 3rd 方皮肤库;我发现调试器会进入这个第 3 方代码。然后我发现了“{$D-}”编译器指令,它可以关闭整个单元的调试。这限制了调试器只能进入我的项目代码,这使您的建议切实可行。我认为这种方法存在一个普遍问题;您正在查找的事件可能在调用树的深处被调用;所以手动单步执行代码仍然需要猜测和试错。
        • @awmross:调试应用程序(无论是否由事件驱动)几乎完全是一种猜测和试错的艺术;)
        【解决方案7】:

        您可以尝试AOP frameworks for Delphi 之一。 MeAOP 提供了一个您可以使用的默认记录器。它不会告诉你事件处理程序内部发生了什么,但它会告诉你事件处理程序何时被调用以及何时返回。

        【讨论】:

        • 我也为此想到了 AOP。我尝试了 MeAOP,但它似乎无法在 D2010 下编译(我认为这是一个 Unicode 问题)
        猜你喜欢
        • 2012-02-01
        • 1970-01-01
        • 2014-01-04
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2022-10-20
        相关资源
        最近更新 更多