Search code examples
exceptiondelphi-7

How to catch an exception generated in a Thread to be able to manipulate it


I have in my application a procedure that handles the generated exceptions. It turns out that if the exception is generated in untThreadDSR, the exception is not being captured by the procedure to which it should also manipulate it. If the exception is generated from any other form, the procedure captures it correctly.

The procedure is in the main DataModule:

...
private
  { Private declarations }
  procedure HandleExceptions(Sender: TObject; E: Exception);
...

procedure DM.HandleExceptions(Sender: TObject; E: Exception);
begin
  if pos('UNIQUE KEY',E.Message) <> 0 then
  begin
    if pos('UNQ_CUSTOMER_NAME',E.Message) <> 0 then
      MsgMsg('Customer already registered.',2)
    else if pos('UNQ_USERS_LOGIN',E.Message) <> 0 then
      MsgMsg('User already registered.',2);
  end else
  if pos('ERROR_DATASYS',E.Message) <> 0 then
    MsgMsg('The Server Date is incorrect. Please contact your network administrator!',2)
  else //Other messages will be sent to Support
    SendReport(E.Message, V_COMPANY, V_LOGIN, V_APP_VERSION, Sender);
end;

untThreadDSR:

unit untThreadDSR;

interface

uses
  Classes, SysUtils, Messages, Windows, Forms;

type
  TThreadDSR = class(TThread)
  procedure DSR_Start;
  private
    { Private declarations }
  protected
    procedure Execute; override;
  end;

implementation

uses untDM;

procedure TThreadDSR.DSR_Start;
begin
  //I put this intentionally just to generate an exception
  StrToInt('');
  //If this is on any other form, the exception is caught by "HandleExceptions", but from here on out!
end;

procedure TThreadDSR.Execute;
begin
  Synchronize(DSR_Start);
end;

end.

TThreadDSR is called as follows:

procedure TFrmDSR.btnExecuteClick(Sender: TObject);
var
  Thread: TThreadDSR;
begin
  Thread := TThreadDSR.Create(true);
  Thread.FreeOnTerminate := true;
  Thread.Resume;
end;

Solution

  • Exceptions don't cross thread boundaries. If an uncaught exception escapes the procedure called by TThread.Synchronize(), Synchronize() captures the exception and re-raises it in the context of the worker thread. If Execute() subsequently does not catch the exception, the thread will terminate and assign the exception to the TThread.FatalException property.

    You can use the TThread.OnTerminate event (which is called in the context of the main thread) to pass the FatalException to your HandleExceptions() procedure.