Throwing an exception in TThread Execute?

I just realized that my exceptions are not displayed to the user in my threads!

First I used this in my thread to throw an exception that does not work:

except on E:Exception do begin raise Exception.Create('Error: ' + E.Message); end; 

The IDE shows me exceptions, but my application does not work!

I reviewed the solution, here is what I found:

Delphi Thread Exclusion Mechanism

http://www.experts-exchange.com/Programming/Languages/Pascal/Delphi/Q_22039681.html

And not one of them worked for me.

Here is my theme unit:

 unit uCheckForUpdateThread; interface uses Windows, IdBaseComponent, IdComponent, IdTCPConnection, IdTCPClient, IdHTTP, GlobalFuncs, Classes, HtmlExtractor, SysUtils, Forms; type TUpdaterThread = class(TThread) private FileGrabber : THtmlExtractor; HTTP : TIdHttp; AppMajor, AppMinor, AppRelease : Integer; UpdateText : string; VersionStr : string; ExceptionText : string; FException: Exception; procedure DoHandleException; procedure SyncUpdateLbl; procedure SyncFinalize; public constructor Create; protected procedure HandleException; virtual; procedure Execute; override; end; implementation uses uMain; { TUpdaterThread } constructor TUpdaterThread.Create; begin inherited Create(False); end; procedure TUpdaterThread.Execute; begin inherited; FreeOnTerminate := True; if Terminated then Exit; FileGrabber := THtmlExtractor.Create; HTTP := TIdHTTP.Create(nil); try try FileGrabber.Grab('http://jeffijoe.com/xSky/Updates/CheckForUpdates.php'); except on E: Exception do begin UpdateText := 'Error while updating xSky!'; ExceptionText := 'Error: Cannot find remote file! Please restart xSky and try again! Also, make sure you are connected to the Internet, and that your Firewall is not blocking xSky!'; HandleException; end; end; try AppMajor := StrToInt(FileGrabber.ExtractValue('AppMajor[', ']')); AppMinor := StrToInt(FileGrabber.ExtractValue('AppMinor[', ']')); AppRelease := StrToInt(FileGrabber.ExtractValue('AppRelease[[', ']')); except on E:Exception do begin HandleException; end; end; if (APP_VER_MAJOR < AppMajor) or (APP_VER_MINOR < AppMinor) or (APP_VER_RELEASE < AppRelease) then begin VersionStr := Format('%d.%d.%d', [AppMajor, AppMinor, AppRelease]); UpdateText := 'Downloading Version ' + VersionStr; Synchronize(SyncUpdateLbl); end; finally FileGrabber.Free; HTTP.Free; end; Synchronize(SyncFinalize); end; procedure TUpdaterThread.SyncFinalize; begin DoTransition(frmMain.TransSearcher3, frmMain.gbLogin, True, 500); end; procedure TUpdaterThread.SyncUpdateLbl; begin frmMain.lblCheckingForUpdates.Caption := UpdateText; end; procedure TUpdaterThread.HandleException; begin FException := Exception(ExceptObject); try Synchronize(DoHandleException); finally FException := nil; end; end; procedure TUpdaterThread.DoHandleException; begin Application.ShowException(FException); end; end. 

If you need more information, just let me know.

Again: the IDE catches all exceptions, but my program does not show them.

EDIT: it was a Cosmin solution that worked in the end - and the reason it was not in the beginning was because I did not add the ErrMsg variable, instead I just placed any variable in Synchronize that would not work, but I I do not know why. I realized this when I had no other ideas, and I just messed up the solutions.

As always, the joke is about me. = P

+8
multithreading exception delphi raise
source share
6 answers

Here is my very, very short “question” on this issue. It only works in Delphi 2010+ (because anonymous methods were introduced in this version). Unlike the more complex methods already published on my page, only an error message is displayed, nothing more, nothing more.

 procedure TErrThread.Execute; var ErrMsg: string; begin try raise Exception.Create('Demonstration purposes exception'); except on E:Exception do begin ErrMsg := E.ClassName + ' with message ' + E.Message; // The following could be all written on a single line to be more copy-paste friendly Synchronize( procedure begin ShowMessage(ErrMsg); end ); end; end; end; 
