Delphi thread exception mechanism

In Delphi 2005 — and probably most other versions — if an exception escapes from the Execute method without being handled, then it is caught by the function that called Execute and stored in the thread's FatalException property. (Look in Classes.pas, ThreadProc.) Nothing further is done with that exception until the thread is freed, at which point the exception is also freed.

It's your responsibility, therefore, to check that property and do something about it. You can check it in the thread's OnTerminate handler. If it's non-null, then the thread terminated due to an uncaught exception. So, for example:

procedure TForm1.onterm(Sender: TObject);
var
  ex: TObject;
begin
  Assert(Sender is TThread);
  ex := TThread(Sender).FatalException;
  if Assigned(ex) then begin
    // Thread terminated due to an exception
    if ex is Exception then
      Application.ShowException(Exception(ex))
    else
      ShowMessage(ex.ClassName);
  end else begin
    // Thread terminated cleanly
  end;
  Dec(nrthd);
end;

There's no need for the interlocked functions for tracking your thread count. Both your thread-creation function and your termination handler always run in the context of the main thread. Plain old Inc and Dec are sufficient.


Threading is one place where you should swallow exceptions.

The gist of handling Exceptions in threads is that if you want the exception to be shown to the end user, you should capture it and pass it on to the main thread where it can safely be shown.

You'll find some examples in this EDN thread How to Handle exceptions in TThread Objects.

procedure TMyThread.DoHandleException;
begin
  // Cancel the mouse capture
  if GetCapture <> 0 then SendMessage(GetCapture, WM_CANCELMODE, 0, 0);
  // Now actually show the exception
  if FException is Exception then
    Application.ShowException(FException)
  else
    SysUtils.ShowException(FException, nil);
end;

procedure TMyThread.Execute;
begin
  FException := nil;
  try
    // raise an Exception
    raise Exception.Create('I raised an exception');
  except
    HandleException;
  end;
end;

procedure TMyThread.HandleException;
begin
  // This function is virtual so you can override it
  // and add your own functionality.
  FException := Exception(ExceptObject);
  try
    // Don't show EAbort messages
    if not (FException is EAbort) then
      Synchronize(DoHandleException);
  finally
    FException := nil;
  end;
end;