Search code examples
delphiexceptiondelphi-2007

Where does exception message comes from?


When exceptions like EZeroDivide, EOverflow, EMathError... are raised, they appear with a default message.

For example, the following code raises an EZeroDivide with the following message:

Floating point division by zero

procedure TForm1.Button1Click(Sender: TObject);
var
  N : Extended;
  D : Extended;
begin
  N := 100;
  D := 0;
  Caption := FloatToStr(N/D);
end;

EZeroDivide Floating point division by zero

When I "manually" raise an EZeroDivide exception by code, I have to pass the Msg parameter to the constructor and it is raised as an EZeroDivide exception which have an empty string message:

procedure TForm1.Button2Click(Sender: TObject);
begin
  raise EZeroDivide.Create('');
end;

EZeroDivide without message

Where does default exception messages come from?


Solution

  • Those exception instances are generated internally by the RTL. The specific string in the question can be found in the resourcestring section of SysConst.pas

     SZeroDivide = 'Floating point division by zero';
    

    Internally the RTL uses the Error method to raise such exceptions. The full list of runtime errors is defined in the System unit in the following enum :

    TRuntimeError = (reNone, reOutOfMemory, reInvalidPtr, reDivByZero,
      reRangeError, reIntOverflow, reInvalidOp, reZeroDivide, reOverflow,
      reUnderflow, reInvalidCast, reAccessViolation, rePrivInstruction,
      reControlBreak, reStackOverflow,
      { reVar* used in Variants.pas }
      reVarTypeCast, reVarInvalidOp,
      reVarDispatch, reVarArrayCreate, reVarNotArray, reVarArrayBounds,
      reAssertionFailed,
      reExternalException, { not used here; in SysUtils }
      reIntfCastError, reSafeCallError,
      reMonitorNotLocked, reNoMonitorSupport
    {$IF defined(LINUX) or defined(MACOS) or defined(ANDROID)}
      , reQuit
    {$ENDIF LINUX or MACOS or ANDROID}
    {$IFDEF POSIX}
      , reCodesetConversion
    {$ENDIF POSIX}
      , rePlatformNotImplemented, reObjectDisposed
      );
    

    If you have a genuine reason for raising a runtime error yourself you can do it by calling, for example :

     System.Error(reZeroDivide);
    

    If you're very observant, you'll notice that Error does not have a forward declaration in the System unit but exists only in the implementation section. Typically this would prevent you from having visibility to call the method outside of its containing unit, but the System unit is special in many ways and this method is accessible globally. Just beware that you will also often be including the Windows unit and this declares a const named ERROR which will usually hide System.Error, so scoping it explicitly is usually required.