Solved

Application wide exception and error handling

Posted on 1998-11-11
31
264 Views
Last Modified: 2012-06-27
When a fatal error or exception occur in my Win95/NT application, I would like to be able to log in a file some information like the line number where the problem occurs, etc...

Is there any way to do this without having to put try{}and catch{} everywhere in the program?

I am looking for an application wide exception handler.
0
Comment
Question by:sjut
  • 14
  • 10
  • 5
  • +1
31 Comments
 
LVL 22

Expert Comment

by:nietod
Comment Utility
Why not just place it in main?  That is, place the guts of main in a try block and catch all exceptions that occur in main or anywhere else?
0
 
LVL 86

Accepted Solution

by:
jkr earned 50 total points
Comment Utility
Yep - you'll have to use 'SetUnhandledExceptionFilter()' to provide your own top-level exception handler that supersedes 'UnhandledExceptionFilter()' (usually the creator of these infamous message boxes that state 'The instruction at <blah>').

Feel free to ask if you need a sample.
0
 
LVL 22

Expert Comment

by:nietod
Comment Utility
i.e.

WinMain()
{
   try
   {
       // old main code here.
   }
   catch (...)
   {
   }

}
0
 
LVL 22

Expert Comment

by:nietod
Comment Utility
SetUnhandledExceptionFilter() is for use with structured exceptions (__try) not for ordinary C++ exceptions.
0
 
LVL 86

Expert Comment

by:jkr
Comment Utility
nietod - a C++ try/catch block won't be able to catch e.g. access violations - this can only be done using a SEH handler. And besides, there is the '_set_se_translator()' API that provides a mechanism to conver SEH exceptions into C++ exceptions, but the other direction does not exist...
0
 
LVL 22

Expert Comment

by:nietod
Comment Utility
>>_set_se_translator()' API that provides a mechanism to
>> conver SEH  exceptions into C++ exceptions, but the
>> other direction does not exist
Right, that is why it is best to use C++ exceptions--you can catch all exceptions.  (Also they allow for object destruction, which SEH doesn't.)

Besides, sjut's answer indicates he is used C++ exceptions, not SEH, so he won't find any effect from setting the filter.
0
 
LVL 86

Expert Comment

by:jkr
Comment Utility
>> When a fatal error or exception
That's why i thought of Win32 exceptions... let's see what sjut thinks about it ;-)
0
 
LVL 5

Expert Comment

by:yonat
Comment Utility
jkr wrote:
>>>>>
a C++ try/catch block won't be able to catch e.g. access violations - this can only be done using a SEH handler.
<<<<<

Surprisingly, a catch (...) statement catches SEH as well as C++ exceptions. Of course, if you want to know what kind of exception you caught, you'll need to use _set_se_translator().
0
 
LVL 5

Expert Comment

by:yonat
Comment Utility
jkr wrote:
>>>>>
a C++ try/catch block won't be able to catch e.g. access violations - this can only be done using a SEH handler.
<<<<<

Surprisingly, a catch (...) statement catches SEH as well as C++ exceptions. Of course, if you want to know what kind of exception you caught, you'll need to use _set_se_translator().
0
 

Author Comment

by:sjut
Comment Utility
I am developping an MFC based application using Visual C++.  In Microsoft documentation, it is strongly recommended to use C++ exception handling, rather than structured exception handling.

In the other hand, it seem that there is no "SetUnhandledExceptionFilter()" equivalent in C++ exception.

I'd like to know what I'm losing if I decide to use SEH.

Again, just to be clear in what I need.  If possible, I don't want to put try{} block everywhere in the program.  I don't want to throw() any special exception.  I just want to trap exception that cause Win95/NT displaying a message box and terminate my application in order to log information before crashing.

jkr - if you still thinking that the SEH solution is what I need, I'd like to see a little example.
0
 
LVL 22

Expert Comment

by:nietod
Comment Utility
>> In the other hand, it seem that there is no "SetUnhandledExceptionFilter()"
>> equivalent in C++ exception.
There is a good reason for that.  The C++ standard committee debated long an hard between two approaches to exceptions.  Many programmers believed (as I did) that it is desirable to have a "handler procedure" to call when an error occurs to handle the error an potentially correct it and then restart the code that caused the error.  When they did hard research on the matter they found that in practice that few programs employed that sort of design and the few that did were very unsuccessful at handling the errors anyways.  On the other hand, programs that pass the error information back down the call stack to some specific error handler were prevelant and far more successful.  The reason is that it is often only the caller that knows the context of the error and the correct steps to take to handle it.  Furthermore it was found that all solutions that employed the "restart" approach could be easily rewriten to provide the same functionality with the pass back to caller approach.  t
0
 
