C++, __try and try/catch/finally
Asked Answered
W

3

61

I'm wondering a bit about C++ try/catch/finally blocks. I've seen these commands with two underscores like __try. But MVSC 2010 projects also run without the underscores. So when do you need these underscores?

Wivinah answered 13/8, 2011 at 9:5 Comment(6)
finally is not in C++ and is really kind of pointless for properly written C++ code.Rothstein
@Rothstein Saying something is pointless is not much help if you don't support your statement with some explanation.Larder
@Larder finally is pointless because RAII is a better way of doing it in almost every case and scope_guard takes care of the remaining 0.1%.Rothstein
@bames53. Thanks for the tip for RAII. It is certainly a good practice. A bit of a pain, if the class you're using does not follow it, so finally could be still useful. E.g.: Transaction * t = manager->BegintTransaction(); try { t->Write("Foo");} catch (...) { LogError(); throw; } finally { manager->CloseTransaction(t); }` This way, you don't need to close the transaction in both the try block and in the catch block as well, just before re-throwing the exception.Larder
@Larder In those cases where a type hasn't been designed to automatically cleanup and you're having to manually retrofit it with cleanup, you can use something like scope_guard instead. Here's how your example might look. IMO this is still better than finally. This isn't standard yet but here's the proposal, and you can use existing 3rd-party libs.Rothstein
@bames53, cheers. That's pretty interesting. Thanks again.Larder
P
108

On Windows, exceptions are supported at the operating system level. Called Structured Exception Handling (SEH), they are the rough equivalent to Unix signals. Compilers that generate code for Windows typically take advantage of this, they use the SEH infrastructure to implement C++ exceptions.

In keeping with the C++ standard, the throw and catch keywords only ever throw and catch C++ exceptions. The corresponding SEH exception code for the MSVC compiler is 0xe06d7363. The last 3 bytes are the ASCII code for "msc".

Unifying it with the operating system support also means that C++ destructors will be called during stack unwinding for an SEH exception. The code that does the unwinding is inside Windows and treats the SEH raised by a throw the exact same way as any SEH. However, the Microsoft compiler has an optimization that tries to avoid generating the code required that ensures that destructors are called in all cases. If it can prove that there's no throw statement inside the scope block that controls the object's lifetime then it skips the registration code. This is not compatible with asynchronous SEH exceptions, you should use the /EHa compile option to suppress this optimization if you intend to catch SEH exceptions.

There are a lot of SEH exception types. The ones that can be generated by the operating system are listed in the ntstatus.h SDK header file. In addition, you might interop with code that uses SEH to implement their own exception handling, they will use their own exception code. Like .NET, managed exceptions use the 0xe0434f4d ("com") exception code.

To catch SEH exceptions in a C++ program, you must use the non-standard __try keyword. The __except keyword is analogous to the C++ catch keyword. It has more capabilities, you specify an exception filter expression that determines whether or not an active exception should be caught. Anything is possible, but you typically only look at the passed exception information to see if you're interested in handling it. The __finally keyword lets you write code that runs after the exception is handled. No equivalent for that in C++ but not uncommon in other languages.

All of this is fairly poorly documented as pointed out in the comments. The proof is in the pudding. Here's an example program that you can play with. It demonstrates how SEH exceptions still allows for C++ destructors to be called, provided you compile with /EHa and how C++ exceptions are implemented on top of SEH. MSVC compiler required, run with Ctrl+F5 to avoid the debugger being helpful:

#include "stdafx.h"
#include <windows.h>
#include <iostream>

// NOTE: the value of the C/C++, Code Generation, Enable C++ Exceptions setting in important
// Try it both with /EHsc (the default) and /EHa to see the difference

class Example {  
public:
    ~Example() { std::cout << "destructed" << std::endl; }
};

int filterException(int code, PEXCEPTION_POINTERS ex) {
    std::cout << "Filtering " << std::hex << code << std::endl;
    return EXCEPTION_EXECUTE_HANDLER;
}

