No matter how careful of a programmer you are, there will always be times when a hardware
exception will occur in your code. Perhaps it was a third party component that was the
culprit. Perhaps, it was a fellow co-worker that broke something. Or maybe it was
Microsoft itself not playing fair with its documentation and/or implementations.
Whatever the case, it is often very useful to be able to capture a run-time exception
that was generated by the CPU. Sure, you can use a catch(...) to be your fail-safe, but
wouldn't it be great to be able to convert that exception that was generated by the hardware
into a C++ exception? I created this class in order to do that very thing. In fact,
this class was the basis for my super assert that I created, because I found that I could
cause a hardware exception any time I wanted, and by using this C++ hardware exception container,
I could access each thread's stack frame at run-time. This would eventually enable me to
perform a stack trace inside of an assert, but I will explain that more in a different
tutorial.
Anyway, I hope that this is useful to someone. I spent a while digging around in the
mire that is Microsoft's documentation before I put this together. Perhaps this will save
someone else time in the future.
Enjoy.
-BossHogg
#ifndef HARDWARE_EXCEPTION
#define HARDWARE_EXCEPTION 1
enum HWExceptionType
{
eIllegalMemoryAccess = EXCEPTION_ACCESS_VIOLATION,
eUnexpectedBreakpoint = EXCEPTION_BREAKPOINT,
eDataTypeMisalignment = EXCEPTION_DATATYPE_MISALIGNMENT,
eSingleStepInstruction = EXCEPTION_SINGLE_STEP,
eArrayBoundsExceeded = EXCEPTION_ARRAY_BOUNDS_EXCEEDED,
eDenormalFloat = EXCEPTION_FLT_DENORMAL_OPERAND,
eFloatDivideByZero = EXCEPTION_FLT_DIVIDE_BY_ZERO,
eFloatInexactResult = EXCEPTION_FLT_INEXACT_RESULT,
eFloatInvalidOperation = EXCEPTION_FLT_INVALID_OPERATION,
eFloatOverflow = EXCEPTION_FLT_OVERFLOW,
eFloatStackCorrupted = EXCEPTION_FLT_STACK_CHECK,
eFloatUnderflow = EXCEPTION_FLT_UNDERFLOW,
eIntDivideByZero = EXCEPTION_INT_DIVIDE_BY_ZERO,
eIntOverflow = EXCEPTION_INT_OVERFLOW,
ePrivelegedInstruction = EXCEPTION_PRIV_INSTRUCTION,
eUncontinuableException = EXCEPTION_NONCONTINUABLE_EXCEPTION
};
class HWException
{
public:
HWException(HWExceptionType aType,
EXCEPTION_POINTERS* pExp):
itsCategory(aType),
itsPointers(pExp),
itsLocation(pExp->ExceptionRecord->ExceptionAddress)
{
}
HWExceptionType GetCategory() const {return itsCategory;}
DWORD GetLocation() const {return itsLocation;}
EXCEPTION_POINTERS* GetSysPointer()const {return itsPointers;}
protected:
HWExceptionType itsCategory;
DWORD itsLocation;
EXCEPTION_POINTERS* itsPointers;
};
static void HWTranslateException(unsigned int u,
EXCEPTION_POINTERS* pExp)
{
throw HWException((HWExceptionType)u,pExp);
}
#endif
///////////////////////////////////////////////////////////////////////
Example usage:
///////////////////////////////////////////////////////////////////////
#include "windows.h"
#include "HWException.h"
int main()
{
//Note, setting the exception translator must be done
//on a per thread basis.
_set_se_translator(HWTranslateException);
try {
//This will cause an access violation
char* ptr = NULL;
*ptr = 5;
}
catch (HWException& e)
{
//We can now know both the type and the
//memory location of the instruction that
//caused the exception. Cool!
HWExceptionType exceptionType = e.GetCategory();
DWORD address = e.GetLocation();
}
catch (...)
{
//If we got here, then it was some other kind
//of C++ exception...
}
return 0;
}
|