LVL 22

Expert Comment

by:nietod
Comment Utility
>> I'd like to know what I'm losing if I decide to use SEH.
A lot and gaining nothing.  SEH handles cleaning up the stack, That is it frees local varaibles.  But it doesn not destroy objects.  Thus SEH really can make a mess in C++.  Because of that there is an option in VC that makes SEH destroy objects.  How does it work?  it actually just uses the C++ exceptions instead of the SEH.

>> If possible, I don't want to put try{} block everywhere in the program.  I don't
>> want to throw() any special exception.  I just want to trap exception that cause
>> Win95/NT displaying a message box and terminate my application in order
>> to log information before crashing.
You don't need to.  You can just put one in main(), that will trap all exceptions except for ones caused by constructors or destructors of global objects.  (jkr's solution will trap those as well.)  

If you really plan to use exceptions only in this way, i.e, you only care about windows exceptions and you only want to log some error information and die, then jkr's solution is pretty simple and meets your needs.  If you ever want to handle your own exceptions, and if you ever want the ability to recover from errors, then you really should consider C++ exceptions.  The only extra work will be you will need to convert the SEH exceptions to C++ exceptions.  (not a big deal.)
0
 

Author Comment

by:sjut
Comment Utility
>>You don't need to.  You can just put one in main(), that will >>trap all exceptions except for ones caused by constructors or >>destructors of global objects.  

1. Are you sure that Exception caused by Message handler will also be trapped this way?

2. I don't have a main(). Do you think that if I put one in InitInstance()(which is call by the framework's implementation of WinMain), it will do the job?
0
 
LVL 22

Expert Comment

by:nietod
Comment Utility
>>1. Are you sure that Exception caused by Message handler will also be trapped this way?
It will catch anything that occurs in a function called (directly or indirectly) by main.  (or WinMain()).  I do exactly what you are proposing this way.  I try to catch and recover from exceptions.  But those that I can't recover from a caught in WinMain long enough to display diagnostics.

>> 2. I don't have a main(). Do you think that if I put one in InitInstance()
Then I beleive you want it in "run", not "InitInstance"  InitInstance is for initialization.  There is a different function, I beleive ti is "run" that contains the message loop and the bulk of your program.  (You can probably find it in the docs.  If not set a breakpoint in your program when it is really running (on a message handler say).  Then look back in the call stack for the lowest procedure you can override. )

0
 
LVL 86

Expert Comment

by:jkr
Comment Utility
JUST A MOMENT!!!!
I'd _NEVER_ advise anyone to use SEH exceptions _instead_ of C++ exceptions - they have different design goals. SEH exceptions result from an application interacting with the underlying subsystem, whereas C++ exceptions are thrown due to errors in the program's control flow - so, the longer i think about it, i'd recommend a combination ob both suggestions (as translating 0xc0000005 to e.g. an CAccessViolationException IMHO makes _no_ sense - how to recover from that in a C++ way?)
0
How your wiki can always stay up-to-date

Quip doubles as a “living” wiki and a project management tool that evolves with your organization. As you finish projects in Quip, the work remains, easily accessible to all team members, new and old.
- Increase transparency
- Onboard new hires faster
- Access from mobile/offline

 
LVL 22

Expert Comment

by:nietod
Comment Utility
>> I'd _NEVER_ advise anyone to use SEH exceptions _instead_ of C++ exceptions
I'm not suggesting that you did.  But I am suggesting the reverse.  Convert SEH to C++ exceptions and use C++ exceptions instead.  Unless the use of exceptions will really be as limited as the question suggests.  In that case, you suggestion is easier to implement and will actually provide better coverage.  But other that that special case, I would opt for C++ exceptions.

>> as translating 0xc0000005 to e.g. an CAccessViolationException IMHO
>> makes _no_ sense - how to recover from that in a C++ way
It depends on the program, but there certainly are cases where it is recoverable.
In my case my program is made of numerious "loadable" modules, that perform various tasks (prints, posts, file maintenance etc...)  If an exception is thrown when these load (even an access violation), it is caught back in the main module and the program continues to run fine.  
0
 
LVL 86

Expert Comment