+9
source share

Something very important that you need to understand about reusable development:

Each thread has its own call stack, almost as if they were separate programs. This includes the main thread of your program.

Streams can only interact with each other in certain ways:

  • They can work with shared data or objects. This can lead to concurrency race conditions, and therefore you should be able to help them share data. This brings us to the next point.
  • They can "signal each other" using various OS support routines. These include things such as:
    • Mutexes
    • Critical Sections
    • Events
  • And finally, you can send messages to other threads. If the stream was somehow recorded as a message receiver.

NB . Note that threads cannot strictly refer to other threads directly. If, for example, Thread A tried to call Thread B directly, it would be a step in the Thread A call stack!

This brings us to the topic of the question: "exceptions do not occur in my threads"

The reason for this is that all exceptions are:

  • Write down the error
  • And spin up the call stack . <- NB: your TThread instance cannot disable the call stack of the main thread and cannot arbitrarily interrupt the execution of the main threads.

This way, TThread will not automatically notify you of exceptions in your main application.

You must make an explicit decision about how you want to handle errors in threads and execute accordingly.

Decision

  • The first step is the same as in a single-threaded application. You need to decide what the error means and how the thread should respond.
    • Should the thread continue processing?
    • If the thread is interrupted?
    • Should an error be logged / reported?
    • Is an error required for the user? <- This is certainly the most difficult to implement, so we will skip it now.
  • Once this is accepted, execute the appropriate excpetion handler.
  • TIP: Make sure the exception doesn't escape the thread. The OS won't like you if it does.
  • If you need the main program (thread) to report an error to the user, you have several options.
    • If the stream was recorded to return the result object, then it is easy: make changes so that it can return an error in this object if something went wrong.
    • Send a message to the main thread to report an error. Please note: the main thread already implements the message loop, so your application will report an error as soon as it processes this message.

EDIT: Sample code for the specified requirement.

If all you want to do is notify the user, then Cosmind Prund's Answer should work just fine for Delphi 2010. Older versions of Delphi require a bit more work. The following is conceptually similar to Jeff's own answer , but with no errors:

 procedure TUpdaterThread.ShowException; begin MessageDlg(FExceptionMessage, mtError, [mbOk], 0); end; procedure TUpdaterThread.Execute; begin try raise Exception.Create('Test Exception'); //The code for your thread goes here // // except //Based on your requirement, the except block should be the outer-most block of your code on E: Exception do begin FExceptionMessage := 'Exception: '+E.ClassName+'. '+E.Message; Synchronize(ShowException); end; end; end; 

Some important corrections to Jeff's own answer, including the implementation shown in his question:

Calling Terminate only makes sense if your thread is implemented in a while not Terminated do .... while not Terminated do . See what the Terminate method actually does.

Calling Exit is a waste, but you probably did it due to your next mistake.

In your question, you handle each step in your own try...except to handle the exception. This is an absolute no-no ! By doing this, you pretend that although an exception has occurred, everything is in order. Your thread is trying to take the next step, but in fact it is guaranteed to fail! This is not a way to handle exceptions!

+13
source share

Threads do not automatically propagate to other threads. Therefore, you must deal with it yourself.

Rafael outlined one approach, but there are alternatives. Raphael's decision points to a synchronous call to the exception by sorting it into the main thread.

In one of my own uses of threads, a thread pool, threads capture and take responsibility for exceptions. This allows the control thread to process them as it sees fit.

The code is as follows.

 procedure TMyThread.Execute; begin Try DoStuff; Except on Exception do begin FExceptAddr := ExceptAddr; FException := AcquireExceptionObject; //FBugReport := GetBugReportCallStackEtcFromMadExceptOrSimilar. end; End; end; 

If the control thread chooses to raise an exception, it can do this as follows:

 raise Thread.FException at Thread.FExceptAddr; 

