10
votes

We have 2 applications, the first one is VCL project, the other is a windows service.

In the VCL project we do:

try
except
  on E: Exception do
    // do something with E.Message
end

but in the windows service (which uses several threads) we use:

try
except
    // do something with Exception(ExceptObject).Message
end

The information I got from my coworkers is that "We must use ExceptObject in threads and E: Exception in applications that use GUI". But I couldn't find anything regarding this.

I found an example here http://edn.embarcadero.com/article/10452 where it uses an instance variable to store the exception and it uses the ExceptObject, but gives no explanation why.

Is this ExceptObject even thread-safe (since it comes from the unit 'System')?

So what is the right way to handle exceptions in Delphi and why is there more than one way to do it?

2
The reason the edn article uses ExceptObject is the temporary exception instance is not accessible where the code handles the exception. Another way could be to pass the temporary variable to the procedure. Can't say anything about your service.Sertac Akyuz
I think the edn code is wrong. It should use AcquireExceptionObject, the reference FException there stores may not be valid when the proc is synched with the main thread.Sertac Akyuz
It's VCL not VLC and threading or GUI is never a driver for such choices.David Heffernan
"We must use ExceptObject in threads and E: Exception in applications that use GUI" - that is completely false. on E: Exception works just fine in all threads whether a GUI is being used or not. The exception handling system doesn't know or care about GUIsRemy Lebeau
"The information I got from my coworkers is...". They stated what they do without explaining "Why?" Remember, all programmers are fallible. The problem with not explaining why: is that it becomes impossible to hold the reasoning up to rational scrutiny. FTR my understanding of the on E: <type> syntax, is that it's syntactic sugar to access ExceptObject with a temporary local variable. Trying to do so manually via ExceptObject exposes you to the risk of invalid typecasts. Although it's convention, I don't believe you can guarantee Exception(ExceptObject) is safe.Disillusioned

2 Answers

10
votes

There is no right way for exception handling. There is just one way. What might confuse you could be dealing with the exception object which is created, and which causes an exception to raise, but whose lifetime is the most important for you here.

In general, there's only two ways of dealing with those exception objects. Either you let them alive beyond the exception block scope and release them by yourself or let them free by the RTL when the exception block ends.

But to answer what I guess you've asked. Exception class isn't thread safe. And, your coworkers were wrong as no one is forced to use specific exception handling in threads. These rules are the same for all threads created by the process, no matter what. Just, those exception objects can be unstable within exception blocks:

1. Get the current exception object from the ExceptObject

The ExceptObject returns the current exception object. In practice, it may cause this; if you store such object reference into a variable inside an exception handler block and another exception will get raised within such block, that stored instance may become invalid. Which is quite unsafe.

But it doesn't mean you could not take a reference of such object and pass it to another thread by using some synchronization mechanisms (since it's not a thread safe class) and work with it there. You just need to take care that no other exception will be raised because that would invalidate the previously stored object so as you must take care of staying inside the exception handler from the caller's point of view and you must use a kind of thread synchronization mechanism.

So actually working with the exception object acquired from an on expression can be more stable than using ExceptObject. But the same rules applies here as well; you'd need to synchronize the object instance from the on expression with another thread (since it's not a thread safe class), but in such case, object acquired from the on expression won't get changed unlike the ExceptObject one can be within a certain exception block.

2. Retain exception object by using AcquireExceptionObject

The AcquireExceptionObject function allows you to keep the exception object alive even out of the exception block.

For an exception handling when speaking about thread synchronization, I'd suggest you using the AcquireExceptionObject function which makes the exception object free to consume, even after the exception block ends. For you that brings the only responsability, free such acquired object by calling the ReleaseExceptionObject procedure or raising the exception by this object again.

8
votes

Victoria is absolutely correct.

Personally, I have a strong preference for this idiom:

try
  ...
   except
     // IO error
     On E : EInOutError do
       ShowMessage('IO error : '+E.Message);
     // Division by zero
     On E : EDivByZero do
       ShowMessage('Div by zero error : '+E.Message);
     // Catch other errors
     else
       ShowMessage('Unknown error');
   end;

To elaborate:

  1. Victoria said "There is no right way for exception handling. There is just one way." That's absolutely correct.

  2. The advice you got about "use one syntax for threads, and the other for GUIs" is simply wrong. There is no "different syntax" for "threads" vs. "GUI". That's nonsense :(

  3. I prefer using on : MyExceptionType in an exception block.

  4. I also prefer to differentiate different exception types, whenever/wherever possible.

  5. The example you cited, http://edn.embarcadero.com/article/10452, deals with how to avoid a possible access violation if you don't handle the exception within that particular thread. Saving the exception instance in a member variable helps mitigate this problem.

The following link might help clarify: