Click here to Skip to main content
15,860,972 members
Articles / Desktop Programming / MFC
Article

Several classes for exception handling

Rate me:
Please Sign up or sign in to vote.
4.92/5 (27 votes)
19 Jan 20024 min read 285.6K   4.8K   125   24
C++ wrappers for stack trace, unhandled exception and win32 structured exceptions

Disclaimer

I am not MS certified developer and my opinion and code may have some bugs.
Use it carefully at your own risk.

Introduction

After familiarization with .NET Framework and System.Exception Object everyone can exclaim "Wow, it's really cool to be able to trace a call stack via a single call of Exception.StackTrace". But I write most of my working projects in VC++ 6.0. and I want to have a similar feature there too. Fortunately I can easily implement an "exception with stack trace" in C++. I recommend strongly to read the book "Debugging application" by John Robbins to get a better understanding about the subject. You can find here some ideas\code from John Robbins's book throughout my code. Also you can read the source code of my demo program and at last read this article too.

Main

Every programmer sometimes errs and tries to work with a wrong pointer. And it's real headache to detect the line of code with the bug. Look at this example of code with just such an annoying "access violation" error

try
{        
    char * p = 0;        
    printf("%u", strlen(p)); // oops
}
catch(se_translator::access_violation& ex)
{
    sym_engine::stack_trace(os, ex.info()->ContextRecord);        
}

But now have a look at this screenshot from VC IDE output window.

First-chance exception in Exception.exe: 0xC0000005: Access Violation.
First-chance exception in Exception.exe (KERNEL32.DLL): 0xE06D7363: Microsoft C++ Exception.
intel\strlen.asm(78) : Exception.exe at strlen()
D:\Exception\Exception.cpp(185) : Exception.exe at foo()
D:\Exception\Exception.cpp(65) : Exception.exe at main()
crt0.c(206) : Exception.exe at mainCRTStartup()
 (0) : KERNEL32.dll at GlobalGetAtomNameA()

vc ide

Just double click and it point you to a bug's code directly. It's magic, isn't it ?

Ok, Let's be deep in a subject and familiarize with source code. There are two main parts of magic:

  1. sym_enigne class
  2. se_translator class

The sym_engine class incapsulates Symbol Handler and Debugging Service API. With the aid of StackWalk API we can obtain a stack trace without problem. Especially it very easy in SEH frame. Have a look on the code below.

int exception_filter(std::ostream& os, EXCEPTION_POINTERS * pex)
{
    sym_engine::stack_trace(os, pex->ContextRecord);
    return EXCEPTION_EXECUTE_HANDLER ;
}
void foo (std::ostream& os)
{        
    __try {
        // any buggy code here
    }
    __except(exception_filter(os, GetExceptionInformation())) {
    }
}

For obtaining a stack trace we need a CONTEXT structure. And we can get it via call GetExceptionInformation() in the filter expression of an SEH exception handler. Ok. But we can't get such info in C++ catch(...) block. So, then we have to call the se_translator class for help.

The se_translator class maps win32 structured exceptions into C++ typed exceptions via _set_se_translator API. The CP site already has article SEH and C++ Exceptions - catch all in one by Martin Ziacek about using _set_se_translator and I don't want to repeat it. Instead I rewrite foo() function in C++ style.

se_translator translator;        // install a new translator function
void foo(std::ostream& os)
{    
    try
    {    
        // force to install a exception frame
        WORKAROUND_VCPP_SYNCHRONOUS_EXCEPTION
        char * p = 0;
        printf("%u", strlen(p));    // oops
    }
    catch(se_translator::access_violation& ex)
    {
        sym_engine::stack_trace(os, ex.info()->ContextRecord);        
    }
}

At first, I've installed a new translator function and now I can handle structured exception like C++ exception. I've wrapped every structured exception into C++ class. The base class is se_translator::exception. And for example several derived classes : se_translator::no_memory, se_translator::access_violation, se_translator::int_divide_by_zero, etc.

Yes, now we've grasped the way to obtaining stack for Win32 structured exception. But interesting, is there any way for obtaining similar info for native C++ exception like std::exception and so on ? Hm, I can't achieve it, but can propose a workaround. We can define the new exception class derived from std::exception which will hold the class stack's info. Let's name it exception2 class.

try
{
    throw exception2("my exception");    // stores the stack info here
}
catch(exception2& ex)
{
    os << ex.what() << std::endl;
    os << ex.stack_trace() << std::endl;
}

At last the final class which I want to present it's exception_trap class. What if exception will be raised out from any exception frame ? Most likely the users will phone and terrorize your QA and you will tear hair at a loss. But if you could have a look at the crash's call stack definitely your life would become more easy. The exception_trap class wrapps SetUnhandledExceptionFilter API and can print out the crash's cause, call stack and another useful info. For this you must just define somewhere global variable.

exception_trap<> trap;
Enjoy :)

Some traps

As any programmer knows, any good idea always has a some traps. In that case it's in synchronous exception model (/EHs compiler option) and _set_se_translator API. Sometimes due to optimization reasons the compiler can avoid to generate code for try\catch block. See MSDN article for more info. Hence if access violation or another exceptions will be occured nobody can catch it and tricks with SE translator will useless here.

I know two ways for avoiding such ugly scenario.

  • use asynchronous exception handling model (/EHa compiler option)
  • manually force the compiler to generate code for try\catch block, for example via using my macro WORKAROUND_VCPP_SYNCHRONOUS_EXCEPTION.

I can't name it the best solution but it's working.

Project's settings

Make sure what you set a next options:

  • compiler option: /GX (Enable Exception Handling)

If you want to use the symbol engine in your release version too

  • compiler option: /Zi (Produces a program database (.PDB file) )
  • linker option: /DEBUG (Generate Debug Info)

and don't forget to place the .pdb file in the same folder with your .exe or .dll file (see MSDN topic SymInitialize for more info)
And again I point to the book "Debugging application" by John Robbins for more info about program database problem.

Thanks

Sven Axelsson for "Using an output stream for debugging"
Rostislav Sharafutdinov for some correction

License

This article has no explicit license attached to it but may contain usage terms in the article text or the download files themselves. If in doubt please contact the author via the discussion board below.

A list of licenses authors might use can be found here


Written By
Web Developer
Russian Federation Russian Federation
I am freelance programmer. About 3 years of experience in C++ and I would rather use ATL, STL, WTL but not MFC Smile | :) . Main backgrounds are Win32 API, COM and Networking. Now I am interested about AI (Neural Network, Fuzzy Logic and GA). Currently based in Vladivostok, Russia.

Comments and Discussions

 
QuestionWindows x64 support? Pin
Member 889647330-Apr-12 11:27
Member 889647330-Apr-12 11:27 
QuestionTSP Converting Problem Pin
lavdim8615-Nov-11 0:53
lavdim8615-Nov-11 0:53 
GeneralObtaining thread CONTEXT Pin
martin-g24-Dec-07 5:07
martin-g24-Dec-07 5:07 
Generalhombre Pin
sanjit_rath19-Nov-07 3:39
sanjit_rath19-Nov-07 3:39 
GeneralLooks great! Pin
Rafal Glowinski12-Oct-07 8:25
Rafal Glowinski12-Oct-07 8:25 
QuestionRegQueryValueEx, Access Violation error (0xC0000005) [modified] Pin
stef_880316-May-07 11:24
stef_880316-May-07 11:24 
GeneralCompile errors [modified] Pin
Jerry Jeremiah18-Oct-06 17:11
Jerry Jeremiah18-Oct-06 17:11 
GeneralRe: Compile errors Pin
Jerry Jeremiah18-Oct-06 17:51
Jerry Jeremiah18-Oct-06 17:51 
Generaldoesn't work for stack overflow exception Pin
lucky_by24-Aug-06 22:46
lucky_by24-Aug-06 22:46 
GeneralAdditional ideas Pin
Andrew Schetinin26-Apr-06 21:22
Andrew Schetinin26-Apr-06 21:22 
GeneralPure Virtual Function Call Pin
Anonymous3-May-04 22:37
Anonymous3-May-04 22:37 
Generalcall stack for MFC exceptions Pin
g_sunil_kumar@hotmail.com13-Nov-03 12:35
g_sunil_kumar@hotmail.com13-Nov-03 12:35 
Generaldebug of pointer Pin
carry_dragon24-Oct-03 2:25
susscarry_dragon24-Oct-03 2:25 
GeneralA few points Pin
User 58385215-Sep-03 21:05
User 58385215-Sep-03 21:05 
GeneralRe: A few points Pin
Vikas Gandhi4-May-04 4:44
Vikas Gandhi4-May-04 4:44 
QuestionAm I missing something here? Pin
Todd C. Wilson27-Oct-02 11:34
Todd C. Wilson27-Oct-02 11:34 
Generaldbghelp.lib Pin
smohansa20-Sep-02 12:49
smohansa20-Sep-02 12:49 
GeneralRe: dbghelp.lib Pin
Lionel Schiepers19-Nov-03 7:30
sussLionel Schiepers19-Nov-03 7:30 
GeneralHelp with VS.NET Pin
hrh_hamlet10-Aug-02 5:01
hrh_hamlet10-Aug-02 5:01 
When compiling on VS.NET the stack trace prints something like:
(0) : Exception.exe at ?()
 ...
 ...
(0) : KERNEL32.dll at ?()

Cry | :((
Any idea why, or who to solve this?

10x,
A.
GeneralRe: Help with VS.NET Pin
Member 141616914-Oct-04 18:33
Member 141616914-Oct-04 18:33 
GeneralHeavy pdb Pin
Swinefeaster30-Jan-02 0:12
Swinefeaster30-Jan-02 0:12 
GeneralRe: Heavy pdb Pin
Prof. Nimnul6-Jan-07 3:00
Prof. Nimnul6-Jan-07 3:00 
GeneralCool article ! Pin
Jim Crafton22-Jan-02 10:41
Jim Crafton22-Jan-02 10:41 
GeneralRe: Cool article ! Pin
Konstantin Boukreev22-Jan-02 15:45
Konstantin Boukreev22-Jan-02 15:45 

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Praise Praise    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.