Sometimes you may have code that cannot cause synchronization, for example. some DLLs and this approach are useful.

Please note that if you do not throw an exception that was caught, then you need to destroy it, otherwise you will have a memory leak.

+6
source share

Well,

It will be difficult without source code, but I tested this:

How to handle exceptions in TThread objects

And it works great. Perhaps you should take a look at it.

EDIT:

You do not follow what the links that you indicate indicate. Check out my link and you will see how to do it.

EDIT 2:

Try and tell if this worked:

  TUpdaterThread= class(TThread) private FException: Exception; procedure DoHandleException; protected procedure Execute; override; procedure HandleException; virtual; end; procedure TUpdaterThread.Execute; begin inherited; FreeOnTerminate := True; if Terminated then Exit; FileGrabber := THtmlExtractor.Create; HTTP := TIdHTTP.Create(Nil); try Try FileGrabber.Grab('http://jeffijoe.com/xSky/Updates/CheckForUpdates.php'); Except HandleException; End; Try AppMajor := StrToInt(FileGrabber.ExtractValue('AppMajor[', ']')); AppMinor := StrToInt(FileGrabber.ExtractValue('AppMinor[', ']')); AppRelease := StrToInt(FileGrabber.ExtractValue('AppRelease[[', ']')); Except HandleException; End; if (APP_VER_MAJOR < AppMajor) or (APP_VER_MINOR < AppMinor) or (APP_VER_RELEASE < AppRelease) then begin VersionStr := Format('%d.%d.%d', [AppMajor, AppMinor, AppRelease]); UpdateText := 'Downloading Version ' + VersionStr; Synchronize(SyncUpdateLbl); end; finally FileGrabber.Free; HTTP.Free; end; Synchronize(SyncFinalize); end; procedure TUpdaterThread.HandleException; begin FException := Exception(ExceptObject); try Synchronize(DoHandleException); finally FException := nil; end; end; procedure TMyThread.DoHandleException; begin Application.ShowException(FException); end; 

EDIT 3:

You said you could not catch an EIdHTTPProtocolException. But it works for me. Try this sample and see it for yourself:

 procedure TUpdaterThread.Execute; begin Try raise EIdHTTPProtocolException.Create('test'); Except HandleException; End; end; 
+3
source share

Earlier I used SendMessge to communicate between threads using TWMCopyData, so I think the following should work:

 Const MyAppThreadError = WM_APP + 1; constructor TUpdaterThread.Create(ErrorRecieverHandle: THandle); begin Inherited Create(False); FErrorRecieverHandle := Application.Handle; end; procedure TUpdaterThread.Execute; var cds: TWMCopyData; begin try DoStuff; except on E:Exception do begin cds.dwData := 0; cds.cbData := Length(E.message) * SizeOf(Char); cds.lpData := Pointer(@E.message[1]); SendMessage(FErrorRecieverHandle, MyAppThreadError, LPARAM(@cds), 0); end; end; end; 

I used it only to send simple data types or strings, but I am sure that it can be adapted to send additional information as needed.

You will need to add Self.Handle to the constructor in the form created by the stream and handle the messsage in the form that created it

 procedure HandleUpdateError(var Message:TMessage); message MyAppThreadError; var StringValue: string; CopyData : TWMCopyData; begin CopyData := TWMCopyData(Msg); SetLength(StringValue, CopyData.CopyDataStruct.cbData div SizeOf(Char)); Move(CopyData.CopyDataStruct.lpData^, StringValue[1], CopyData.CopyDataStruct.cbData); Message.Result := 0; ShowMessage(StringValue); end; 
+2
source share

It is strange that everyone answered this question, but could not identify an obvious problem: given that the exceptions that occur in the background thread are asynchronous and can occur at any time, this means that when displaying exceptions from the background thread, a pop-up dialog box will pop up in in random order for the user, it is quite possible to show an exception that has nothing to do with what the user is doing at the moment. I doubt this can improve the user interface.

+1
source share

All Articles