Thursday, September 3, 2009

Windows SEH in C++

A windows app was mysteriously dying, roughly once a day; no clues in any of the various log files.
So I enabled Windows SEH:
_set_se_translator(win32_exception_handler);

(See MSDN article about _set_se_translator)

And here is my implementation of that function:

void win32_exception_handler(unsigned int code, EXCEPTION_POINTERS *ep){
throw new win32_exception(code,ep);
}

And the class (cut down to basics) is:

class win32_exception{
std::string info;
public:
win32_exception(unsigned int code,EXCEPTION_POINTERS *ep){
//Describe it in info
}
const char *what()const{return info.c_str();}
};


Then, I have this code (which was already here before I enabled SEH):

try{
e=main_loop(stuff);
catch(std::exception &e){
ErrorLog()<<"Got a std::exception thrown by main_loop:"<<e.what()<<" (will treat as fatal)\n";
return -9;
}
catch(win32_exception &e){
ErrorLog()<<"Caught win32_exception thrown by main_loop:"<<e.what()<<" (will treat as fatal)\n";
return -9;
}
catch(...){
ErrorLog()<<"Got an unexpected exception thrown by main_loop (will treat as fatal)\n";
return -9;
}


Now, what I don't get is today the program died with this message:
Got an unexpected exception thrown by main_loop (will treat as fatal)

I'm staring and staring at the code and don't get why the win32_exception code didn't catch and instead some other exception type got caught. Enabling SEH was the only change, all such exceptions should go through my function, and that only throws win32_exception instances.

The other mystery to me is what is the type is of this "unexpected" exception.

(I'm hoping to come back and add to this blog entry when I work out what is going on!)

9 comments:

keith.s.wilkinson said...

I'm not a Windows developer, so I don't know *if the following ideas are off track, *whether you're aware of the following resources, and whether they will be of help. Maybe you are presuming that the program died because it detected a fatal internal error in the program, but it could have died because Windows killed it for some reason. SysInternals and Windows Internals may be useful resources (there are books by those names on Amazon, I think).

keith.s.wilkinson said...

ISBN 0735625301 and 0735626618.

darren said...

Hi Keith,
If I can manage to catch the exception it will tell me useful things like EXCEPTION_ACCESS_VIOLATION, EXCEPTION_FLT_DIVIDE_BY_ZERO, EXCEPTION_STACK_OVERFLOW.

I.e. I'm trying to find out what the problem; then I'll start looking into fixes (which may involve telling someone else their code has a problem ;-).

darren said...

So far the best lead I've found is that if a DLL was not compiled with /EHa then my application using that DLL may have trouble catching it.
And, separately, I found a reference saying unhandled SEHs can get caught by a catch(...) block.

So, my plan is now to surround smaller blocks of code with try/catch blocks to try and narrow down where the SEH is arising.

keith.s.wilkinson said...

My 4th edn. Windows Internals book has about seven pages on Exception dispatching; it also says that SEH is explained in Windows API Ref. Docs. of the Platform SDK or in a book by Richter (who has a much later book Windows via C/C++ ISBN: 0735624240 which seems to cover the same stuff). You have probably already looked at MSDN

darren said...

A curious breakthrough: my win32_exception_handler() *is* being called, and I can log it from there. But when I throw a win32_exception it doesn't get caught as that, but in the "..." block instead.

My wild guess is that my win32_exception_handler applies to the whole application, but the error comes out of some deep DLL which isn't able to throw a C++ exception properly.

Anyway, if you're having a similar problem, and not planning to recover, try logging from win32_exception_handler() instead of, or as well as, throwing a C++ exception.

P.S. The problem code is 3221225477, which is C0000005 in hex, which is EXCEPTION_ACCESS_VIOLATION.

keith.s.wilkinson said...

SysInternals web site and forum may be useful resources.

Anonymous said...

My guess : you mispelled the exception type . Should have writen :

catch(win32_exception *e){
ErrorLog()<<"Caught win32_exception thrown by main_loop:"<what()<<" (will treat as fatal)\n";
return -9;
}

darren said...

Thanks Anon, well-spotted. I dragged More Effective C++ off the shelf, and read items 12 and 13, and I think you may be right. In fact catching by reference is correct, throwing by pointer is what is dubious. So I think my SEH handler should have been written like this (i.e. without the new):

void win32_exception_handler(unsigned int code, EXCEPTION_POINTERS *ep){
throw win32_exception(code,ep);
}