Mark
Mark

Reputation: 140

Raising exceptions is not showing messages

I am struggling with something I have always perceived as something straightforward, but apparently I am missing something in this case.

I am using Delphi Seattle.

In part of my program I have a certain main calculation (a loop) that repeatedly calls a sub-calculation. In certain cases the sub-calculation result will go towards infinity and cause a floating point overflow exception. I cannot predict this, nor can I define / trap an acceptable maximum value (depends on case) so I need to trap the overflow exception, notify the user and abort the calculation.

I have simplified this calculation to the following example program. The code itself is nonsense of course but the point is to force an overflow exception in a similar program structure as my real application.

  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;

Running from the debugger, I get three Debugger Exception Notifications:

  1. Exception class $C0000091 (floating point overflow)
  2. Exception in sub-calculation
  3. Exception in main calculation

However, only the last one is shown to the user as an exception.

In my real application it's even worse. When I run the application and the overflow exception occurs, nothing is shown to the user and the (main) calculation just aborts with partial results.

Can anybody explain to me why I don't get the two exceptions raised (as I would expect)? Is there a specific setting somewhere that causes the overflow exception to be treated different? Any suggestion to help me forward will be highly appreciated. Of course, I'll be more than happy to provide additional details, should this be required.

Thanks in advance! Mark

Upvotes: 1

Views: 2501

Answers (2)

Rychu
Rychu

Reputation: 1

Use sysUtils.abort("silent exception")

Upvotes: 0

Disillusioned
Disillusioned

Reputation: 14832

I don't think there' a misunderstanding on the concept of what an exception is. It's merely that raising a standard Exception (i. e. not an EMyCustomException) with a specific message is not shown to the end-user once it's "nested".

You do have a misunderstanding of some aspects of exceptions. I suspect you correctly understand how they affect code-flow (an area most people struggle with). But any expectation that they will show messages indicates a huge misunderstanding.

Note that it would be fundamentally incorrect for exceptions to in any way automatically display the exception message because if the app runs on an unattended server, the last thing you want is dialogs popping up on screen waiting for a non-existent user to close them.

Even on a front-end, it would be a horrible user experience to display a long chain of exception messages for each and every negative consequence side-effect you choose to report in a boiler-plate (DRY violating) try...except block.

The only time any exception message will be displayed is when code explicitly calls something to do so

E.g.: ShowMessage(SomeException.Message);
or Application.ShowException(SomeException.Message);

You also asked:

but if "raise Exception.Create..." is not meant to display anything, then why does the outer level?

I can see why you might think that; but this is exactly the misunderstanding referred to earlier...

The outer level raise Exception.Create('Some Message'); does NOT display any message. The Delphi framework has its own try...except block which catches the outer level exception, and by default will show the message and class of the exception. You could override the default handler to display a different message or use a different dialog, or not display anything, and simply log the message.

As an exercise, you may want to step through vcl/rtl code using a debugger to see this in action. The source code that ships with Delphi may seem intimidating, but studying that code is an excellent way to learn.

Suggested further reading


Obviously and understandably you have a concern about tracking information at each exception handler. This is good; but the way you're trying to go about it is flawed. I suggest you read up on the following poss-dups:

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

I also recommend you consider using an exception handling framework. I'm not sure of the current state of the following, but they're all worth investigating:

  • Mad Except
  • Exceptional Magic
  • Eureka
  • Jcl Debug

Note that tools like those above are able to generate a call-stack which provides a full chain of calls leading to the trigger exception. Combine this with general trace logging and you have a powerful set of tools to fully investigate most typical errors.

In general you don't want to be writing a large number of try...except blocks. They really should be the (ahem) "exception to the rule".


Side note: Acknowledging your code is merely a sample...

I must point out that as it stands, your question code is seriously flawed. You fail to guarantee the Result of MainCalculation will be initialised no matter if/where an exception might occur and whether or not it's swallowed. You need to be very careful of this to avoid returning the wrong value to callers such as ButtonClick.

The last thing you want is callers incorrectly assuming a failed calculation succeeded or vice-versa. (This is a benefit of the structured exception model and not writing lots of exception handlers.)

Upvotes: 1

Related Questions