Can someone explain the difference between a C++ exception and a structured exception in MFC?
Can someone explain the difference between a C++ exception and a structured exception in MFC?
On Windows C++ exceptions and SEH are basically the same thing. C++ exception is just one of many possible SEH exceptions that can be thrown. Some are thrown from hardware, some from software. C++ ones are software triggered exception. Here is very cool blog post from Raymond Chen that will help you dig a little bit deeper into the rabbit hole of SEH exceptions and how C++ exceptions are built upon them.
Here is my practical implementation of the method from above article. I'm actually shocked than after 12 years and:
Note that this information falls under the category of implementation detail. There is no guarantee that this method will continue to work in the future, so don’t write code that relies on it. It’s just a debugging tip.
it's still working the same on the latest Windows 10 in 2022! My solution also features full info extracted from the exception, so you won't even need a debugger from now on, just catch exception with __try\__except and use this function to print info for any exception:
#define EXCEPTION_CPP_LOWERCASE 0xE06D7363
#define EXCEPTION_CPP_UPPERCASE 0xE04D5343
void pexcept(DWORD c, _EXCEPTION_RECORD *er)
{
cout << "--------------------------------------------------------------------" << endl;
switch(c)
{
case STILL_ACTIVE:
cout << "STILL_ACTIVE" << endl;
break;
case EXCEPTION_ACCESS_VIOLATION:
if(er->ExceptionInformation[0] == 0)
{
cout << "READ ";
}
else if(er->ExceptionInformation[0] == 1)
{
cout << "WRITE ";
}
else if(er->ExceptionInformation[0] == 8)
{
cout << "DEP "; // Data Execution Prevention
}
cout << "ACCESS_VIOLATION" << endl;
break;
case EXCEPTION_DATATYPE_MISALIGNMENT:
cout << "DATATYPE_MISALIGNMENT" << endl;
break;
case EXCEPTION_BREAKPOINT:
cout << "BREAKPOINT" << endl;
break;
case EXCEPTION_SINGLE_STEP:
cout << "SINGLE_STEP" << endl;
break;
case EXCEPTION_ARRAY_BOUNDS_EXCEEDED:
cout << "ARRAY_BOUNDS_EXCEEDED" << endl;
break;
case EXCEPTION_FLT_DENORMAL_OPERAND:
cout << "FLOAT_DENORMAL_OPERAND" << endl;
break;
case EXCEPTION_FLT_DIVIDE_BY_ZERO:
cout << "FLOAT_DIVIDE_BY_ZERO" << endl;
break;
case EXCEPTION_FLT_INEXACT_RESULT:
cout << "FLOAT_INEXACT_RESULT" << endl;
break;
case EXCEPTION_FLT_INVALID_OPERATION:
cout << "FLOAT_INVALID_OPERATION" << endl;
break;
case EXCEPTION_FLT_OVERFLOW:
cout << "FLOAT_OVERFLOW" << endl;
break;
case EXCEPTION_FLT_STACK_CHECK:
cout << "FLOAT_STACK_CHECK" << endl;
break;
case EXCEPTION_FLT_UNDERFLOW:
cout << "FLOAT_UNDERFLOW" << endl;
break;
case EXCEPTION_INT_DIVIDE_BY_ZERO:
cout << "INTEGER_DIVIDE_BY_ZERO" << endl;
break;
case EXCEPTION_INT_OVERFLOW:
cout << "INTEGER_OVERFLOW" << endl;
break;
case EXCEPTION_PRIV_INSTRUCTION:
cout << "PRIVILEGED_INSTRUCTION" << endl;
break;
case EXCEPTION_IN_PAGE_ERROR:
if(er->ExceptionInformation[0] == 0)
{
cout << "READ ";
}
else if(er->ExceptionInformation[0] == 1)
{
cout << "WRITE ";
}
else if(er->ExceptionInformation[0] == 8)
{
cout << "DEP "; // Data Execution Prevention
}
cout << "IN_PAGE_ERROR" << endl;
cout << "DATA VADDRESS: " << er->ExceptionInformation[1] << endl;
cout << "NTSTATUS CAUSE: " << er->ExceptionInformation[2] << endl;
break;
case EXCEPTION_ILLEGAL_INSTRUCTION:
cout << "ILLEGAL_INSTRUCTION" << endl;
break;
case EXCEPTION_NONCONTINUABLE_EXCEPTION:
cout << "NONCONTINUABLE_EXCEPTION" << endl;
break;
case EXCEPTION_STACK_OVERFLOW:
cout << "STACK_OVERFLOW" << endl;
break;
case EXCEPTION_INVALID_DISPOSITION:
cout << "INVALID_DISPOSITION" << endl;
break;
case EXCEPTION_GUARD_PAGE:
cout << "GUARD_PAGE_VIOLATION" << endl;
break;
case EXCEPTION_INVALID_HANDLE:
cout << "INVALID_HANDLE" << endl;
break;
// case EXCEPTION_POSSIBLE_DEADLOCK:
// cout << "POSSIBLE_DEADLOCK" << endl;
// break;
case CONTROL_C_EXIT:
cout << "CONTROL_C_EXIT" << endl;
break;
case EXCEPTION_CPP_LOWERCASE:
case EXCEPTION_CPP_UPPERCASE:
cout << "CPP_EXCEPTION" << endl;
cout << "PARAMS NUM: " << er->NumberParameters << endl;
for(ui64 i = 0; i < er->NumberParameters; ++i)
{
cout << dec << "PARAM" << setw(2) << left << i << " ";
switch(i)
{
case 0:
cout << " SOME INTERNAL VALUE";
break;
case 1:
cout << " POINTER TO THROWN OBJ";
break;
case 2:
cout << " POINTER TO OBJECT INF";
break;
case 3:
cout << "DLL/EXE THROWER HINSTANCE";
break;
}
cout << ": 0x" << hex
<< uppercase << er->ExceptionInformation[i] << endl;
}
{
ui64 hinst = er->ExceptionInformation[3];
DWORD *obj_inf = (DWORD *)er->ExceptionInformation[2];
cout << hex << uppercase << obj_inf[0] << endl;
cout << obj_inf[1] << endl;
cout << obj_inf[2] << endl;
cout << obj_inf[3] << endl;
cout << "-----------------------------" << endl;
obj_inf = (DWORD *)(hinst + obj_inf[3]);
cout << obj_inf[0] << endl;
cout << obj_inf[1] << endl;
cout << "-----------------------------" << endl;
obj_inf = (DWORD *)(hinst + obj_inf[1]);
cout << obj_inf[0] << endl;
cout << obj_inf[1] << endl;
cout << "-----------------------------" << endl;
ui64 *class_inf = (ui64 *)(hinst + obj_inf[1]);
cout << class_inf[0] << endl;
cout << class_inf[1] << endl;
cout << class_inf[2] << endl;
char *class_name = (char *)(class_inf + 2);
cout << class_name << endl;
}
cout << "CURRENT EXE HIN: " << hex << uppercase << "0x" << GetModuleHandle(NULL) << endl;
cout << "UCRTBASE HIN: " << hex << uppercase << "0x" << LoadLibrary(L"ucrtbase.dll") << endl;
cout << "VCRUNTIME140 HIN: " << hex << uppercase << "0x" << LoadLibrary(L"vcruntime140.dll") << endl;
cout << "STD MSVCP140 HIN: " << hex << uppercase << "0x" << LoadLibrary(L"msvcp140.dll") << endl;
break;
default:
cout << "UNKNOWN_EXCEPTION [" << hex << uppercase << c << "]" << endl;
}
cout << "CONTINUE EXECUTION: "
<< (er->ExceptionFlags == EXCEPTION_NONCONTINUABLE ? "NOT " : "" )
<< "POSSIBLE" << endl;
cout << "INSTRUCTION ADDRESS: 0x" << hex << uppercase
<< er->ExceptionAddress << endl;
if(er->ExceptionRecord != NULL)
{
cout << "CHAINED EXCEPTION!" << endl;
pexcept(er->ExceptionRecord->ExceptionCode,
er->ExceptionRecord);
}
else
{
cout << "--------------------------------------------------------------------" << endl;
}
}
Here is how your output might look (ASCII drawing formed by hand):
--------------------------------------------------------------------
CPP_EXCEPTION
PARAMS NUM: 4
PARAM0 SOME INTERNAL VALUE: 0x19930520
PARAM1 POINTER TO THROWN OBJ: 0x14FA30
PARAM2 POINTER TO OBJECT INF: 0x7FFE68B246B0 ---->
PARAM3 DLL/EXE THROWER HINSTANCE: 0x7FFE68AC0000
+-----+
--> |0 |
+-----+
|4D0C |
+-----+
|0 |
+-----+ +-----+
|646D0|---->|3 |
+-----+ +-----+ +-----+
|646F0|---->|0 |
+-----+ +-----+ +----------------+
|84578|---->|0 |
+-----+ +----------------+
|7FFE68B1C218 |
+----------------+
|5F74756F56413F2E|
+----------------+
..... _ t u o V A ? .
.?AVout_of_range@std@@
CURRENT EXE HIN: 0x0000000140000000
UCRTBASE HIN: 0x00007FFE99D50000
VCRUNTIME140 HIN: 0x00007FFE95040000
STD MSVCP140 HIN: 0x00007FFE68AC0000
CONTINUE EXECUTION: NOT POSSIBLE
INSTRUCTION ADDRESS: 0x00007FFE99F54FD9
--------------------------------------------------------------------
As you can see, lust number is not a pointer but actual class name of the exception (interpreted as number in Little Endian). The HINSTANCE parameter of the exception is the same as Standard C++ Library (msvcp140.dll in my case), so it's obvious where this exception originated.