Ayende excitedly points out that from within a finally block, you can determine whether an exception was thrown or not; in the comments, he goes on to explore how useful this may be.
Let's begin with noting that Marshal.GetExceptionCode is likely implemented in terms of the VC compiler intrinsic, _exception_code which is exposed through the GetExceptionCode macro. This VC intrinsic can only be used from within the filter expression of an exception filter, or from within the exception handler's block (and it is enforced by the compiler). However, you can call Marshal.GetExceptionCode from anywhere in your program, and it's supposed (according to the documentation) to return 0xCCCCCCCC if no exception was thrown yet. Nonetheless, practice shows that Ayende's tip seems to work; 0 is returned if no exception was thrown, no matter if the method is called from within a finally block or anywhere else.
However, I can see several problems with this approach, some of which were already noted in the original post's comments:
- You can call RaiseException (import from kernel32.dll) and pass 0 as the exception code. It will raise an exception, but Marshal.GetExceptionCode will happily return 0, as that's what you've specified. This can be overcome by using Marshal.GetExceptionPointers() != IntPtr.Zero, but nonetheless invalidates the original approach.
- If you have a catch block that swallows the exception, Marshal.GetExceptionCode will happily return 0 (but that was probably the intent, just making sure it's clear).
- Marshal.GetExceptionCode has a link demand which requires the immediate caller to have permissions to execute unmanaged code (SecurityPermissionAttribute.UnmanagedCode permission). This means it's not useful for any scenario that has to run under partial trust (since it's very unlikely to allow unmanaged code execution under partial trust).
- If someone causes an asynchronous exception to be thrown in your thread, you have no way of knowing whether the exception came from your code (e.g., if you call Thread.Interrupt on a thread using this technique).
- You have no means of identifying what exception was thrown, and blindly committing to some behavior is not very different, in essence, from writing a catch {} block (which is rarely a good idea).
- This isn't portable (e.g. Mono might have a completely different way of representing exceptions which I have no idea of). That probably isn't of much concern to anyone, but still some commenters have pointed this out.
I would deem this technique useful in a very limited set of scenarios, so I think the excitement is rather premature.