Important: as a general rule, you should write your application in a such way that it will behave correctly without EurekaLog on board. This means that if your or 3rd party code throws an exception in a thread - it must be handled in a way that you expect. For example, by showing an error message, canceling action, retrying, etc. Once your application behaves like it is supposed to do - then you add EurekaLog for it. EurekaLog will auto-handle all unhandled exceptions automatically (by hooking few well-known places), and for everything else - you can call EurekaLog from your own handlers when needed. In other words:
• | Incorrect: your application does not show messages for exceptions in thread and you are adding EurekaLog in hopes to fix this behavior; |
• | Correct: your application correctly handles thread exceptions and you are adding EurekaLog to receive reports about such exceptions. |
There are many ways to create and use threads in Delphi. Therefore there are many ways of handling thread exceptions. Typical examples are listed below. For a more complete guide - see our multithreading in EurekaLog series.
BeginThread
If you are using BeginThread function:
uses
EBase; // for BeginThreadEx
// IMPORTANT: BeginThread is replaced with BeginThreadEx
TH := BeginThreadEx(nil, 0, YourThreadFunc, nil, 0, TID);
Alternatively:
uses
EBase; // for SetEurekaLogStateInThread
function YourThreadFunc(Parameter: Pointer): Integer;
begin
// Enable EurekaLog for this thread
SetEurekaLogStateInThread(0, True);
// Your actual code goes here
end;
// Original BeginThread is used
TH := BeginThread(nil, 0, YourThreadFunc, nil, 0, TID);
Or (when you disable low-level hooks for better compatibility with exe protectors):
uses
EBase, // for SetEurekaLogStateInThread and HandleException
EThreadsManager, // for _NotifyThreadGone
ETypes; // for atThread
function YourThreadFunc(Parameter: Pointer): Integer;
begin
// Notify EurekaLog that this thread has started
_NotifyThreadStart;
// Enable EurekaLog for this thread
SetEurekaLogStateInThread(0, True);
try
// Your actual code goes here
except
on E: Exception do
Result := HandleException(E, False, atThread);
end;
// Notify EurekaLog that this thread is done
_NotifyThreadGone;
end;
// Original BeginThread is used
TH := BeginThread(nil, 0, YourThreadFunc, nil, 0, TID);
See for more details:
TThread
If you are using TThread class and wait for the thread to finish:
uses
EBase; // for TThreadEx
type
// IMPORTANT: TThread was replaced with TThreadEx
TMyThread = class(TThreadEx)
protected
procedure Execute; override;
end;
Thread := TMyThread.Create(False); // Thread: TMyThread;
try
// ...
Thread.WaitFor;
E := Thread.FatalException; // E: Exception;
// Clear the FatalException property,
// so it won't be deleted when thread is deleted
PPointer(@Thread.FatalException)^ := nil;
// Re-raise thread's exception
if Assigned(E) then
raise E;
// What if there is no exception, but thread failed?
if Thread.ReturnValue <> 0 then
Abort;
// Success
// Do something with thread here...
// ...
finally
FreeAndNil(Thread);
end;
Note: this is only one of many possible methods.
If you are using TThread class and do not wait for the thread (in other words: "fire and forget" with FreeOnTerminate = True):
uses
EBase, // for TThreadEx
ETypes; // for atThread
type
TMyThread = class(TThreadEx)
protected
procedure Execute; override;
// OnTerminate handler
// You could move it to Form or any other object
procedure HandleThreadException(Sender: TObject);
end;
procedure TMyThread.HandleThreadException(Sender: TObject);
begin
if Assigned(FatalException) then
HandleException(FatalException, False, atThread);
end;
Thread := TMyThread.Create(True); // Thread: TMyThread;
Thread.FreeOnTerminate := True;
Thread.OnTerminate := Thread.HandleThreadException;
Thread.Start;
// Do not access FreeOnTerminate-thread variable after .Start
Thread := nil;
Alternatively, if you do not want or can not use TThreadEx - you could modify both examples above as:
type
TMyThread = class(TThread) // - TThread is used
// ...
procedure TMyThread.Execute;
begin
// Enable EurekaLog for this thread
SetEurekaLogStateInThread(0, True);
// Your actual code goes here
end;
// ... same as above ...
See for more details:
Thread pools
If you are using any kind of thread pool:
uses
EBase, // for SetEurekaLogStateInThread and HandleException
ETypes; // for atThread
function MyTask(lpThreadParameter: Pointer): Integer; stdcall;
begin
// Mark thread for yourself
SetEurekaLogStateInThread(0, True);
try
try
// ... your task code ...
Result := 0; // <- to indicate success
except
on E: Exception do
// To handle exception and indicate failure
Result := HandleException(E, False, atThread);
end;
finally
// Clean after yourself
// (i.e. mark/prepare thread for other tasks)
SetEurekaLogStateInThread(0, False);
end;
end;
procedure TForm1.Button1Click(Sender: TObject);
begin
IsMultiThreaded := True;
// Execute MyTask in a background thread
Win32Check(QueueUserWorkItem(MyTask, nil, 0));
end;
See for more details:
See also:
Send feedback...
|
Build date: 2024-09-30
Last edited: 2024-07-08
|
PRIVACY STATEMENT
The documentation team uses the feedback submitted to improve the EurekaLog documentation.
We do not use your e-mail address for any other purpose.
We will remove your e-mail address from our system after the issue you are reporting has been resolved.
While we are working to resolve this issue, we may send you an e-mail message to request more information about your feedback.
After the issues have been addressed, we may send you an email message to let you know that your feedback has been addressed.
Permanent link to this article: https://www.eurekalog.com/help/eurekalog/how_to_handle_thread_exception.php
|
|