by:jkr
Comment Utility
nietod - of course you're right. But an access violation usually indicates that the data you're working on is messed up in some way (at least, it tells you that the model you're using is not working correctly) - and therefore, i wouldn't allow an application to continue running unless i definitely know that this could have happened and that recovery from here is safe - and if i knew this before, i could either have avoided the exception or handled it at the place where it occurred.
And i think the principle 'Better NO data than false (or corrupted) data' is not too bad ...
0
 
LVL 22

Expert Comment

by:nietod
Comment Utility
>> But an access violation usually indicates that the data you're
>> working on is messed up in some way
I've spent 10 years trying to write a bug free application.  I haven't come close.  Instead I'm trying to insulate the user from damage from bugs.  If a error occurs in one module, then hopefully the others can continue to work, at least long enough for users to finish posts and safely save their work.  
0
 
LVL 86

Expert Comment

by:jkr
Comment Utility
>>safely save their work.  
That's a point! At least, if the data the application holds is still the same the user entered... (i am a pessimist ;-)

Ooops - the ultimate bug-free application:

void main ( void)
{
/* functionality is the reason for bugs */
}


0
 
LVL 22

Expert Comment

by:nietod
Comment Utility
It could still crash -- I'm a pessimist too.
0
 
LVL 86

Expert Comment

by:jkr
Comment Utility
nietod - not when linking with '/entry:main', omitting all MS startup code ;-)
(BTW: The result is a 1k .exe, disassemby:
  00401000: 55                 push        ebp
  00401001: 8B EC              mov         ebp,esp
  00401003: 5D                 pop         ebp
  00401004: C3                 ret
it's hard to crash this <lol>)
0
 

Author Comment

by:sjut
Comment Utility
I tried the try{} and catch{} in the Run() and it works fine.  How can I get details informations about the exception

int MyApp::Run()
{
  try
  {
    return CWinApp::Run();
  }
  catch(...)
  {
  // Get information about the exception
  }
}

Thank you.
0
 
LVL 86

Expert Comment

by:jkr
Comment Utility
Ooops, i owe you the SEH example (BTW: from an existing application):
/*
 *
 * Function:     LONG WINAPI      __XceptFilter      (      EXCEPTION_POINTERS*      pExp)
 *
 * Description:
 *
 *      filter function for structured exception handling. This function is
 *      necessary due to a bug in ODBCJT32.DLL (Microsoft ODBC Desktop Driver
 *      Pack 3.0, Vers. ID 3.40.2829) which causes an access violation exception
 *      in 'RtlpWaitForCriticalSection()' (mem 0x77f6cb4e, call sequence
 *      BaseThreadStart()->MSJT3032@0x0401ffb2()->RtlEnterCriticalSection()->
 *      RtlpWaitForCriticalSection()) and/or at MSJT3032@0x040200a5 when
 *      'CDatabase::Close()' is called more than once within an application.
 *      This exception filter detects these two particular cases and suppresses
 *      the exceptions by signalling 'EXCEPTION_CONTINUE_EXECUTION' to the top-level
 *      exception handler of WIN32 (typically 'UnhandledExceptionFilter()'.
 *      In all other cases, this filter will return 'EXCEPTION_EXECUTE_HANDLER',
 *      so the exceptions will be processed as usual.
 *
 * Parameters:
 *
 *      pExp                        pointer to structure describing the exception
 *
 * Return value:
 *
 *      EXCEPTION_CONTINUE_EXECUTION when one of the cases descibed above is
 *      detected, EXCEPTION_EXECUTE_HANDLER otherwise
 *
 */
LONG WINAPI      __XceptFilter      (      EXCEPTION_POINTERS*      pExp)
{
      LONG      lnRC      =      EXCEPTION_EXECUTE_HANDLER;
      if      (      EXCEPTION_ACCESS_VIOLATION      ==      pExp->ExceptionRecord->ExceptionCode)
            {
                  if      (            0x77f6cb4e      ==      ( DWORD) pExp->ExceptionRecord->ExceptionAddress
                              ||      0x040200a5      ==      ( DWORD) pExp->ExceptionRecord->ExceptionAddress
                        )
                        {
                              return( EXCEPTION_CONTINUE_EXECUTION);
                        }
            }

      return( lnRC);
}

(set with       SetUnhandledExceptionFilter(__XceptFilter);)

You can retrieve information about the exception by accessing its member function 'GetErrorMessage()' (assuming that it is derived from CException
0
 
LVL 22

Expert Comment

by:nietod
Comment Utility
The SEH exceptions are integers.  I believe you can catch an integer exception value and compare it with the exception constants defined in window.h.

If that does not work, or even if it does, a better way might be to convert the SEH exceptions to other types _set_se_translator().  Take a look at _set_se_translator, thn ask if you have questions.

Note that you can find out about the nature of the exception this way.  You cannot find out the location (line number) of it.  There is no way to do that.  If you are debugging, however, you can have the debugger invoked when an exception is thrown.  That will get you close to the site of the error.  (I do this by using an exception class hierarchy and by placint an int 3  (debugger interrupt) in the base class's constructor.)
0
 

Author Comment

by:sjut
Comment Utility
I made my own SE_Exception object. I made my translator function and set it with _set_se_translator( trans_func ).

class SE_Exception
{
private:
    unsigned int nSE;

public:
    SE_Exception() {}
            SE_Exception( unsigned int n ) : nSE( n ) {}
    SE_Exception( SE_Exception& ) {}
    ~SE_Exception() {}
    unsigned int getSeNumber() { return nSE; }
};

void trans_func( unsigned int u, _EXCEPTION_POINTERS* pExp )
{
      throw SE_Exception( u );
}

If I generated a divide by zero in my program.  I get the good code in the "trans_func" but I don't have the good code and the good SE_exception object in the catch section of my Run() function.

catch( SE_exception e )
{
  e.GetSeNumber(); // Not the same as in trans_func()
}

I keep searching but I can't find the problem.
0
 
LVL 86

Expert Comment

by:jkr
Comment Utility
Did you try
SE_Exception( unsigned int n ) { nSE = n;};
as the constructor?

0
 
LVL 22

Expert Comment

by:nietod
Comment Utility
>>Did you try
>>     SE_Exception( unsigned int n ) { nSE = n;};
>>     as the constructor?
he initializes it with "nSE( n )" so that's not the problem.

I see it.  The problem is the copy constructor..  If you want to create a copy constructor, then remember to copy nSE in it.  Otherwise,  go with the default copy constructor.
0
 
LVL 22

Expert Comment

by:nietod
Comment Utility
In case you are wondering what the copy constructor has to do with it, the object you throw is copied around a lot (behind the scenes) by the compiler.  First you create a local SE_Exception which you throw.  Since that object is local, and you are throwing, it means it is about to be destroyed (along with all the other locals). So it won't be around to catch later.  The C++ compiler creates a copy of this object usng the copy constructor before it destroys the local one you threw.  (Where this copy is stored is implimentation defined, but the fact that the copy is made is required by the standard)  It also uses the copy constructor to copy the object when you catch it.  So there are at least two times that the copy constructor will be invoked for this.
0
 
LVL 86

Expert Comment

by:jkr
Comment Utility
Hmmm - somtimes i must be blind... ;-)
Thanx nietod...
0
 
LVL 22

Expert Comment

by:nietod
Comment Utility
Also in the copy constructor, remember to declare the source as constant, like

   SE_Exception(const SE_Exception& Src) : nSE(Src.nSE) {}

If you don't,  there may be times that you will get compiler errors that aren't really necessary.
0
 

Author Comment

by:sjut
Comment Utility
nietod - your suggestions works fine!!

Thanx a lot to both of you guys!

Instead of using "UINT u", I am using the "_EXCEPTION_POINTERS* pExp" in order to have more detailled information in my catch block.

SE_Exception( _EXCEPTION_POINTERS* pExp ) { m_pExp = pExp; }

SE_Exception(const SE_Exception& Src) : nSE(Src.nSE)
   { m_pExp = Src.m_pExp;}

void trans_func( unsigned int u, _EXCEPTION_POINTERS* pExp )
{
  throw SE_Exception( pExp );
}

Now I'm gone tried to clean the stuff properly.


0

Featured Post

Highfive + Dolby Voice = No More Audio Complaints!

Poor audio quality is one of the top reasons people don’t use video conferencing. Get the crispest, clearest audio powered by Dolby Voice in every meeting. Highfive and Dolby Voice deliver the best video conferencing and audio experience for every meeting and every room.

Join & Write a Comment

IntroductionThis article is the second in a three part article series on the Visual Studio 2008 Debugger.  It provides tips in setting and using breakpoints. If not familiar with this debugger, you can find a basic introduction in the EE article loc…
This article shows you how to optimize memory allocations in C++ using placement new. Applicable especially to usecases dealing with creation of large number of objects. A brief on problem: Lets take example problem for simplicity: - I have a G…
The goal of the video will be to teach the user the difference and consequence of passing data by value vs passing data by reference in C++. An example of passing data by value as well as an example of passing data by reference will be be given. Bot…
The viewer will be introduced to the technique of using vectors in C++. The video will cover how to define a vector, store values in the vector and retrieve data from the values stored in the vector.

728 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question

Need Help in Real-Time?

Connect with top rated Experts

13 Experts available now in Live!

Get 1:1 Help Now