提出异常不会显示消息

时间:2017-08-22 18:17:12

标签: delphi exception-handling

我正在努力做一些我一直认为是直截了当的事情,但显然我在这种情况下缺少一些东西。

我正在使用Delphi Seattle。

在我的程序的一部分中,我有一个重复调用子计算的主要计算(循环)。在某些情况下,子计算结果将朝向无穷大并导致浮点溢出异常。我无法预测这一点,也无法定义/捕获可接受的最大值(取决于大小写),因此我需要捕获溢出异常,通知用户并中止计算。

我已将此计算简化为以下示例程序。代码本身当然是无稽之谈,但重点是在与我的实际应用程序类似的程序结构中强制出现溢出异常。

  type
  TForm1 = class(TForm)
    Button1: TButton;
    procedure Button1Click(Sender: TObject);
  private
    { Private declarations }
    function MainCalculation: boolean;
    function SubCalculation(AFloatingPoint: Double): Double;
  public
    { Public declarations }
  end;

.........

procedure TForm1.Button1Click(Sender: TObject);
    begin
      if MainCalculation then
        ShowMessage('Calculation succeeded.')
      else
        ShowMessage('Calculation failed.');
    end;

    function TForm1.MainCalculation: boolean;
    var
      ii: Integer;
      dd: Double;
    begin
      try
        dd := 1E200;
        for ii := 1 to 100 do
          dd := dd * SubCalculation(dd);
      except
        raise Exception.Create('Error in main calculation.');
      end;
    end;

    function TForm1.SubCalculation(AFloatingPoint: Double): Double;
    begin
      try
        result := Power(AFloatingPoint, AFloatingPoint);
      except
        raise Exception.Create('Error in sub-calculation.');
      end;
    end;

从调试器运行,我得到三个调试器异常通知:

  1. 异常类$ C0000091(浮点溢出)
  2. 子计算中的例外
  3. 主要计算中的例外情况
  4. 但是,只有最后一个作为例外显示给用户。

    在我的实际应用中,情况更糟。当我运行应用程序并发生溢出异常时,没有向用户显示任何内容,并且(主)计算只是以部分结果中止。

    有人可以向我解释为什么我没有提出两个例外(如我所料)?是否有某个特定的设置导致溢出异常被区别对待?任何帮助我前进的建议都将受到高度赞赏。当然,如果需要,我会非常乐意提供更多细节。

    提前致谢! 标记

2 个答案:

答案 0 :(得分:1)

  

我认为对例外情况的概念不存在误解。只是一旦它“嵌套”,最终用户就不会向特定消息提出标准异常(即不是EMyCustomException)。

您确实对异常的某些方面存在误解。我怀疑你是否正确理解它们如何影响代码流(大多数人都在努力解决的问题)。但是,任何期望他们会显示信息都表明存在巨大的误解。

请注意,异常以任何方式自动显示异常消息从根本上是不正确的,因为如果应用程序在无人参与的服务器上运行,那么您想要的最后一件事是在屏幕上弹出对话框,等待不存在的用户关闭他们。<​​/ p>

即使在前端,也会为 可怕的 用户体验,为您选择的每个负面后果副作用显示一长串异常消息报告样板(DRY违规)try...except阻止。

显示任何异常消息的唯一时间是代码明确调用某些内容时

例如:ShowMessage(SomeException.Message);
Application.ShowException(SomeException.Message);

您还问:

  

但如果“引发Exception.Create ......”并不意味着显示任何内容,那么为什么外层呢?

我明白为什么你会这么想;但这正是前面提到的误解......

外层raise Exception.Create('Some Message'); 显示任何讯息。 Delphi框架有自己的try...except块,用于捕获外层异常,而 default 将显示异常的消息和类。您可以覆盖默认处理程序以显示不同的消息或使用不同的对话框,或者不显示任何内容,只需记录消息。

作为练习,您可能希望使用调试器逐步执行vcl / rtl代码以查看此操作。 Delphi附带的源代码可能看起来令人生畏,但研究该代码是一种很好的学习方法。

建议进一步阅读

显然,您可以理解在每个异常处理程序中跟踪信息的问题。这很好;但你试图去做的方式是有缺陷的。我建议你阅读下面的内容:

How should I re-raise a Delphi exception after logging it?
What is the correct way to nest exceptions? - Using Delphi

我还建议您考虑使用异常处理框架。我不确定以下的当前状态,但它们都值得调查:

  • 疯狂除外
  • 特殊魔术
  • 尤里卡
  • Jcl Debug

请注意,上述工具能够生成一个调用堆栈,该调用堆栈提供导致触发器异常的完整调用链。将此与常规跟踪日志记录相结合,您可以使用一组功能强大的工具来全面调查大多数典型错误。

通常,您不希望编写大量try...except块。他们真的应该是(ahem)“ 例外 到规则”。

旁注 :确认您的代码只是一个示例......

我必须指出,就目前情况而言,您的问题代码存在严重缺陷。您无法保证Result的{​​{1}}将被初始化,无论 if / where 是否可能发生异常以及是否被吞下。您需要非常小心,以避免将错误的值返回给MainCalculation等调用者。

你想要的最后一件事是调用者错误地假设计算失败成功,反之亦然。 (这是结构化异常模型的一个好处,而不是编写大量异常处理程序。)

答案 1 :(得分:0)

使用sysUtils.abort("silent exception")