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;