Exception Handling

14.1 try..except

try
  var N := StrToInt(Edit1.Text);
  Result := 100 div N;
except
  on E: EConvertError do
    ShowMessage('Not a valid number: ' + E.Message);
  on E: EDivByZero do
    ShowMessage('Cannot divide by zero');
  on E: Exception do
  begin
    LogError(E.ClassName + ': ' + E.Message);
    raise;                          // re-raise the same exception
  end;
end;

14.2 try..finally (Resource Protection)

Delphi has no combined try..except..finally. Nest them: try try ... except ... end; finally ... end;.

var List := TStringList.Create;
try
  List.LoadFromFile('data.txt');
  Process(List);
finally
  List.Free;                        // ALWAYS runs: prevents leaks
end;

// Nested for multiple resources
var A := TFileStream.Create('a.bin', fmOpenRead);
try
  var B := TFileStream.Create('b.bin', fmCreate);
  try
    B.CopyFrom(A, 0);
  finally
    B.Free;
  end;
finally
  A.Free;
end;

14.3 Raising and Custom Exceptions

Exception class names start with E by convention. Inherit from Exception.

Common exceptions: EConvertError, EDivByZero, ERangeError, EIntOverflow, EAccessViolation, EInvalidPointer, EOutOfMemory, EAbort (silent: Abort), EFileNotFoundException, EInOutError, EArgumentException, EInvalidOperation, EListError, EDatabaseError, EFDDBEngineException, ENetHTTPClientException.

type
  EInsufficientFunds = class(Exception)
  private
    FShortfall: Currency;
  public
    constructor Create(AShortfall: Currency);
    property Shortfall: Currency read FShortfall;
  end;

constructor EInsufficientFunds.Create(AShortfall: Currency);
begin
  inherited CreateFmt('Insufficient funds: short by %m', [AShortfall]);
  FShortfall := AShortfall;
end;

procedure TAccount.Withdraw(Amount: Currency);
begin
  if Amount <= 0 then
    raise EArgumentOutOfRangeException.Create('Amount must be positive');
  if Amount > FBalance then
    raise EInsufficientFunds.Create(Amount - FBalance);
  FBalance := FBalance - Amount;
end;

14.4 Global Exception Handling and Best Practices

Abort raises EAbort, which stops processing silently (no message dialog).

Exception.RaiseOuterException(...) / E.InnerException chains exceptions.

Tools: madExcept and EurekaLog for detailed crash reports with call stacks. JclDebug (JEDI) for stack traces.

Best practices: protect every created object with try..finally, catch specific exceptions, don't swallow exceptions, use Assert for programmer errors ({$C+} assertions).

// VCL: TApplicationEvents.OnException or Application.OnException
procedure TMainForm.AppException(Sender: TObject; E: Exception);
begin
  LogToFile(E);
  Application.ShowException(E);
end;