One of the limitations of handling .net exceptions is that for the code in the Finally block there is no good way to find out which exception, if any, caused the code in the Try block to exit, and there is also a normal way for the code in the finally block that has such information to make it accessible to code that may throw an exception.
In vb.net, it allows you to clone things in a way that works very well, although it looks a bit ugly.
Module ExceptionDemo Function CopySecondArgToFirstAndReturnFalse(Of T)(ByRef dest As T, src As T) As Boolean dest = src Return False End Function Function AnnotateExceptionAndReturnFalse(ex As Exception, TryBlockException As Exception) As Boolean If ex Is Nothing Then Return False ' Should never occur If TryBlockException Is Nothing Then Return False ' No annotation is required ex.Data("TryBlockException") = TryBlockException Return False End Function Sub ExceptionTest(MainAction As Action, CleanupAction As Action) Dim TryBlockException As Exception = Nothing Try MainAction() Catch ex As Exception When CopySecondArgToFirstAndReturnFalse(TryBlockException, ex) ' This block never executes, but above grabs a ref to any exception that occurs Finally Try CleanupAction() Catch ex As Exception When AnnotateExceptionAndReturnFalse(ex, TryBlockException) ' This block never executes, but above performs necessary annotations End Try End Try End Sub Sub ExceptionTest2(Message As String, MainAction As Action, CleanupAction As Action) Debug.Print("Exception test: {0}", Message) Try ExceptionTest(MainAction, CleanupAction) Catch ex As Exception Dim TryBlockException As Exception = Nothing Debug.Print("Exception occurred:{0}", ex.ToString) If ex.Data.Contains("TryBlockException") Then TryBlockException = TryCast(ex.Data("TryBlockException"), Exception) If TryBlockException IsNot Nothing Then Debug.Print("TryBlockException was:{0}", TryBlockException.ToString) End Try Debug.Print("End test: {0}", Message) End Sub Sub ExceptionDemo() Dim SuccessfulAction As Action = Sub() Debug.Print("Successful action") End Sub Dim SuccessfulCleanup As Action = Sub() Debug.Print("Cleanup is successful") End Sub Dim ThrowingAction As Action = Sub() Debug.Print("Throwing in action") Throw New InvalidOperationException("Can't make two plus two equal seven") End Sub Dim ThrowingCleanup As Action = Sub() Debug.Print("Throwing in cleanup") Throw New ArgumentException("That not an argument--that just contradiction") End Sub ExceptionTest2("Non-exception case", SuccessfulAction, SuccessfulCleanup) ExceptionTest2("Exception in main; none in cleanup", ThrowingAction, SuccessfulCleanup) ExceptionTest2("Exception in cleanup only", SuccessfulAction, ThrowingCleanup) ExceptionTest2("Exception in main and cleanup", ThrowingAction, ThrowingCleanup) End Sub End Module
The module above starts with a few helper modules, which probably should be in their own Exceptional Helpers module. The ExceptionTest method shows a template for code that can throw an exception in the Try and Finally block. The ExceptionTest2 method calls ExceptionTest and reports which exception if it returns. ExceptionDemo raises ExceptionTest2 in such a way as to throw exceptions in different combinations of Try and Finally blocks.
As shown, if an exception occurs during cleaning, this exception will be returned to the caller, with the original exception being an element in its Data dictionary. An alternative pattern would be to catch the exception that occurs during the cleanup and include it in the data of the original exception (which would remain unmapped). My general tendency is that in many cases it is better to throw an exception that occurs during the cleanup, since any code that planned to deal with the original exception probably expects the cleanup to succeed; if such a wait cannot be fulfilled, the exception that escapes should probably not be the one that the caller expected. Note also that the latter approach will require a slightly different way of adding information to the original exception, because for the exception that is selected in the nested Try block, you may need to store information about several exceptions that were selected in the nested blocks Finally .