void testProcessorFault() {
    Example e;
    int* p = 0;
    *p = 42;
}

void testCppException() {
    Example e;
    throw 42;
}

int main()
{
    __try {
        testProcessorFault();
    }
    __except(filterException(GetExceptionCode(), GetExceptionInformation())) {
        std::cout << "caught" << std::endl;
    }
    __try {
        testCppException();
    }
    __except(filterException(GetExceptionCode(), GetExceptionInformation())) {
        std::cout << "caught" << std::endl;
    }
    return 0;
}

Output:

Filtering c0000005
destructed
caught
Filtering e06d7363
destructed
caught
Pulvinate answered 13/8, 2011 at 10:14 Comment(10)
An link to the documentation which explicitly states the reasoning in #3 para of the answer?Strumpet
None that I know of, the behavior of __try is only documented for the C compiler. There's a wee bit in the docs for /EHa but it is insufficient. There wouldn't be any SO if the documentation was consistently stellar :)Pulvinate
So the comments in the above answer are just an perception and not a documented fact.Strumpet
Hmm, very metaphysical, aren't we all just butterflies perceiving to be human? This butterfly thinks he's been writing machine control software in C++ on Windows for the past 15 years. Letting an SEH crash the machine isn't very popular with our customers. Don't worry too much about the downvote, your answer is very popular and you'll probably get a badge for it. Another kind of perception.Pulvinate
Do not misunderstand my comment, I have very high regards for you & the excellent answers have seen coming from you eversince I started participating here. As for the downvote, I don't sweat over it, But If I or for that matter anyone else doesn't learn anything from a downvote then it did no good to anyone. I am just seeking an documentary evidence or an logical explanation for an conflicting viewpoint you provided, since I have to believe it myself to correct a incorrect understanding that I might have. In any case/condition I didn't mean to offend so please don't take any.Strumpet
Non taken. I posted a sample program that demonstrates what I documented.Pulvinate
My +1, Thanks that convinces me and I updated my answer to reflect the correct behavior & point to your answer, I am not deleting it as it does provides some good links for reference but I will mark it as community wiki to not collect any rep from it.Strumpet
Destructor calls could be seen as the equivalent to __finally in a C++ program. Having destructors might even be the reason, why there is no dedicated keyword for always run regardless of exceptions thrown code.Fraktur
I'm late to this party but this post was the final push that caused a dawning of understanding with regard to our handling SEH exceptions in a way that we can both protect our customers from crashes AND get back some useful information about where the failure was. Thank you, Hans for cutting through the jungle and allowing the light in.Crowl
Can someone tell me where "GetExceptionCode()" is defined or at least declared?Cease
S
29

__try / __except is for catching SEH (windows generated errors) not for catching general exceptions.

try / catch is what the C++ standard specifies for handling general C++ exceptions.

For the standard C++ code you write you should always use try/ catch and not __try / __except

Also, finally is not C++ Standard specified construct, It works for you because it is a Microsoft compiler extension.

Strumpet answered 13/8, 2011 at 9:6 Comment(1)
try/catch will NOT handle SEH. When /EHa (Yes With SEH Exceptions) is use try/catch will handle both: C++ and SEH exceptions. The downside is that catch(...) will handle SEH, but you cannot know exception code. Destructor will be called when EHa is used. Both try and __try cannot be mixed in same function. Therefore it is best to have two functions (one calling another): One handling try and another handling __try (Without /EHa)Trexler
P
3

__try/__except is Microsoft specific If you want your code to be compilable with other compilers (for examplec g++) (or) in another OS avoid using them, and stick with the standard try/catch statements

Pep answered 13/8, 2011 at 9:8 Comment(1)
This is correct, although then you will lose the ability to catch Windows' SEH Exceptions. So that's when you need that non-standard handling.Asbestos

© 2022 - 2024 — McMap. All rights reserved.