【问题标题】:Delphi 2007 - programmatic manipulation of the "exceptions to ignore" listDelphi 2007 - 对“要忽略的异常”列表进行编程操作
【发布时间】:2014-03-23 22:40:57
【问题描述】:

我有一个应用程序在调用FormatDateTime 时偶尔会返回整数溢出。我不知道是什么情况触发了这种情况,尽管我发现提到了问题herehere

我目前的解决方法是包装函数并处理异常:

function FormatDateTimeEx (const FormatString : ANSIString ;
                                 DateTime     : TDateTime) : ANSIString ;
begin
try
    Result := FormatDateTime (FormatString, DateTime) ;
except
    Result := '???' ; 
    end ;
end ;

这解决了导致编译的可执行文件崩溃的问题,但如果我在 IDE 中运行程序时调试器没有因EIntOverflow 异常而中断,我也更喜欢它。我不想通过告诉 IDE 忽略 EIntOverflow 异常来做到这一点,因为这样它不会在其他情况下发生未捕获的整数溢出时中断(对吗?)。

是否有一种编程方式(编译时或运行时)告诉 IDE 在本地忽略异常类,以应对可能发生异常的情况,并且您已经知道并正在处理它?

【问题讨论】:

  • 了解实际问题并应用真正的解决方案不是更好吗?
  • 我认为这要求相同的stackoverflow.com/questions/4271066/…
  • 我敢肯定,如果您提供 SSCCE,我们可以提供修复
  • 只是猜测:您使用的FormatDateTime 不是线程安全的。尝试在整个应用程序中使用线程安全版本
  • @SirRufo,问题也可能出在“错误”的输入日期时间值上;例如这会导致除以 0 异常 FormatDateTime('', 10000000000); 只是因为 TDateTime 类型是如此不安全。

标签: delphi exception delphi-2007 integer-overflow


【解决方案1】:

是否有一种编程方式(编译时或运行时)告诉 IDE 在本地忽略异常类,以应对可能发生异常的情况,并且您已经知道并正在处理它?

没有。

您无法要求调试器忽略某些EIntOverflow 异常但不忽略其他异常。你要么全部忽略它们,要么一个都不忽略。您可以控制调试器中哪些异常中断的唯一方法是通过异常类。您不能要求调试器忽略程序某一部分的异常,但不能忽略另一部分。

坦率地说,在我看来,解决这个问题的正确方法是首先弄清楚为什么会触发这些异常。并做一些事情来防止它们发生。异常是代码中的错误的症状,您需要直接解决。

【讨论】:

  • 这里有些混乱。我想要的是某种方式来禁用调试器在我的 FormatDateTimeEx 例程开始时拦截EIntOverflow 异常(比如说),然后在退出例程时再次允许它。有点像{$I-}{$I+} 的调试器异常版本。当然,最好的办法是阻止它们发生,但这可能并不容易,也不容易解决。如果有一种方法可以做到这一点,那么可能还有其他例外情况(例如 Indy)。
  • 我不确定哪里出了问题。您可以将调试器配置为忽略某些异常类。但是该配置是全局的。它不能应用于代码的本地部分。要么全有,要么全无。
  • Rob Kennedy's article 中,他描述了使用 IDE 高级断点或多或少地执行此操作(您只能禁用/重新启用所有异常)。主要缺点是您必须在使用该单元的每个项目中设置断点。这可能会减慢执行速度。
  • @ross 如果你愿意,就这样做吧。令我惊讶的是,您似乎并不倾向于修复代码中的错误。
  • 我对这项技术比修复 FormatDateTime 更感兴趣。如果我能再次可靠地触发它,我会进一步调查。您是否发现某些情况下您知道代码中的某个位置可能会发生异常,您使用try..except 处理它,并且您不希望调试器报告它,但是您这样做 是否希望(未处理)在其他地方发生相同的异常来触发调试器?
【解决方案2】:

