Prev: Deleting all panes when Miniframe is Closing
Next: How to get crash dump when a unhandled CException is thrown by a MFC app
From: Peter Schneider on 5 Jan 2010 08:36 Hello, in AfxCallWndProc MFC catch exceptions derived from CException (in particular CInvalidArgException which is thrown by some MFC functions such as CArray::GetAt even though this is undocumented). A message box will be shown but then the application will continue to run in a potentially corrupt state. This makes it hard to find the root cause of an unhandled exception, particularly when a problem is difficult to reproduce or occurs only on customer machines, for example. I'd prefer a normal crash and a good old crash dump showing the call stack at the time the exception was thrown. Is there a way to achieve this? Thanks, Peter
From: Goran on 6 Jan 2010 02:56 On Jan 5, 2:36 pm, Peter Schneider <peter.m.schnei...(a)gmx.de> wrote: > Hello, > > in AfxCallWndProc MFC catch exceptions derived from CException (in > particular CInvalidArgException which is thrown by some MFC functions > such as CArray::GetAt even though this is undocumented). A message box > will be shown but then the application will continue to run in a > potentially corrupt state. > > This makes it hard to find the root cause of an unhandled exception, > particularly when a problem is difficult to reproduce or occurs only > on customer machines, for example. Note however that exception is not unhandled: it is, there's a big try/ catch in MFC window proc ;-). > > I'd prefer a normal crash and a good old crash dump showing the call > stack at the time the exception was thrown. Is there a way to achieve > this? I don't know of any, not without massively changing your code to actually crash when stepping out of bounds (that is, replacing all calls to GetAt and operator[] with your own function). What I have to offer thought is a reflection on the past: in the past, in release builds, MFC used to just continue and let you bork your memory. Recently (since VS 2005? perhaps), it started throwing. In all honesty, I prefer old behavior, that could produce a crash more easily. Goran.
From: Goran on 6 Jan 2010 03:07 On Jan 6, 2:45 am, Joseph M. Newcomer <newco...(a)flounder.com> wrote: > I can only answer part of your question. > > In VS, there is a way to turn on handling exceptions which intercepts them at the point > where they occur; to enable this, go to Debug >Exceptions. > > You might want to consider putting in some exception handlers of your own.. I'd looking at > Doug Harrison's article on exceptions at > > http://members.cox.net/doug_web/eh.htm > > Ultimately, when using exceptions, I found that the only good way to defend is to have > LOTS of try/catch blocks scattered widely. One system I worked in too a year of doing > this before I got every situation covered (and remember: if you write a try, you HAVE to > have a recovery strategy for the catch, or you haven't solved the problem.. That's what > takes the time. In many cases, you have to clean up with something like > catch(CException * e) > { > ...do local cleanup... > throw; > } No, good code should not have __any__ of these. Local cleanup in C++ is best handled through judicious application of RAII and it's more powerful cousin, scope guard. ( I don't know why I started pressing for scope guard here these days ;-) ). (BTW, what you wrote there is the equivalent of a "finally" in... ahem... lesser languages. C++ really has no need for finally.) Application of RAII and scope guard also invalidates the notion that good code needs lots of try/catch blocks. It doesn't, because most of the time it will only do local cleanup and not re-throw a different exception type (but if that's seen as needed, then try/catch it is). Goran.
From: Goran on 7 Jan 2010 04:29 On Jan 6, 6:16 pm, Joseph M. Newcomer <newco...(a)flounder.com> wrote: > Thing * something(...) > { > Thing * result = new Thing; > ... do something > return result; > } > > Now what happens if an exception is thrown? We have a dangling pointer.. So you have to > do > > Thing * something(...) > { > Thing * result = new Thing; > try { > ... do something > } > catch(CException * e) > { > delete result; > throw; > } Nah, in this trivial example, appropriate RAII class exist already. Thing* something(...) { std::auto_ptr<Thing> PResult(new Thing); ... return P.release(); } > > Consider also the infamous "locking" example: > > void Whatever(...) > { > Lock.Acquire(); // where Lock::~Lock releases the lock > ... do something > } (I'll presume a usual case: Lock is local to a function, there's additional synchronization object that outlives the function, and Lock actually operates on that one). > > Note how cool! No need to worry about an internal 'return' failing to release the lock! > An exception releases the lock! Now consider the scenario > > void Whatever(...) > { > Lock.Acquire(); > .. mess around with internal data structure > FixUpInternalDataStructure(); // throws an exception > } > > Note that this does: it releases the lock, saying the structure invariant is maintained, > when in fact the FixUpInternalDataStructure has failed to put the structure back into its > correct state and thrown an exception instead. So the invariant is not maintained, and > the naive assumption that the code is "correct" because a nice automatic cleanup has > happend is violated. You mixed two concerns: locking and data integrity. You want that "lock is free" also means "data is consistent". Or, if you will, you want that FixUpUnternalDataStructure has only "basic exception safety" (http://en.wikipedia.org/wiki/ Exception_handling#Exception_safety) __and__ that locking achieve that. That's not possible without additional work. So if that's what you really want, you might try a scope guard and a Lock object that outlives Whatever, e.g.: void UnlockOnSuccess(Lock& l) { if (!unhandled_exception()) l.Unlock(); } void Whatever(Lock& l) { l.Lock(); ON_BLOCK_EXIT(&UnlockOnSuccess, l); FixUpUnternalDataStructure(); } But, IMO correct approach is to __not__ mix the two concerns. That is, FixUp... should better satisfy "commit or rollback semantics" (I am again speaking with that Wikipedia article in mind). (We had this discussion before; as you might remember and see here, I believe that locking and data consistency are two different concerns and are best handled differently). > One of the interesting problems we ran into was the notion of scope of exceptions. If I > have a type T, and there is an exception, T::x, which is a protected type, we cannot throw > this exception, and in fact the following code is erroneous > > class T { > protected: > class x : public CException() {}; > public: > void method() { > submethod(); > } > protected: > void submethod() { > if(...) throw new x; > } > > }; > > It is required that method must do a catch(x * e) because it cannot allow the anonymous > (outside the class) exception T::x to escape. It can simply return, or it can throw an > exception whose namespace is presumably visible in the caller. So it must throw a > different exception type. In our languages, exception names always had global scope, but > we learned to treat them as local scope names to maintain modularity. That's only partially true. In case of MFC exceptions, it's largely false, because they are supposed to be used polymorphically (you see how GetErrorMessage and ReportError are virtual? what() of standard library, too). So for the majority of cases, exact exception type should be irrelevant, hence type visibility won't matter. That is of course not true for, IME(xperience) rare, cases where code indeed does want to catch a specific exception type and do something specific. Goran.
From: Peter Schneider on 7 Jan 2010 05:20
I completely agree that correct exception handling is not trivial and as we can see from the interesting discussion in this thread, it's often controversial, too. But I think I need to clarify my intial question a bit because the discussion is not directly related to it anymore. I am aware that the CExceptions are handled by a try/ catch in MFC window proc if the application code does not handle them. But in my opinion and seemingly also in Goran's, this handler is doing more harm than good. If the app didn't handle it and it gets caught here, it's pretty likely that the app is in an undefined state now. I am primarily concerned with exceptions which represent what Java calls run-time exceptions: exceptions which are thrown when something occurred which is typically cuased by a programming error. CInvalidArgException which can be thrown by CArray::GetAt is such an example. In my opinion, it doesn't make sense to write handlers for this everywhere (for example, each time GetAt is called or in each top- level message handler). If GetAt is called with an invalid argument this means that there is a programming error of some kind and no reasonable way to deal with this in a general fashion. But because MFC "swallows" these exceptions, it get's unnecessarily hard to find such problems if they do occur. Particularly if the problem is difficult to reproduce and only occurs on end-user machines. There won't be a windows error report (WER). Only complaints about a message box indicating something like "an invalid arg was detected". So my question should have been: Is there a way to work around this MFC exception handler and to get normal crashes instead without having to change lots of code? But I am afraid Goran already answered it here: > I don't know of any, not without massively changing your code to > actually crash when stepping out of bounds (that is, replacing all > calls to GetAt and operator[] with your own function). Peter |