How to catch unhandled exception that are raised in background thread?
How to catch unhandled exception that are raised in background thread?
I have for habit to execute anonymous thread like :
TThread.CreateAnonymousThread(
procedure
begin
.....
end).start;
But the problem is that if some unhandled exception will raise during the execution, then i will be not warned about it! For the main thread we have Application.OnException
. Do we have something similar for background thread ?
Application.OnException
4 Answers
4
TThread
has a public FatalException
property:
TThread
FatalException
If the Execute
method raises an exception that is not caught and handled within that method, the thread terminates and sets FatalException
to the exception object for that exception. Applications can check FatalException
from an OnTerminate
event handler to determine whether the thread terminated due to an exception.
Execute
FatalException
FatalException
OnTerminate
For example:
procedure TMyForm.DoSomething;
begin
...
thread := TThread.CreateAnonymousThread(...);
thread.OnTerminate := ThreadTerminated;
thread.Start;
...
end;
procedure TMyForm.ThreadTerminated(Sender: TObject);
begin
if TThread(Sender).FatalException <> nil then
begin
...
end;
end;
TThread
CreateAnonymousThread
@JerryDodge I'm aware that the OP is using.
CreateAnonymousThread()
. It returns a suspended TThread
object, you can tweak its properties and assign event handlers to it before calling Start()
on it– Remy Lebeau
Sep 18 '18 at 16:03
CreateAnonymousThread()
TThread
Start()
Never knew that (FatalException property...). I think I still prefer catching and handling them in place, though. This seems less controlled, generally.
– J...
Sep 18 '18 at 16:12
@J... It depends on your needs.
Execute
is wrapped in a try..except
block, and OnTerminate
is synced with the UI thread. If you don't need that sync, you can skip the event– Remy Lebeau
Sep 18 '18 at 16:18
Execute
try..except
OnTerminate
@RemyLebeau thanks ! yes their is FatalException but anyway it's also painless to update all existing thread to override in all of them the ThreadTerminated than using a try .. finally in each of them :(
– loki
Sep 18 '18 at 18:06
N̶o̶,̶ ̶t̶h̶e̶r̶e̶ ̶i̶s̶ ̶n̶o̶t̶h̶i̶n̶g̶ ̶s̶i̶m̶i̶l̶a̶r̶ ̶f̶o̶r̶ ̶a̶ ̶b̶a̶c̶k̶g̶r̶o̶u̶n̶d̶ ̶t̶h̶r̶e̶a̶d̶.̶ (Thanks, Remy!)
The best solution is to always be absolutely certain to never let an exception escape from a thread. Ideally, your thread procedures should look something like this :
TThread.CreateAnonymousThread(
procedure
begin
try
your code
except
on E : Exception do
... handle it!
end;
end).start;
It's a pity :( :( thanks !
– loki
Sep 18 '18 at 15:39
Actually, there is something... See my answer
– Remy Lebeau
Sep 18 '18 at 16:00
Ok, finally I got the best answer :
Assign the global ExceptionAcquired
procedure to our own implementation (it is nil by default). This procedure gets called for unhandled exceptions that happen in other threads than the main thread.
ExceptionAcquired
ExceptionAcquired := MyGlobalExceptionAcquiredHandler;
@DavidHeffernan ok i removed it from the sample
– loki
Sep 19 '18 at 6:43
The answer of J... and Remy Lebeau are good with what delphi offer, but i need a little more and I finally decide to modify a little the unit System.Classes
System.Classes
var
ApplicationHandleThreadException: procedure (Sender: TObject; E: Exception) of object = nil;
function ThreadProc(const Thread: TThread): Integer;
...
try
Thread.Execute;
except
Thread.FFatalException := AcquireExceptionObject;
if assigned(ApplicationHandleThreadException) and
assigned(Thread.FFatalException) and
(Thread.FFatalException is Exception) and
(not (Thread.FFatalException is EAbort)) then
ApplicationHandleThreadException(Thread, Exception(Thread.FFatalException));
end;
in this way you just need to assign ApplicationHandleThreadException
to handle unhandled exception raise in any TThread. You don't need to be worry about the multi thread because global var like ExceptAddr
are declared as threadvar
so everything work fine, even to retrieve the stack trace !
ApplicationHandleThreadException
ExceptAddr
threadvar
https://quality.embarcadero.com/browse/RSP-21269
Modifying the RTL source is arguably a bad idea if you want your code to work when compiled on any system other than your own.
– J...
Sep 18 '18 at 20:19
@J... yes i agree, especially when you upgrade delphi! But I can not imagine any descent app using firemonkey who didn't touche the RTL source !
– loki
Sep 19 '18 at 6:44
Thanks for contributing an answer to Stack Overflow!
But avoid …
To learn more, see our tips on writing great answers.
Required, but never shown
Required, but never shown
By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy
Works great when using a
TThread
class, but OP is usingCreateAnonymousThread
, so might want to point that out...– Jerry Dodge
Sep 18 '18 at 16:01