您无法控制调试器异常处理,但可以控制导致其中一些异常的编译器选项。 以下是如何在本地禁用编译器开关,然后返回之前的状态:

1) 初始化 - 在某个单元的开头某处,或者可能在单独的 *.inc 文件中。

{$IFOPT R+}
  {$DEFINE RangeCheckOn}
{$ENDIF}

您可以在此处保存编译器开关的初始(即在项目设置中设置)状态。 2)现在,用法:

{$R-}
  ...your code here with disabled switch...
{$IFDEF RangeCheckOn}
  {$R+}
{$ENDIF}

在这里您禁用开关,执行一些没有它的代码并在完成后将其返回到初始状态。

我正在使用这个技巧对 (U)Int64 进行一些位移操作,这会在触摸 MSB 时引发错误的 EIntOverflow 异常(XE2 实际存在的编译器错误,对于较新的版本不确定)。

但我同意之前的答案,您应该检查引发异常的真正原因。我建议您使用 FormatEx 版本,在出现异常时将记录参数值。因此,您可以跟踪导致异常的值并检查它是否是可重现的情况。

【讨论】:

  • 这一切都是真的,但似乎与手头的问题无关。当然,除非您提议重新编译 RTL。这就是你的想法吗?
  • 嗯,AFAIK,EIntOverflow 仅在设置 $R 时才会引发,因此主题启动器描述的问题可以通过在引发此异常的代码位置上使用我的方法(即调用格式)。甚至只有在 IDE 下运行时才能添加包装。无论如何,我仍然坚持使用这种“尝试 %do something% except {ahh, error? To hell with it} end”的方法,只有当一个人完全理解他在做什么时。
  • 用户无法控制引发异常的代码。这已经编译到 RTL 中了。所以你提倡重新编译 RTL,至少是这里适用的那部分。
  • 好吧,引发异常的代码是他直接调用的 FormatDateTime。他可以像使用 FormatDateTimeEx 一样随意包装它。
  • 没有任何包装可以阻止 FormatDateTime 首先引发异常。
【解决方案3】:

是否有一种编程方式(编译时或运行时)告诉 IDE 在本地忽略异常类,以应对可能发生异常的情况,并且您已经知道并正在处理它?

不在代码中,不。但是,可以使用断点在调试器中完成。

用两个断点包装受影响的代码:

function FormatDateTimeEx(const FormatString: AnsiString; DateTime: TDateTime): AnsiString;
begin // <-- breakpoint
  try
    Result := FormatDateTime (FormatString, DateTime) ;
  except
    Result := '???'; 
  end;
end; // <-- breakpoint

进入第一个断点的属性并启用忽略后续异常选项并禁用Break选项。

进入第二个断点的属性并启用处理后续异常选项并禁用Break选项。

【讨论】:

  • 感谢 Remy,我在 this post 中找到了该解决方案。出于某种原因,每次通过断点时,我仍然会在 IDE 事件日志中获得条目。这似乎导致消息循环触发,从而导致应用程序中的其他副作用。我找不到您可以在哪里隐藏这些事件日志条目。
  • 当断点被命中时,您不能禁用 EventLog 消息,只能控制断点的行为方式。但不break 的断点不应触发应用程序消息循环中的事件。
  • 我想我明白发生了什么。它似乎与定时器事件触发有关,然后在第一个定时器事件处理完成之前再次触发。这仅在记录两个事件涉及额外延迟时才会发生。代码味道。
  • 如果您使用的是TTimer,那么在它仍在运行时无法再次输入它的事件,除非您的处理程序正在做一些事情来为新消息抽出消息队列。
  • @DavidHeffernan:在两个断点之间,仍然可以引发和捕获异常。这种方法只是告诉调试器忽略它们,而不是停止代码并提示用户如何处理它们。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2011-04-16
  • 1970-01-01
  • 2011-11-21
相关资源
最近更新 更多