What Should I Know About Structured Exceptions (Seh) in C++

What should I know about Structured Exceptions (SEH) in C++?

They are the Win32 equivalent to Unix signals, and let you catch CPU exceptions such as access violation, illegal instruction, divide by zero.

With the right compiler options (/EHa for Visual C++), C++ exceptions use the same mechanism as stack unwinding works properly for both C++ (user) exceptions and SEH (OS) exceptions.

Unlike C++ exceptions, SEH are not typed but all share the same data structure which has an exception code (the cause) and additional information on what code faulted and what the CPU registers held at the time of the fault. See GetExceptionCode and GetExceptionInformation for more details on this.

Also, SEH has "first-chance" handling, which allows you to log or otherwise handle the exception before unwinding destroys all the local variables.

Difference between a C++ exception and Structured Exception

You actually have three mechanisms:

  • C++ exceptions, implemented by the compiler (try/catch)
  • Structured Exception Handling (SEH), provided by Windows (__try / __except)
  • MFC exception macros (TRY, CATCH - built on top of SEH / C++ exceptions - see also TheUndeadFish's comment)

C++ exceptions usually guarantee automatic cleanup during stack unwinding (i.e. destructors of local objects run), the other mechanisms don't.

C++ exceptions only occur when they are explicitly thrown. Structured Exceptions may occur for many operations, e.g. due to undefined behavior, passing invalid pointers to APIs, unmounting the backing store of a memory mapped file, and many more.

MFC did introduce the exception macros to support exceptions even if compilers didn't implement them.

Consequences of using Structured Exception Handling (SEH)?

/EHa disables an optimization. With /EHs in effect, the compiler can omit exception filters if it can be sure that no C++ exception is ever thrown by the code wrapped in a try {}. That's a small space optimization on x86 and x64, very small time optimization on x86. Problem is, those filters are needed if you catch non-C++ exceptions. The consequence is that the stack gets unwound when such an exception is caught without the destructor of a C++ object getting called. Not good, /EHa avoids it.

Mixing doesn't cause linker problems. It causes the above problem.

Yes, /EHa also makes catch(...) do a very stupid thing, it really catches everything. That ship wreck sailed a while ago though, Pokemon C++ exception handling is a bad idea too.

Different behaviour observed when using SEH (structured exception handling)

@Joachim points out correctly about the comma operator problem.

I think, the __except() should look like this:

__except((GetExceptionCode() == EXCEPTION_ACCESS_VIOLATION) ?
EXCEPTION_EXECUTE_HANDLER :
EXCEPTION_CONTINUE_SEARCH)

This will make the exception handler execute if the exception is an access violation. If it's not, the exception will propagate up to the closest outer __try, if any.

I'm fearing that pTest is a pointer to a class and it may not need to be dereferenced in order to execute GetValue() and the compiler recognizes that when optimizations are enabled in the release mode. Or maybe even it sees GetValue() as useless and does not generate any code to call it. It's also possible that the compiler sees that dereferencing a NULL pointer results in undefined behavior and decides to punish your for it at compile time by screwing with your code, it has every right to do it. gcc is notorious for doing it, for example.

Should I use SEH by default in C?

In my opinion, no. The downside is a lot of extra __try/__finally noise and I don't see what the upside is.

SelectObject(hDCCompat, hBmpCompat);
BitBlt(hDC, ..., hDCCompat, ...);

How are you expecting these to fail? For example SelectObject reports an error by returning NULL (which you don't check for), not by raising an SEH exception. Many instances of SEH exceptions indicate a fundamental error that isn't recoverable (you have corrupted memory or you've made a logic error such as passing in an invalid handle to a function or something). These sorts of errors can't be handled gracefully and a crash is typically easier to debug.

If you want to make your code robust in the face of early returns (which many C coding standards discourage, in part for this reason) then you should consider structuring your code in a way where it is more difficult to modify in dangerous ways. E.g.

int f()
{
int ret;
Resource r;

if (!AcquireResource(&r))
return FAIL;

ret = FunctionWithLogicAndEarlyReturns(&r);

CleanupResource(&r);
return ret;
}

You can hope that because this function is simple there will be little temptation to put in additional early returns, early returns in the called "logic" function don't harm the clean-up of the acquired resource.

intermixing c++ exception handling and SEH (windows)

You could wrap the addrinfo in a class that calls getaddrinfo in the constructor and freeaddrinfo in its destructor.

That way it will always be freed, whether there is an exception thrown or not.

SEH error reporting

If you raise an exception in your filter you will end up in the filter method again. Your exception will be handled by the same __except block.

But there is no problem in using another __try __except block in your filter method.

Unfortunately I cannot give you any references to this. I just tried it my self and you can too. Just force an division by zero.

Usually I do not use SEH but the few times I did I had no issues in raising an exception in the filter method. But I did not find anything in the msdn when I looked for this.

Structured Exception Handler (SEH) does not catch heap corruption

Apparently it's intentional that heap corruptions cannot be caught by user-defined exception handlers, even though they're emitted as exceptions with their own exception code (0xC0000374 "STATUS_HEAP_CORRUPTION"). Here's a Visual C++ bug report which was basically closed as "won't fix":

https://connect.microsoft.com/VisualStudio/feedback/details/664497/cant-catch-0xc0000374-exception-status-heap-corruption

As you have discovered, this is not a bug in the compiler or the OS. The heap corruption that your function causes is treated as a critical error, and as part of handling that error the OS terminates the process. This is what causes your exception handlers to not be invoked.

I'd guess that Windows Error Reporting or other ways of creating a crash dump could still catch it.

As for preventing the dialog, in the registry you can either disable WER completely or just disable the dialog so that the process won't block:

https://msdn.microsoft.com/de-de/library/windows/desktop/aa366711(v=vs.85).aspx (see "DontShowUI")



Related Topics



Leave a reply



Submit